use objects::{
error::HeddleError,
object::{ChangeId, MarkerName, ThreadName},
};
use tempfile::TempDir;
use super::*;
fn create_ref_manager() -> (TempDir, RefManager) {
let temp_dir = TempDir::new().unwrap();
let heddle_dir = temp_dir.path().join(".heddle");
std::fs::create_dir_all(&heddle_dir).unwrap();
let refs = RefManager::new(&heddle_dir);
refs.init().unwrap();
(temp_dir, refs)
}
#[test]
fn test_get_thread_from_packed_refs() {
let (_temp, refs) = create_ref_manager();
let id = ChangeId::generate();
refs.set_thread(&ThreadName::new("cold-branch"), &id)
.unwrap();
refs.pack_refs().unwrap();
let loose = refs.root.join("refs/threads/cold-branch");
assert!(!loose.exists(), "pack_refs should delete loose file");
assert_eq!(
refs.get_thread(&ThreadName::new("cold-branch")).unwrap(),
Some(id)
);
}
#[test]
fn test_loose_overrides_packed_refs() {
let (_temp, refs) = create_ref_manager();
let id1 = ChangeId::generate();
let id2 = ChangeId::generate();
refs.set_thread(&ThreadName::new("main"), &id1).unwrap();
refs.pack_refs().unwrap();
refs.set_thread(&ThreadName::new("main"), &id2).unwrap();
assert_eq!(
refs.get_thread(&ThreadName::new("main")).unwrap(),
Some(id2)
);
}
#[test]
fn test_pack_refs_consolidates_loose() {
let (_temp, refs) = create_ref_manager();
let ids: Vec<ChangeId> = (0..5).map(|_| ChangeId::generate()).collect();
for (i, id) in ids.iter().enumerate() {
refs.set_thread(&ThreadName::new(format!("branch-{}", i)), id)
.unwrap();
}
refs.pack_refs().unwrap();
let packed_path = refs.packed_refs_path();
assert!(packed_path.exists(), "packed-refs file should exist");
for (i, id) in ids.iter().enumerate() {
assert_eq!(
refs.get_thread(&ThreadName::new(format!("branch-{}", i)))
.unwrap(),
Some(*id)
);
}
}
#[test]
fn test_list_threads_includes_packed() {
let (_temp, refs) = create_ref_manager();
let id = ChangeId::generate();
refs.set_thread(&ThreadName::new("packed-branch"), &id)
.unwrap();
refs.pack_refs().unwrap();
let threads = refs.list_threads().unwrap();
assert!(threads.contains(&ThreadName::new("packed-branch")));
}
#[test]
fn test_delete_thread_removes_from_packed() {
let (_temp, refs) = create_ref_manager();
let id = ChangeId::generate();
refs.set_thread(&ThreadName::new("to-delete"), &id).unwrap();
refs.pack_refs().unwrap();
refs.delete_thread(&ThreadName::new("to-delete")).unwrap();
assert_eq!(
refs.get_thread(&ThreadName::new("to-delete")).unwrap(),
None
);
}
#[test]
fn test_packed_refs_format() {
let (_temp, refs) = create_ref_manager();
let id = ChangeId::generate();
refs.set_thread(&ThreadName::new("format-test"), &id)
.unwrap();
refs.pack_refs().unwrap();
let packed_path = refs.packed_refs_path();
let contents = std::fs::read_to_string(&packed_path).unwrap();
assert!(contents.contains("refs/threads/format-test"));
assert!(contents.contains(&id.to_string_full()));
}
#[test]
fn test_markers_in_packed_refs() {
let (_temp, refs) = create_ref_manager();
let id = ChangeId::generate();
refs.create_marker(&MarkerName::new("v1.0.0"), &id).unwrap();
refs.pack_refs().unwrap();
let loose = refs.root.join("refs/markers/v1.0.0");
assert!(!loose.exists(), "pack_refs should delete loose marker file");
assert_eq!(
refs.get_marker(&MarkerName::new("v1.0.0")).unwrap(),
Some(id)
);
}
#[test]
fn test_delete_thread_cas_removes_packed_entry() {
let (_temp, refs) = create_ref_manager();
let id = ChangeId::generate();
refs.set_thread(&ThreadName::new("packed-thread"), &id)
.unwrap();
refs.pack_refs().unwrap();
refs.delete_thread_cas(&ThreadName::new("packed-thread"), RefExpectation::Value(id))
.unwrap();
assert_eq!(
refs.get_thread(&ThreadName::new("packed-thread")).unwrap(),
None
);
}
#[test]
fn test_delete_thread_cas_packed_conflict() {
let (_temp, refs) = create_ref_manager();
let id1 = ChangeId::generate();
let id2 = ChangeId::generate();
refs.set_thread(&ThreadName::new("packed-thread"), &id1)
.unwrap();
refs.pack_refs().unwrap();
let result = refs.delete_thread_cas(
&ThreadName::new("packed-thread"),
RefExpectation::Value(id2),
);
assert!(matches!(result, Err(HeddleError::Conflict(_))));
assert_eq!(
refs.get_thread(&ThreadName::new("packed-thread")).unwrap(),
Some(id1)
);
}
#[test]
fn test_ref_summary_index_reports_packed_entries_and_loose_overrides() {
let (_temp, refs) = create_ref_manager();
let packed_thread = ChangeId::generate();
let packed_marker = ChangeId::generate();
let loose_override = ChangeId::generate();
refs.set_thread(&ThreadName::new("release"), &packed_thread)
.unwrap();
refs.create_marker(&MarkerName::new("v1.0.0"), &packed_marker)
.unwrap();
refs.pack_refs().unwrap();
let packed_summary = refs.inspect_ref_summary_index().unwrap();
assert!(packed_summary.present);
assert!(packed_summary.valid);
assert_eq!(packed_summary.threads, 1);
assert_eq!(packed_summary.markers, 1);
assert_eq!(packed_summary.packed_threads, 1);
assert_eq!(packed_summary.packed_markers, 1);
refs.set_thread(&ThreadName::new("release"), &loose_override)
.unwrap();
let override_summary = refs.inspect_ref_summary_index().unwrap();
assert!(override_summary.present);
assert!(override_summary.valid);
assert_eq!(override_summary.threads, 1);
assert_eq!(override_summary.packed_threads, 1);
assert_eq!(
refs.list_threads().unwrap(),
vec![ThreadName::new("release")]
);
assert_eq!(
refs.get_thread(&ThreadName::new("release")).unwrap(),
Some(loose_override)
);
}