use serde_json::Value;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Command, Output};
use std::sync::atomic::{AtomicU64, Ordering};
static NEXT_REPO_ID: AtomicU64 = AtomicU64::new(0);
struct TestRepo(PathBuf);
impl TestRepo {
fn new() -> Self {
let id = NEXT_REPO_ID.fetch_add(1, Ordering::Relaxed);
let path = std::env::temp_dir().join(format!("rustytag-show-{}-{id}", std::process::id()));
fs::create_dir_all(&path).unwrap();
run_git(&path, &["init", "--quiet"]);
run_git(&path, &["config", "user.name", "RustyTag Test"]);
run_git(&path, &["config", "user.email", "rustytag@example.com"]);
fs::write(path.join("README.md"), "test repository\n").unwrap();
run_git(&path, &["add", "README.md"]);
run_git(&path, &["commit", "--quiet", "-m", "Initial commit"]);
Self(path)
}
fn run_rustytag(&self, args: &[&str]) -> Output {
Command::new(env!("CARGO_BIN_EXE_rustytag"))
.args(args)
.current_dir(&self.0)
.env("HOME", self.0.join("empty-home"))
.output()
.unwrap()
}
fn run_rustytag_with_home(&self, args: &[&str], home: &Path) -> Output {
Command::new(env!("CARGO_BIN_EXE_rustytag"))
.args(args)
.current_dir(&self.0)
.env("HOME", home)
.output()
.unwrap()
}
}
impl Drop for TestRepo {
fn drop(&mut self) {
fs::remove_dir_all(&self.0).unwrap();
}
}
fn run_git(repo: &Path, args: &[&str]) {
let output = Command::new("git")
.args(["-C", repo.to_str().unwrap()])
.args(args)
.output()
.unwrap();
assert!(
output.status.success(),
"git command failed: {}",
String::from_utf8_lossy(&output.stderr)
);
}
#[test]
fn show_machine_output_is_clean_in_untagged_repo() {
let repo = TestRepo::new();
let human = repo.run_rustytag(&["show"]);
assert!(human.status.success());
let human_stdout = String::from_utf8(human.stdout).unwrap();
assert!(human_stdout.contains("No tags found, using initial version"));
assert!(human_stdout.contains("Current Version: 0.1.0"));
let quiet = repo.run_rustytag(&["show", "--quiet"]);
assert!(quiet.status.success());
assert_eq!(String::from_utf8(quiet.stdout).unwrap(), "0.1.0\n");
assert!(quiet.stderr.is_empty());
let json = repo.run_rustytag(&["show", "--json"]);
assert!(json.status.success());
assert!(json.stderr.is_empty());
let output: Value = serde_json::from_slice(&json.stdout).unwrap();
assert_eq!(output["version"], "0.1.0");
assert_eq!(output["commit_count"], 1);
assert_eq!(output["repository_url"], Value::Null);
assert_eq!(output["rustytag_version"], env!("CARGO_PKG_VERSION"));
assert!(output["branch"].is_string());
}
#[test]
fn show_rejects_json_and_quiet_together() {
let output = Command::new(env!("CARGO_BIN_EXE_rustytag"))
.args(["show", "--json", "--quiet"])
.output()
.unwrap();
assert!(!output.status.success());
assert!(String::from_utf8_lossy(&output.stderr).contains("cannot be used with"));
}
#[test]
fn user_prefix_is_applied_and_saved_to_repository_config() {
let repo = TestRepo::new();
let home = repo.0.join("home");
fs::create_dir_all(&home).unwrap();
fs::write(home.join(".rustytag.json"), r#"{"version_prefix":"v"}"#).unwrap();
let output = repo.run_rustytag_with_home(&["show", "--quiet"], &home);
assert!(output.status.success());
assert_eq!(String::from_utf8(output.stdout).unwrap(), "v0.1.0\n");
let repository_config: Value =
serde_json::from_slice(&fs::read(repo.0.join(".rustytag.json")).unwrap()).unwrap();
assert_eq!(repository_config["version_prefix"], "v");
}
#[test]
fn detected_tag_prefix_is_saved_to_repository_config() {
let repo = TestRepo::new();
run_git(&repo.0, &["tag", "v0.1.0"]);
let output = repo.run_rustytag(&["show", "--quiet"]);
assert!(output.status.success());
assert_eq!(String::from_utf8(output.stdout).unwrap(), "v0.1.0\n");
let repository_config: Value =
serde_json::from_slice(&fs::read(repo.0.join(".rustytag.json")).unwrap()).unwrap();
assert_eq!(repository_config["version_prefix"], "v");
}
#[test]
fn explicit_version_prefix_is_committed_with_the_tag() {
let repo = TestRepo::new();
run_git(
&repo.0,
&[
"remote",
"add",
"origin",
"https://github.com/example/project.git",
],
);
let output = repo.run_rustytag(&["patch", "--version", "v0.1.0"]);
assert!(
output.status.success(),
"rustytag failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let tags = git_stdout(&repo.0, &["tag", "--list"]);
assert_eq!(tags, "v0.1.0\n");
let repository_config: Value =
serde_json::from_slice(&fs::read(repo.0.join(".rustytag.json")).unwrap()).unwrap();
assert_eq!(repository_config["version_prefix"], "v");
assert_eq!(repository_config["version"], "0.1.0");
assert!(git_stdout(&repo.0, &["status", "--porcelain"]).is_empty());
}
#[test]
fn configured_prefix_is_saved_and_used_by_the_repository() {
let repo = TestRepo::new();
let configured = repo.run_rustytag(&["config", "--set", "VERSION_PREFIX=v"]);
assert!(configured.status.success());
let shown = repo.run_rustytag(&["show", "--quiet"]);
assert!(shown.status.success());
assert_eq!(String::from_utf8(shown.stdout).unwrap(), "v0.1.0\n");
let repository_config: Value =
serde_json::from_slice(&fs::read(repo.0.join(".rustytag.json")).unwrap()).unwrap();
assert_eq!(repository_config["version_prefix"], "v");
}
fn git_stdout(repo: &Path, args: &[&str]) -> String {
let output = Command::new("git")
.args(["-C", repo.to_str().unwrap()])
.args(args)
.output()
.unwrap();
assert!(
output.status.success(),
"git command failed: {}",
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8(output.stdout).unwrap()
}