use super::support::{Fixture, Observed, fixture, remove, run_wrapped, stderr_of};
use super::worktree_copy::{needle, repository_with_rules};
use std::path::PathBuf;
const RULES_FILE: &str = ".cache/forbidden-strings.rules.txt";
const COPIED: &str = "cli-git: copied ";
const USAGE_ERROR_STATUS: i32 = 129;
fn assert_copied(fixture: &Fixture, observed: &Observed, destination: &str) {
assert_eq!(observed.code, Some(0), "{}", stderr_of(observed));
assert!(stderr_of(observed).starts_with(COPIED), "{}", stderr_of(observed));
assert_eq!(
std::fs::read(fixture.root.join(destination).join(RULES_FILE)).expect("copied rules"),
format!("{}\n", needle()).into_bytes(),
"{destination}"
);
}
fn assert_rejected(fixture: &Fixture, observed: &Observed, destination: &str) {
assert_ne!(observed.code, Some(0), "{}", stderr_of(observed));
assert!(!stderr_of(observed).contains(COPIED), "{}", stderr_of(observed));
assert!(!stderr_of(observed).contains("does not read"), "Git's error stands alone: {}", stderr_of(observed));
assert!(!fixture.root.join(destination).exists(), "{destination} must not exist");
}
#[test]
fn an_abbreviated_long_option_gets_the_copy() {
let fixture: Fixture = fixture("forms-abbreviated");
let repo: PathBuf = repository_with_rules(&fixture, true);
for (arguments, destination) in [
(vec!["worktree", "add", "--quiet", "--det", "../one"], "one"),
(vec!["worktree", "add", "--quiet", "--lock", "--reas", "why", "../two"], "two"),
(vec!["worktree", "add", "--quiet", "--lock", "--reas=why", "../three"], "three"),
(vec!["worktree", "add", "--quiet", "--no-che", "../four"], "four"),
(vec!["worktree", "add", "--qu", "--rel", "../five"], "five"),
(vec!["worktree", "add", "--quiet", "--orp", "-b", "six", "../six"], "six"),
] {
let observed: Observed = run_wrapped(&fixture, repo.as_path(), arguments.as_slice());
assert_copied(&fixture, &observed, destination);
}
remove(&fixture);
}
#[test]
fn an_exact_name_wins_and_a_negated_abbreviation_negates() {
let fixture: Fixture = fixture("forms-exact");
let repo: PathBuf = repository_with_rules(&fixture, true);
let observed: Observed = run_wrapped(&fixture, repo.as_path(), &["worktree", "add", "--quiet", "--lock", "--no-lo", "../one"]);
assert_copied(&fixture, &observed, "one");
remove(&fixture);
}
#[test]
fn an_ambiguous_prefix_is_gits_error_and_gets_no_copy() {
let fixture: Fixture = fixture("forms-ambiguous");
let repo: PathBuf = repository_with_rules(&fixture, true);
for (arguments, destination) in [
(vec!["worktree", "add", "--quiet", "--re", "../one"], "one"),
(vec!["worktree", "add", "--quiet", "--n", "../two"], "two"),
(vec!["worktree", "add", "--quiet", "--no-", "../three"], "three"),
] {
let observed: Observed = run_wrapped(&fixture, repo.as_path(), arguments.as_slice());
assert_rejected(&fixture, &observed, destination);
assert!(stderr_of(&observed).contains("ambiguous option") || observed.code == Some(USAGE_ERROR_STATUS), "{}", stderr_of(&observed));
}
remove(&fixture);
}
#[test]
fn forms_git_rejects_keep_gits_error_and_get_no_copy() {
let fixture: Fixture = fixture("forms-rejected");
let repo: PathBuf = repository_with_rules(&fixture, true);
for (arguments, destination) in [
(vec!["worktree", "add", "--quiet", "--detach=yes", "../one"], "one"),
(vec!["worktree", "add", "--quiet", "--no-no-checkout", "../two"], "two"),
(vec!["worktree", "add", "--quiet", "--nonsense", "../three"], "three"),
(vec!["worktree", "--quiet", "add", "../four"], "four"),
(vec!["worktree", "add", "--quiet", "--reason", "../five"], "five"),
] {
let observed: Observed = run_wrapped(&fixture, repo.as_path(), arguments.as_slice());
assert_rejected(&fixture, &observed, destination);
}
remove(&fixture);
}
#[test]
fn git_dir_and_work_tree_globals_get_the_copy() {
let fixture: Fixture = fixture("forms-git-dir");
let repo: PathBuf = repository_with_rules(&fixture, true);
let common: String = repo.join(".git").to_string_lossy().into_owned();
let work_tree: String = repo.to_string_lossy().into_owned();
let git_dir_option: String = format!("--git-dir={common}");
let work_tree_option: String = format!("--work-tree={work_tree}");
let outside: Observed = run_wrapped(
&fixture,
fixture.root.as_path(),
&[git_dir_option.as_str(), work_tree_option.as_str(), "worktree", "add", "--quiet", "-b", "one", "one"],
);
assert_copied(&fixture, &outside, "one");
let separate: Observed = run_wrapped(
&fixture,
fixture.root.as_path(),
&["--git-dir", common.as_str(), "--work-tree", work_tree.as_str(), "worktree", "add", "--quiet", "-b", "two", "two"],
);
assert_copied(&fixture, &separate, "two");
let inside: Observed = run_wrapped(
&fixture,
repo.as_path(),
&[git_dir_option.as_str(), work_tree_option.as_str(), "worktree", "add", "--quiet", "-b", "three", "../three"],
);
assert_copied(&fixture, &inside, "three");
remove(&fixture);
}
#[test]
fn a_destination_below_missing_directories_gets_the_copy() {
let fixture: Fixture = fixture("forms-missing-parents");
let repo: PathBuf = repository_with_rules(&fixture, true);
let observed: Observed = run_wrapped(&fixture, repo.as_path(), &["worktree", "add", "--quiet", "-b", "deep", ".claude/worktrees/deep"]);
assert_eq!(observed.code, Some(0), "{}", stderr_of(&observed));
assert!(stderr_of(&observed).starts_with(COPIED), "{}", stderr_of(&observed));
assert!(repo.join(".claude/worktrees/deep").join(RULES_FILE).exists());
remove(&fixture);
}