fix(sync): S-01 — excluded paths no longer deleted by --delete
This commit is contained in:
@@ -269,3 +269,196 @@ fn test_sync_delete_and_exclude_flags() {
|
||||
|
||||
let _ = fs::remove_dir_all(&temp_root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_s01_delete_preserves_excluded_files_and_directories() {
|
||||
let temp_root =
|
||||
std::env::temp_dir().join(format!("sanctum_s01_test_{}", rand::random::<u64>()));
|
||||
let container_path = temp_root.join("test_s01.sanctum");
|
||||
let source_dir = temp_root.join("source");
|
||||
|
||||
fs::create_dir_all(&source_dir).unwrap();
|
||||
fs::create_dir_all(source_dir.join("excluded_dir")).unwrap();
|
||||
fs::write(source_dir.join("normal.txt"), b"Normal file").unwrap();
|
||||
fs::write(source_dir.join("document.pdf"), b"Important PDF").unwrap();
|
||||
fs::write(
|
||||
source_dir.join("excluded_dir").join("subfile.txt"),
|
||||
b"Subfile in excluded dir",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (db, dek) = create_test_container(&container_path);
|
||||
|
||||
// 1. Initialer Push ALLER Dateien (ohne Exclude)
|
||||
let mut opts = SyncOptions::default();
|
||||
opts.direction = SyncDirection::Push;
|
||||
opts.quiet = true;
|
||||
|
||||
run_sync(
|
||||
&db,
|
||||
0,
|
||||
&dek,
|
||||
FORMAT_VERSION,
|
||||
source_dir.to_str().unwrap(),
|
||||
"/Data",
|
||||
&opts,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(db
|
||||
.resolve_path_in_vault("/Data/document.pdf", 0, &dek)
|
||||
.unwrap()
|
||||
.is_some());
|
||||
assert!(db
|
||||
.resolve_path_in_vault("/Data/excluded_dir/subfile.txt", 0, &dek)
|
||||
.unwrap()
|
||||
.is_some());
|
||||
|
||||
// 2. Lokale Kopie von document.pdf und excluded_dir entfernen
|
||||
fs::remove_file(source_dir.join("document.pdf")).unwrap();
|
||||
fs::remove_dir_all(source_dir.join("excluded_dir")).unwrap();
|
||||
|
||||
// 3. Sync Push mit --delete aber MIT --exclude "*.pdf" und --exclude "excluded_dir*"
|
||||
opts.delete = true;
|
||||
opts.delete_excluded = false;
|
||||
opts.exclude_patterns = vec!["*.pdf".to_string(), "excluded_dir*".to_string()];
|
||||
|
||||
let stats = run_sync(
|
||||
&db,
|
||||
0,
|
||||
&dek,
|
||||
FORMAT_VERSION,
|
||||
source_dir.to_str().unwrap(),
|
||||
"/Data",
|
||||
&opts,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
stats.files_deleted, 0,
|
||||
"Ausgeschlossene Dateien/Ordner dürfen NICHT gelöscht werden!"
|
||||
);
|
||||
|
||||
// PDF und excluded_dir Teilbaum müssen im Tresor überlebt haben!
|
||||
assert!(
|
||||
db.resolve_path_in_vault("/Data/document.pdf", 0, &dek)
|
||||
.unwrap()
|
||||
.is_some(),
|
||||
"PDF im Tresor muss überleben"
|
||||
);
|
||||
assert!(
|
||||
db.resolve_path_in_vault("/Data/excluded_dir/subfile.txt", 0, &dek)
|
||||
.unwrap()
|
||||
.is_some(),
|
||||
"Teilbaum in excluded_dir muss überleben"
|
||||
);
|
||||
|
||||
// 4. Jetzt Push mit --delete UND --delete-excluded
|
||||
opts.delete_excluded = true;
|
||||
let stats_del = run_sync(
|
||||
&db,
|
||||
0,
|
||||
&dek,
|
||||
FORMAT_VERSION,
|
||||
source_dir.to_str().unwrap(),
|
||||
"/Data",
|
||||
&opts,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
stats_del.files_deleted >= 2,
|
||||
"Mit --delete-excluded müssen die verwaisten ausgeschlossenen Dateien gelöscht werden"
|
||||
);
|
||||
assert!(
|
||||
db.resolve_path_in_vault("/Data/document.pdf", 0, &dek)
|
||||
.unwrap()
|
||||
.is_none(),
|
||||
"PDF muss mit --delete-excluded gelöscht sein"
|
||||
);
|
||||
assert!(
|
||||
db.resolve_path_in_vault("/Data/excluded_dir/subfile.txt", 0, &dek)
|
||||
.unwrap()
|
||||
.is_none(),
|
||||
"excluded_dir Inhalt muss gelöscht sein"
|
||||
);
|
||||
|
||||
let _ = fs::remove_dir_all(&temp_root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_s01_pull_delete_preserves_local_excluded_and_leak_files() {
|
||||
let temp_root =
|
||||
std::env::temp_dir().join(format!("sanctum_s01_pull_{}", rand::random::<u64>()));
|
||||
let container_path = temp_root.join("test_s01_pull.sanctum");
|
||||
let restore_dir = temp_root.join("restored");
|
||||
|
||||
fs::create_dir_all(&restore_dir).unwrap();
|
||||
// Lokale Dateien anlegen, die im Vault NICHT existieren: folder.jpg (Leak file) und local_notes.pdf (Ausschluss)
|
||||
fs::write(
|
||||
restore_dir.join("folder.jpg"),
|
||||
b"Album Art or Explorer Cache",
|
||||
)
|
||||
.unwrap();
|
||||
fs::write(
|
||||
restore_dir.join("local_notes.pdf"),
|
||||
b"Private Local Notes",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (db, dek) = create_test_container(&container_path);
|
||||
|
||||
// Eine Datei im Vault anlegen
|
||||
let root_node = db.get_node_by_id_in_vault(1, 0, &dek).unwrap().unwrap();
|
||||
let _ = db
|
||||
.create_node_in_vault(0, root_node.id, "vault_file.txt", false, &dek)
|
||||
.unwrap();
|
||||
|
||||
// Pull mit --delete und --exclude "*.pdf"
|
||||
let mut opts = SyncOptions::default();
|
||||
opts.direction = SyncDirection::Pull;
|
||||
opts.delete = true;
|
||||
opts.delete_excluded = false;
|
||||
opts.exclude_patterns = vec!["*.pdf".to_string()];
|
||||
opts.quiet = true;
|
||||
|
||||
let stats = run_sync(
|
||||
&db,
|
||||
0,
|
||||
&dek,
|
||||
FORMAT_VERSION,
|
||||
"/",
|
||||
restore_dir.to_str().unwrap(),
|
||||
&opts,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
stats.files_deleted, 0,
|
||||
"folder.jpg und local_notes.pdf dürfen bei Pull mit --delete NICHT gelöscht werden!"
|
||||
);
|
||||
assert!(
|
||||
restore_dir.join("folder.jpg").exists(),
|
||||
"folder.jpg muss auf dem Host erhalten bleiben"
|
||||
);
|
||||
assert!(
|
||||
restore_dir.join("local_notes.pdf").exists(),
|
||||
"local_notes.pdf muss auf dem Host erhalten bleiben"
|
||||
);
|
||||
|
||||
// Pull mit --delete UND --delete-excluded
|
||||
opts.delete_excluded = true;
|
||||
run_sync(
|
||||
&db,
|
||||
0,
|
||||
&dek,
|
||||
FORMAT_VERSION,
|
||||
"/",
|
||||
restore_dir.to_str().unwrap(),
|
||||
&opts,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
!restore_dir.join("local_notes.pdf").exists(),
|
||||
"local_notes.pdf muss mit --delete-excluded gelöscht werden"
|
||||
);
|
||||
|
||||
let _ = fs::remove_dir_all(&temp_root);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user