use super::{
GitignoreBackup, backup_workspace_gitignore_if_present, collect_workspace_gitignore_relpaths,
restore_workspace_gitignore_backup,
};
use crate::session_dotfile_backup::tree_test_support::init_git_repo;
use crate::test_utils::with_isolated_home;
use std::path::{Path, PathBuf};
#[test]
fn collect_finds_root_and_nested_gitignore_files() {
let tmp = tempfile::tempdir().unwrap();
let work = tmp.path().join("repo");
std::fs::create_dir_all(work.join("pkg/.cache")).unwrap();
std::fs::write(work.join(".gitignore"), "root\n").unwrap();
std::fs::write(work.join("pkg/.gitignore"), "pkg\n").unwrap();
std::fs::write(work.join("pkg/.cache/.gitignore"), "cache\n").unwrap();
init_git_repo(&work);
let rels = collect_workspace_gitignore_relpaths(&work);
assert_eq!(
rels,
vec![
PathBuf::from(".gitignore"),
PathBuf::from("pkg/.cache/.gitignore"),
PathBuf::from("pkg/.gitignore"),
]
);
}
#[test]
fn collect_non_git_workspace_only_checks_root_gitignore_not_subdirs() {
let tmp = tempfile::tempdir().unwrap();
let work = tmp.path();
std::fs::create_dir_all(work.join("nested/deep")).unwrap();
std::fs::write(work.join("nested/.gitignore"), "nested\n").unwrap();
std::fs::write(work.join("nested/deep/.gitignore"), "deep\n").unwrap();
assert!(collect_workspace_gitignore_relpaths(work).is_empty());
std::fs::write(work.join(".gitignore"), "root\n").unwrap();
assert_eq!(
collect_workspace_gitignore_relpaths(work),
vec![PathBuf::from(".gitignore")]
);
}
pub(crate) fn seed_nested_gitignore_repo(work: &Path) {
std::fs::create_dir_all(work.join("pkg")).unwrap();
std::fs::write(work.join(".gitignore"), "root\n").unwrap();
std::fs::write(work.join("pkg/.gitignore"), "pkg\n").unwrap();
init_git_repo(work);
}
pub(crate) fn tamper_gitignore_tree(work: &Path) {
std::fs::write(work.join(".gitignore"), "tampered-root\n").unwrap();
std::fs::write(work.join("pkg/.gitignore"), "tampered-pkg\n").unwrap();
std::fs::create_dir_all(work.join("new")).unwrap();
std::fs::write(work.join("new/.gitignore"), "agent-created\n").unwrap();
}
#[test]
fn nested_gitignore_round_trip_restores_tree_and_removes_agent_created_files() {
with_isolated_home(|work| {
seed_nested_gitignore_repo(work);
let backup =
super::backup_workspace_gitignore_if_present_with_id(work, &mut |n| format!("gi{n}"))
.unwrap();
let GitignoreBackup::Present { backup_root, files } = &backup else {
panic!("expected gitignore tree backup");
};
assert!(
backup_root.starts_with(crate::workspace_paths::snapshot_category_dir("gitignore"))
);
assert_eq!(files.len(), 2);
tamper_gitignore_tree(work);
restore_workspace_gitignore_backup(work, &backup).unwrap();
assert_gitignore_contents(work, ".gitignore", "root\n");
assert_gitignore_contents(work, "pkg/.gitignore", "pkg\n");
assert!(!work.join("new/.gitignore").exists());
});
}
pub(crate) fn assert_gitignore_contents(work: &Path, rel: &str, expected: &str) {
assert_eq!(std::fs::read_to_string(work.join(rel)).unwrap(), expected);
}
#[test]
fn poisoned_disk_snapshot_does_not_change_restored_gitignore_content() {
with_isolated_home(|work| {
std::fs::write(work.join(".gitignore"), "ORIGINAL\n").unwrap();
let backup = super::backup_workspace_gitignore_if_present_with_id(work, &mut |n| {
format!("poison{n}")
})
.unwrap();
let GitignoreBackup::Present { backup_root, .. } = &backup else {
panic!("expected backup");
};
std::fs::write(backup_root.join(".gitignore"), "POISONED\n").unwrap();
std::fs::write(work.join(".gitignore"), "AGENT\n").unwrap();
restore_workspace_gitignore_backup(work, &backup).unwrap();
assert_eq!(
std::fs::read_to_string(work.join(".gitignore")).unwrap(),
"ORIGINAL\n"
);
});
}
#[test]
fn backup_workspace_gitignore_if_present_delegates_to_tree_backup() {
with_isolated_home(|work| {
std::fs::write(work.join(".gitignore"), "root\n").unwrap();
let backup = backup_workspace_gitignore_if_present(work).expect("backup");
assert!(matches!(backup, GitignoreBackup::Present { .. }));
});
}