memvid--memvid
63 行
2.3 KiB
Rust
63 行
2.3 KiB
Rust
use std::fs::OpenOptions;
|
|
use std::io::{Seek, SeekFrom, Write};
|
|
|
|
use memvid_core::{DoctorOptions, DoctorStatus, Memvid, MemvidError};
|
|
|
|
#[test]
|
|
#[cfg(not(target_os = "windows"))] // Windows file locking prevents proper corruption simulation
|
|
fn doctor_recovers_from_corrupted_commit_footer_single_commit() {
|
|
let dir = tempfile::tempdir().expect("tmp");
|
|
let path = dir.path().join("corrupt-footer.mv2");
|
|
|
|
{
|
|
let mut mem = Memvid::create(&path).expect("create");
|
|
mem.put_bytes(b"hello world").expect("put");
|
|
mem.commit().expect("commit");
|
|
}
|
|
|
|
// Corrupt the final bytes of the commit footer. This simulates an interrupted/partial write
|
|
// at the end of the file (e.g., crash during sync).
|
|
{
|
|
let mut file = OpenOptions::new()
|
|
.read(true)
|
|
.write(true)
|
|
.open(&path)
|
|
.expect("open mv2");
|
|
let len = file.metadata().expect("stat").len();
|
|
assert!(len > 16, "mv2 too small to corrupt footer");
|
|
file.seek(SeekFrom::End(-16)).expect("seek footer tail");
|
|
file.write_all(&[0u8; 16]).expect("corrupt footer tail");
|
|
file.sync_all().expect("sync");
|
|
}
|
|
|
|
// Snapshot reads are strict: without a valid commit footer, they fail fast.
|
|
let err = Memvid::open_read_only(&path)
|
|
.err()
|
|
.expect("expected open_read_only to fail on corrupted footer");
|
|
match err {
|
|
MemvidError::InvalidToc { reason } => {
|
|
let reason = reason.to_string();
|
|
assert!(
|
|
reason.contains("no valid commit footer")
|
|
|| reason.contains("commit footer")
|
|
|| reason.contains("footer"),
|
|
"unexpected invalid toc reason: {reason}"
|
|
);
|
|
}
|
|
other => panic!("unexpected error: {other}"),
|
|
}
|
|
|
|
// Exclusive opens can recover the TOC even when the footer is corrupted.
|
|
Memvid::open(&path).expect("open with recovery");
|
|
|
|
// Doctor should rewrite a clean TOC+footer so snapshot reads succeed again.
|
|
let report = Memvid::doctor(&path, DoctorOptions::default()).expect("doctor");
|
|
assert!(
|
|
matches!(report.status, DoctorStatus::Healed | DoctorStatus::Clean),
|
|
"unexpected doctor status: {:?}",
|
|
report.status
|
|
);
|
|
|
|
Memvid::open_read_only(&path).expect("open_read_only after doctor");
|
|
}
|