use super::*;
#[test]
fn lock_manifest_is_sorted() {
let data = TempDir::new("analysis_lock_sorted");
data.write("textures/z.dds", b"z");
data.write("textures/a.dds", b"a");
let (vfs, index) = VFS::from_directories_with_layer_index([data.path()], None);
let lock = index
.lock_manifest(&vfs)
.expect("lock manifest should succeed");
assert_eq!(lock.schema_version, 1);
assert_eq!(lock.entries[0].key, PathBuf::from("textures/a.dds"));
assert_eq!(lock.entries[1].key, PathBuf::from("textures/z.dds"));
}
#[test]
fn lock_manifest_hashes_mixed_case_loose_winner_path() {
let data = TempDir::new("analysis_lock_mixed_case");
data.write("Textures/Foo.DDS", b"mixed");
let (vfs, index) = VFS::from_directories_with_layer_index([data.path()], None);
let lock = index.lock_manifest(&vfs).expect("lock should build");
assert_eq!(lock.entries[0].key, PathBuf::from("textures/foo.dds"));
assert_eq!(lock.entries[0].winner_size, Some(5));
assert!(lock.entries[0].winner_hash_blake3.is_some());
}
#[test]
fn lock_manifest_hashes_actual_same_source_winner_occurrence() {
let data = TempDir::new("analysis_lock_same_source_occurrence");
data.write("Textures/Foo.DDS", b"long losing content");
data.write("textures/foo.dds", b"win");
let (vfs, index) = VFS::from_directories_with_layer_index([data.path()], None);
let winner_len = fs::read(vfs.get_file("textures/foo.dds").unwrap().path())
.unwrap()
.len() as u64;
let lock = index.lock_manifest(&vfs).expect("lock should build");
assert_eq!(lock.entries[0].key, PathBuf::from("textures/foo.dds"));
assert_eq!(lock.entries[0].winner_size, Some(winner_len));
}
#[test]
fn lock_manifest_hashes_manual_loose_provider_actual_file_path() {
let source_root = TempDir::new("analysis_lock_manual_source_root");
let actual_root = TempDir::new("analysis_lock_manual_actual_root");
actual_root.write("actual.txt", b"actual bytes");
let actual_file = actual_root.path().join("actual.txt");
let mut vfs = VFS::new();
assert!(vfs.push_provider(
"virtual.txt",
crate::VfsProvider {
source: SourceMeta {
path: source_root.path().to_path_buf(),
kind: SourceKind::LooseDir,
},
file: crate::VfsFile::from(&actual_file),
},
));
let lock = vfs
.layer_index()
.lock_manifest(&vfs)
.expect("lock should build");
assert_eq!(lock.entries[0].winner_size, Some(12));
}
#[test]
fn lock_manifest_uses_actual_vfs_winner_presence() {
let low = TempDir::new("analysis_lock_removed_low");
let high = TempDir::new("analysis_lock_removed_high");
low.write("shared.txt", b"low");
high.write("shared.txt", b"high");
let (mut vfs, index) = VFS::from_directories_with_layer_index([low.path(), high.path()], None);
vfs.remove_resolved_file("shared.txt");
let lock = index.lock_manifest(&vfs).expect("lock should build");
assert!(lock.entries.is_empty());
}
#[test]
fn lock_manifest_is_deterministic_across_runs() {
let low = TempDir::new("analysis_lock_deterministic_low");
let high = TempDir::new("analysis_lock_deterministic_high");
low.write("textures/a.dds", b"aaa");
high.write("textures/a.dds", b"bbb");
low.write("meshes/m.nif", b"m");
let (vfs, index) = VFS::from_directories_with_layer_index([low.path(), high.path()], None);
let first = index
.lock_manifest(&vfs)
.expect("first lock build should succeed");
let second = index
.lock_manifest(&vfs)
.expect("second lock build should succeed");
let first_rows = first
.entries
.iter()
.map(|entry| {
(
entry.key.clone(),
entry.winner_source.clone(),
entry.winner_kind,
entry.winner_hash_blake3.clone(),
entry.winner_size,
entry.provider_count,
)
})
.collect::<Vec<_>>();
let second_rows = second
.entries
.iter()
.map(|entry| {
(
entry.key.clone(),
entry.winner_source.clone(),
entry.winner_kind,
entry.winner_hash_blake3.clone(),
entry.winner_size,
entry.provider_count,
)
})
.collect::<Vec<_>>();
assert_eq!(first_rows, second_rows);
}
#[test]
fn drift_detects_source_and_hash_changes() {
let low = TempDir::new("analysis_drift_low");
let high = TempDir::new("analysis_drift_high");
low.write("textures/a.dds", b"aaa");
high.write("textures/a.dds", b"bbb");
let (vfs, index) = VFS::from_directories_with_layer_index([low.path(), high.path()], None);
let mut lock = index
.lock_manifest(&vfs)
.expect("lock build should succeed");
lock.entries[0].winner_source = low.path().to_path_buf();
lock.entries[0].winner_hash_blake3 = Some("00".repeat(32));
let drift = index
.diff_against_lock(&vfs, &lock)
.expect("drift diff should succeed");
assert!(
drift
.entries
.iter()
.any(|entry| entry.kind == DriftKind::WinnerSourceChanged)
);
assert!(
drift
.entries
.iter()
.any(|entry| entry.kind == DriftKind::WinnerHashChanged)
);
}