mod common;
use assert_matches::assert_matches;
use iso_code::{Config, CreateOptions, DeleteOptions, Manager, WorktreeError};
use common::{create_test_repo, run_git};
#[test]
fn qa_g_001_branch_already_checked_out() {
let repo = create_test_repo();
let mgr = Manager::new(repo.path(), Config::default()).unwrap();
let (h1, _) = mgr
.create("feature-x", repo.path().join("wt-1"), CreateOptions::default())
.unwrap();
let result = mgr.create(
"feature-x",
repo.path().join("wt-2"),
CreateOptions::default(),
);
assert_matches!(result, Err(WorktreeError::BranchAlreadyCheckedOut { .. }));
let mut force = DeleteOptions::default();
force.force = true;
mgr.delete(&h1, force).unwrap();
}
#[test]
fn qa_g_003_worktree_count_rate_limit() {
let repo = create_test_repo();
let mut cfg = Config::default();
cfg.max_worktrees = 3;
let mgr = Manager::new(repo.path(), cfg).unwrap();
let mut handles = Vec::new();
for i in 0..2 {
let (h, _) = mgr
.create(
format!("lim-{i}"),
repo.path().join(format!("lim-wt-{i}")),
CreateOptions::default(),
)
.unwrap();
handles.push(h);
}
let result = mgr.create(
"lim-2",
repo.path().join("lim-wt-2"),
CreateOptions::default(),
);
assert_matches!(
result,
Err(WorktreeError::RateLimitExceeded { current: 3, max: 3 })
);
for h in handles {
let mut f = DeleteOptions::default();
f.force = true;
let _ = mgr.delete(&h, f);
}
}
#[test]
fn qa_g_004_path_already_exists() {
let repo = create_test_repo();
let mgr = Manager::new(repo.path(), Config::default()).unwrap();
let target = repo.path().join("already-there");
std::fs::create_dir_all(&target).unwrap();
std::fs::write(target.join("marker"), "do-not-touch").unwrap();
let result = mgr.create("some-branch", &target, CreateOptions::default());
assert_matches!(result, Err(WorktreeError::WorktreePathExists(_)));
assert!(target.join("marker").exists(), "marker must survive guard");
}
#[test]
fn qa_g_005_nested_worktree_forbidden() {
let repo = create_test_repo();
let mgr = Manager::new(repo.path(), Config::default()).unwrap();
let outer = repo.path().join("outer");
let (h, _) = mgr
.create("outer", &outer, CreateOptions::default())
.unwrap();
let nested = outer.join("inner");
let result = mgr.create("inner", &nested, CreateOptions::default());
assert_matches!(result, Err(WorktreeError::NestedWorktree { .. }));
let mut f = DeleteOptions::default();
f.force = true;
mgr.delete(&h, f).unwrap();
}
#[test]
fn qa_g_006_network_filesystem_error_variant_exists() {
let e = WorktreeError::NetworkFilesystem {
mount_point: std::path::PathBuf::from("/mnt/nfs"),
};
let s = format!("{e}");
assert!(s.contains("network filesystem"), "Display: {s}");
}
#[test]
fn qa_g_007_wsl_cross_boundary_error_variant_exists() {
let e = WorktreeError::WslCrossBoundary;
let s = format!("{e}");
assert!(s.to_lowercase().contains("wsl"), "Display: {s}");
}
#[test]
fn qa_g_008_bare_repo_permitted() {
let repo_dir = tempfile::TempDir::new().unwrap();
let bare = repo_dir.path().join("bare.git");
std::fs::create_dir_all(&bare).unwrap();
run_git(&bare, &["init", "--bare"]);
let seed = repo_dir.path().join("seed");
run_git(&bare, &["worktree", "add", seed.to_str().unwrap(), "-b", "main"]);
run_git(&seed, &["config", "user.email", "test@example.com"]);
run_git(&seed, &["config", "user.name", "Test"]);
run_git(&seed, &["commit", "--allow-empty", "-m", "seed"]);
let mgr = Manager::new(&bare, Config::default()).expect("bare repo permitted");
mgr.list().expect("list() on bare repo");
}
#[test]
fn qa_g_010_aggregate_disk_limit_enforced() {
let repo = create_test_repo();
std::fs::write(repo.path().join("big.bin"), vec![0u8; 3_000_000]).unwrap();
run_git(repo.path(), &["add", "big.bin"]);
run_git(repo.path(), &["commit", "-m", "big blob"]);
let mut cfg = Config::default();
cfg.max_total_disk_bytes = Some(1_000_000);
let mgr = Manager::new(repo.path(), cfg).unwrap();
let result = mgr.create(
"aggregate-limit",
repo.path().join("over-cap"),
CreateOptions::default(),
);
assert_matches!(result, Err(WorktreeError::AggregateDiskLimitExceeded));
assert!(!repo.path().join("over-cap").exists());
}
#[test]
fn qa_g_011_network_junction_target_error_variant_exists() {
let e = WorktreeError::NetworkJunctionTarget {
path: std::path::PathBuf::from(r"\\server\share\wt"),
};
let s = format!("{e}");
assert!(s.contains("junction"), "Display: {s}");
}
#[test]
fn qa_g_012_git_crypt_locked_auto_cleans_partial_worktree() {
let repo = create_test_repo();
std::fs::write(
repo.path().join(".gitattributes"),
"*.secret filter=git-crypt diff=git-crypt\n",
)
.unwrap();
run_git(repo.path(), &["add", ".gitattributes"]);
run_git(repo.path(), &["commit", "-m", "git-crypt attrs"]);
let mut enc = Vec::new();
enc.extend_from_slice(b"\x00GITCRYPT\x00");
enc.extend_from_slice(&[0xaa; 16]);
std::fs::write(repo.path().join("doc.secret"), &enc).unwrap();
run_git(repo.path(), &["add", "doc.secret"]);
run_git(repo.path(), &["commit", "-m", "encrypted blob"]);
let mgr = Manager::new(repo.path(), Config::default()).unwrap();
let wt = repo.path().join("locked-crypt-wt");
let result = mgr.create("crypt-br", &wt, CreateOptions::default());
assert_matches!(result, Err(WorktreeError::GitCryptLocked));
assert!(!wt.exists(), "partial worktree must be auto-cleaned");
}