fix(sync): S-05 — delete and shred all chunks when truncating to 0 bytes
This commit is contained in:
@@ -689,3 +689,100 @@ fn test_s04_toctou_file_size_uses_bytes_written() {
|
||||
|
||||
let _ = fs::remove_dir_all(&temp_root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_s05_zero_byte_file_truncate_removes_all_chunks() {
|
||||
let temp_root =
|
||||
std::env::temp_dir().join(format!("sanctum_s05_test_{}", rand::random::<u64>()));
|
||||
let container_path = temp_root.join("test.sanctum");
|
||||
let file_path = temp_root.join("truncate_file.bin");
|
||||
|
||||
fs::create_dir_all(&temp_root).unwrap();
|
||||
// 1. Zuerst große Datei mit 2.5 Chunks (> 2 MB) anlegen
|
||||
let large_payload = vec![0xEEu8; 2_500_000];
|
||||
fs::write(&file_path, &large_payload).unwrap();
|
||||
|
||||
let (db, dek) = create_test_container(&container_path);
|
||||
|
||||
// Initialer Push
|
||||
sanctum::sync::sync_single_file_to_vault(
|
||||
&db,
|
||||
0,
|
||||
&dek,
|
||||
FORMAT_VERSION,
|
||||
&file_path,
|
||||
1,
|
||||
"truncate_file.bin",
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.expect("Initial push of large file");
|
||||
|
||||
let node = db
|
||||
.resolve_path_in_vault("/truncate_file.bin", 0, &dek)
|
||||
.unwrap()
|
||||
.expect("Node must exist");
|
||||
assert_eq!(node.size, 2_500_000);
|
||||
|
||||
// Prüfe, dass Chunks existieren (3 Chunks: 0, 1, 2)
|
||||
{
|
||||
let conn = rusqlite::Connection::open(&container_path).unwrap();
|
||||
let count: i64 = conn
|
||||
.query_row(
|
||||
"SELECT COUNT(*) FROM chunks WHERE node_id = ?1",
|
||||
rusqlite::params![node.id],
|
||||
|r| r.get(0),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(count, 3, "Datei muss 3 Chunks besitzen");
|
||||
}
|
||||
|
||||
// 2. Jetzt: Lokale Datei durch 0-Byte Datei ersetzen (Truncate auf 0 Bytes)
|
||||
std::thread::sleep(std::time::Duration::from_millis(1100)); // Timestamp ändern
|
||||
fs::write(&file_path, b"").unwrap();
|
||||
|
||||
let res = sanctum::sync::sync_single_file_to_vault(
|
||||
&db,
|
||||
0,
|
||||
&dek,
|
||||
FORMAT_VERSION,
|
||||
&file_path,
|
||||
1,
|
||||
"truncate_file.bin",
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.expect("Push of 0-byte file");
|
||||
|
||||
match res {
|
||||
sanctum::sync::FileTransferResult::Transferred { size } => {
|
||||
assert_eq!(size, 0);
|
||||
}
|
||||
_ => panic!("Expected Transferred {{ size: 0 }}"),
|
||||
}
|
||||
|
||||
// 3. Verifiziere: In SQLite darf KEIN EINZIGER Chunk mehr für diesen Knoten existieren!
|
||||
// (Ohne den S-05 Fix blieb Chunk 0 fälschlicherweise in der Tabelle zurück)
|
||||
{
|
||||
let conn = rusqlite::Connection::open(&container_path).unwrap();
|
||||
let count: i64 = conn
|
||||
.query_row(
|
||||
"SELECT COUNT(*) FROM chunks WHERE node_id = ?1",
|
||||
rusqlite::params![node.id],
|
||||
|r| r.get(0),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
count, 0,
|
||||
"Bei 0-Byte Datei müssen ALLE Chunks (inkl. Chunk 0) gelöscht sein (S-05)"
|
||||
);
|
||||
}
|
||||
|
||||
let updated_node = db
|
||||
.resolve_path_in_vault("/truncate_file.bin", 0, &dek)
|
||||
.unwrap()
|
||||
.expect("Node must exist");
|
||||
assert_eq!(updated_node.size, 0);
|
||||
|
||||
let _ = fs::remove_dir_all(&temp_root);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user