use repo::Repository;
use tempfile::TempDir;
#[test]
fn test_detect_divergent_history() {
let temp = TempDir::new().unwrap();
let repo = Repository::init_default(temp.path()).unwrap();
std::fs::write(temp.path().join("common.txt"), "common base").unwrap();
let base_state = repo.snapshot(Some("Base state".to_string()), None).unwrap();
repo.refs()
.set_thread("feature", &base_state.change_id)
.unwrap();
std::fs::write(temp.path().join("feature.txt"), "feature work").unwrap();
let feature_state = repo
.snapshot(Some("Feature work".to_string()), None)
.unwrap();
repo.goto(&base_state.change_id).unwrap();
std::fs::write(temp.path().join("main.txt"), "main work").unwrap();
let main_state = repo.snapshot(Some("Main work".to_string()), None).unwrap();
assert_ne!(feature_state.change_id, main_state.change_id);
assert_eq!(feature_state.parents, vec![base_state.change_id]);
assert_eq!(main_state.parents, vec![base_state.change_id]);
}
#[test]
fn test_find_common_ancestor() {
let temp = TempDir::new().unwrap();
let repo = Repository::init_default(temp.path()).unwrap();
std::fs::write(temp.path().join("file.txt"), "A").unwrap();
let _state_a = repo.snapshot(Some("A".to_string()), None).unwrap();
std::fs::write(temp.path().join("file.txt"), "B").unwrap();
let state_b = repo.snapshot(Some("B".to_string()), None).unwrap();
std::fs::write(temp.path().join("file.txt"), "C").unwrap();
let state_c = repo.snapshot(Some("C".to_string()), None).unwrap();
repo.goto(&state_b.change_id).unwrap();
std::fs::write(temp.path().join("file.txt"), "D").unwrap();
let state_d = repo.snapshot(Some("D".to_string()), None).unwrap();
assert_eq!(state_c.parents, vec![state_b.change_id]);
assert_eq!(state_d.parents, vec![state_b.change_id]);
}
#[test]
fn test_three_way_merge_base() {
let temp = TempDir::new().unwrap();
let repo = Repository::init_default(temp.path()).unwrap();
std::fs::write(temp.path().join("base.txt"), "base content").unwrap();
let base = repo.snapshot(Some("Base".to_string()), None).unwrap();
std::fs::write(temp.path().join("file1.txt"), "branch1").unwrap();
let branch1 = repo.snapshot(Some("Branch 1".to_string()), None).unwrap();
repo.goto(&base.change_id).unwrap();
std::fs::write(temp.path().join("file2.txt"), "branch2").unwrap();
let branch2 = repo.snapshot(Some("Branch 2".to_string()), None).unwrap();
assert_eq!(branch1.parents[0], base.change_id);
assert_eq!(branch2.parents[0], base.change_id);
}
#[test]
fn test_detect_conflicting_modifications() {
let temp = TempDir::new().unwrap();
let repo = Repository::init_default(temp.path()).unwrap();
std::fs::write(temp.path().join("conflict.txt"), "base").unwrap();
let base = repo.snapshot(Some("Base".to_string()), None).unwrap();
std::fs::write(temp.path().join("conflict.txt"), "branch1").unwrap();
repo.snapshot(Some("Branch 1".to_string()), None).unwrap();
repo.goto(&base.change_id).unwrap();
std::fs::write(temp.path().join("conflict.txt"), "branch2").unwrap();
repo.snapshot(Some("Branch 2".to_string()), None).unwrap();
}
#[test]
fn test_non_conflicting_changes() {
let temp = TempDir::new().unwrap();
let repo = Repository::init_default(temp.path()).unwrap();
std::fs::write(temp.path().join("base.txt"), "base").unwrap();
let base = repo.snapshot(Some("Base".to_string()), None).unwrap();
std::fs::write(temp.path().join("file1.txt"), "branch1").unwrap();
let branch1 = repo.snapshot(Some("Branch 1".to_string()), None).unwrap();
repo.goto(&base.change_id).unwrap();
std::fs::write(temp.path().join("file2.txt"), "branch2").unwrap();
let branch2 = repo.snapshot(Some("Branch 2".to_string()), None).unwrap();
assert!(
repo.store()
.get_state(&branch1.change_id)
.unwrap()
.is_some()
);
assert!(
repo.store()
.get_state(&branch2.change_id)
.unwrap()
.is_some()
);
}
#[test]
fn test_fast_forward_detection() {
let temp = TempDir::new().unwrap();
let repo = Repository::init_default(temp.path()).unwrap();
std::fs::write(temp.path().join("file.txt"), "A").unwrap();
let state_a = repo.snapshot(Some("A".to_string()), None).unwrap();
std::fs::write(temp.path().join("file.txt"), "B").unwrap();
let state_b = repo.snapshot(Some("B".to_string()), None).unwrap();
std::fs::write(temp.path().join("file.txt"), "C").unwrap();
let state_c = repo.snapshot(Some("C".to_string()), None).unwrap();
assert_eq!(state_b.parents, vec![state_a.change_id]);
assert_eq!(state_c.parents, vec![state_b.change_id]);
}
#[test]
fn test_modify_vs_delete_conflict() {
let temp = TempDir::new().unwrap();
let repo = Repository::init_default(temp.path()).unwrap();
std::fs::write(temp.path().join("file.txt"), "content").unwrap();
let base = repo.snapshot(Some("Base".to_string()), None).unwrap();
std::fs::write(temp.path().join("file.txt"), "modified").unwrap();
repo.snapshot(Some("Modified".to_string()), None).unwrap();
repo.goto(&base.change_id).unwrap();
std::fs::remove_file(temp.path().join("file.txt")).unwrap();
repo.snapshot(Some("Deleted".to_string()), None).unwrap();
}
#[test]
fn test_rename_detection_in_merge() {
let temp = TempDir::new().unwrap();
let repo = Repository::init_default(temp.path()).unwrap();
std::fs::write(temp.path().join("oldname.txt"), "content").unwrap();
let base = repo.snapshot(Some("Base".to_string()), None).unwrap();
std::fs::remove_file(temp.path().join("oldname.txt")).unwrap();
std::fs::write(temp.path().join("newname.txt"), "content").unwrap();
let _branch1 = repo.snapshot(Some("Renamed".to_string()), None).unwrap();
repo.goto(&base.change_id).unwrap();
std::fs::write(temp.path().join("oldname.txt"), "modified content").unwrap();
let _branch2 = repo
.snapshot(Some("Modified original".to_string()), None)
.unwrap();
}
#[test]
fn test_octopus_merge_structure() {
let temp = TempDir::new().unwrap();
let repo = Repository::init_default(temp.path()).unwrap();
std::fs::write(temp.path().join("base.txt"), "base").unwrap();
let base = repo.snapshot(Some("Base".to_string()), None).unwrap();
let mut parent_ids = vec![base.change_id];
for i in 1..=3 {
repo.goto(&base.change_id).unwrap();
std::fs::write(
temp.path().join(format!("branch{}.txt", i)),
format!("branch {}", i),
)
.unwrap();
let state = repo.snapshot(Some(format!("Branch {}", i)), None).unwrap();
parent_ids.push(state.change_id);
}
assert!(parent_ids.len() > 2);
}
#[test]
fn test_binary_file_merge() {
let temp = TempDir::new().unwrap();
let repo = Repository::init_default(temp.path()).unwrap();
std::fs::write(temp.path().join("image.bin"), vec![0u8, 1, 2, 3, 4]).unwrap();
let base = repo.snapshot(Some("Base".to_string()), None).unwrap();
std::fs::write(temp.path().join("image.bin"), vec![5u8, 6, 7, 8, 9]).unwrap();
repo.snapshot(Some("Binary 1".to_string()), None).unwrap();
repo.goto(&base.change_id).unwrap();
std::fs::write(temp.path().join("image.bin"), vec![10u8, 11, 12, 13, 14]).unwrap();
repo.snapshot(Some("Binary 2".to_string()), None).unwrap();
}
#[test]
fn test_directory_file_conflict() {
let temp = TempDir::new().unwrap();
let repo = Repository::init_default(temp.path()).unwrap();
std::fs::write(temp.path().join("item.txt"), "file content").unwrap();
let base = repo.snapshot(Some("Base".to_string()), None).unwrap();
std::fs::remove_file(temp.path().join("item.txt")).unwrap();
std::fs::create_dir(temp.path().join("item.txt")).unwrap();
std::fs::write(temp.path().join("item.txt/subfile.txt"), "subcontent").unwrap();
repo.snapshot(Some("Directory".to_string()), None).unwrap();
repo.goto(&base.change_id).unwrap();
std::fs::write(temp.path().join("item.txt"), "modified file").unwrap();
repo.snapshot(Some("Modified file".to_string()), None)
.unwrap();
}