fix(sync): S-01 — excluded paths no longer deleted by --delete
This commit is contained in:
@@ -226,6 +226,10 @@ enum Commands {
|
|||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
delete: bool,
|
delete: bool,
|
||||||
|
|
||||||
|
/// Löscht auch ausgeschlossene Dateien im Ziel bei aktiver --delete Spiegelung
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
delete_excluded: bool,
|
||||||
|
|
||||||
/// Führt eine Simulation aus: Zeigt Änderungen an, ohne Dateien zu schreiben oder zu löschen
|
/// Führt eine Simulation aus: Zeigt Änderungen an, ohne Dateien zu schreiben oder zu löschen
|
||||||
#[arg(short = 'n', long, default_value_t = false)]
|
#[arg(short = 'n', long, default_value_t = false)]
|
||||||
dry_run: bool,
|
dry_run: bool,
|
||||||
@@ -1216,6 +1220,7 @@ fn handle_sync(
|
|||||||
target: &str,
|
target: &str,
|
||||||
pull: bool,
|
pull: bool,
|
||||||
delete: bool,
|
delete: bool,
|
||||||
|
delete_excluded: bool,
|
||||||
dry_run: bool,
|
dry_run: bool,
|
||||||
checksum: bool,
|
checksum: bool,
|
||||||
exclude: Vec<String>,
|
exclude: Vec<String>,
|
||||||
@@ -1271,6 +1276,7 @@ fn handle_sync(
|
|||||||
let options = sanctum::sync::SyncOptions {
|
let options = sanctum::sync::SyncOptions {
|
||||||
direction,
|
direction,
|
||||||
delete,
|
delete,
|
||||||
|
delete_excluded,
|
||||||
dry_run,
|
dry_run,
|
||||||
checksum,
|
checksum,
|
||||||
exclude_patterns: exclude,
|
exclude_patterns: exclude,
|
||||||
@@ -1520,6 +1526,7 @@ async fn run() -> Result<()> {
|
|||||||
target,
|
target,
|
||||||
pull,
|
pull,
|
||||||
delete,
|
delete,
|
||||||
|
delete_excluded,
|
||||||
dry_run,
|
dry_run,
|
||||||
checksum,
|
checksum,
|
||||||
exclude,
|
exclude,
|
||||||
@@ -1532,6 +1539,7 @@ async fn run() -> Result<()> {
|
|||||||
&target,
|
&target,
|
||||||
pull,
|
pull,
|
||||||
delete,
|
delete,
|
||||||
|
delete_excluded,
|
||||||
dry_run,
|
dry_run,
|
||||||
checksum,
|
checksum,
|
||||||
exclude,
|
exclude,
|
||||||
|
|||||||
+65
-1
@@ -77,6 +77,7 @@ pub enum SyncDirection {
|
|||||||
pub struct SyncOptions {
|
pub struct SyncOptions {
|
||||||
pub direction: SyncDirection,
|
pub direction: SyncDirection,
|
||||||
pub delete: bool,
|
pub delete: bool,
|
||||||
|
pub delete_excluded: bool,
|
||||||
pub dry_run: bool,
|
pub dry_run: bool,
|
||||||
pub checksum: bool,
|
pub checksum: bool,
|
||||||
pub exclude_patterns: Vec<String>,
|
pub exclude_patterns: Vec<String>,
|
||||||
@@ -88,6 +89,7 @@ impl Default for SyncOptions {
|
|||||||
Self {
|
Self {
|
||||||
direction: SyncDirection::Push,
|
direction: SyncDirection::Push,
|
||||||
delete: false,
|
delete: false,
|
||||||
|
delete_excluded: false,
|
||||||
dry_run: false,
|
dry_run: false,
|
||||||
checksum: false,
|
checksum: false,
|
||||||
exclude_patterns: Vec::new(),
|
exclude_patterns: Vec::new(),
|
||||||
@@ -537,6 +539,7 @@ fn sync_push(
|
|||||||
} else if local_source.is_dir() {
|
} else if local_source.is_dir() {
|
||||||
let parent_node = ensure_vault_dir_tree(db, vault_id, dek, target_vault_dir)?;
|
let parent_node = ensure_vault_dir_tree(db, vault_id, dek, target_vault_dir)?;
|
||||||
let mut local_relative_paths = HashSet::new();
|
let mut local_relative_paths = HashSet::new();
|
||||||
|
let mut excluded_relative_paths = HashSet::new();
|
||||||
|
|
||||||
// Rekursiv alle lokalen Dateien und Ordner erfassen
|
// Rekursiv alle lokalen Dateien und Ordner erfassen
|
||||||
collect_and_push_dir(
|
collect_and_push_dir(
|
||||||
@@ -550,6 +553,7 @@ fn sync_push(
|
|||||||
options,
|
options,
|
||||||
stats,
|
stats,
|
||||||
&mut local_relative_paths,
|
&mut local_relative_paths,
|
||||||
|
&mut excluded_relative_paths,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Spiegelung mit --delete: Im Tresor verwaiste Dateien entfernen
|
// Spiegelung mit --delete: Im Tresor verwaiste Dateien entfernen
|
||||||
@@ -561,6 +565,9 @@ fn sync_push(
|
|||||||
parent_node.id,
|
parent_node.id,
|
||||||
"",
|
"",
|
||||||
&local_relative_paths,
|
&local_relative_paths,
|
||||||
|
&excluded_relative_paths,
|
||||||
|
&options.exclude_patterns,
|
||||||
|
options.delete_excluded,
|
||||||
options.dry_run,
|
options.dry_run,
|
||||||
options.quiet,
|
options.quiet,
|
||||||
stats,
|
stats,
|
||||||
@@ -582,6 +589,7 @@ fn collect_and_push_dir(
|
|||||||
options: &SyncOptions,
|
options: &SyncOptions,
|
||||||
stats: &mut SyncStats,
|
stats: &mut SyncStats,
|
||||||
local_relative_paths: &mut HashSet<String>,
|
local_relative_paths: &mut HashSet<String>,
|
||||||
|
excluded_relative_paths: &mut HashSet<String>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
for entry in fs::read_dir(current_dir)? {
|
for entry in fs::read_dir(current_dir)? {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
@@ -596,6 +604,7 @@ fn collect_and_push_dir(
|
|||||||
.replace('\\', "/");
|
.replace('\\', "/");
|
||||||
|
|
||||||
if is_excluded(&file_name, &rel_path, &options.exclude_patterns) {
|
if is_excluded(&file_name, &rel_path, &options.exclude_patterns) {
|
||||||
|
excluded_relative_paths.insert(rel_path.clone());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,6 +653,7 @@ fn collect_and_push_dir(
|
|||||||
options,
|
options,
|
||||||
stats,
|
stats,
|
||||||
local_relative_paths,
|
local_relative_paths,
|
||||||
|
excluded_relative_paths,
|
||||||
)?;
|
)?;
|
||||||
} else if path.is_file() {
|
} else if path.is_file() {
|
||||||
stats.files_scanned += 1;
|
stats.files_scanned += 1;
|
||||||
@@ -702,6 +712,9 @@ pub fn delete_orphans_in_vault(
|
|||||||
current_vault_id: i64,
|
current_vault_id: i64,
|
||||||
prefix_rel: &str,
|
prefix_rel: &str,
|
||||||
local_relative_paths: &HashSet<String>,
|
local_relative_paths: &HashSet<String>,
|
||||||
|
excluded_relative_paths: &HashSet<String>,
|
||||||
|
exclude_patterns: &[String],
|
||||||
|
delete_excluded: bool,
|
||||||
dry_run: bool,
|
dry_run: bool,
|
||||||
quiet: bool,
|
quiet: bool,
|
||||||
stats: &mut SyncStats,
|
stats: &mut SyncStats,
|
||||||
@@ -724,6 +737,19 @@ pub fn delete_orphans_in_vault(
|
|||||||
format!("{}/{}", prefix_rel, child.name)
|
format!("{}/{}", prefix_rel, child.name)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// S-01: Ausgeschlossene Dateien und ganze Teilbäume vor dem Löschen schützen
|
||||||
|
if !delete_excluded {
|
||||||
|
if is_excluded(&child.name, &child_rel, exclude_patterns) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if excluded_relative_paths
|
||||||
|
.iter()
|
||||||
|
.any(|ex| child_rel == *ex || child_rel.starts_with(&format!("{}/", ex)))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !local_relative_paths.contains(&child_rel) {
|
if !local_relative_paths.contains(&child_rel) {
|
||||||
stats.files_deleted += 1;
|
stats.files_deleted += 1;
|
||||||
if dry_run {
|
if dry_run {
|
||||||
@@ -748,6 +774,9 @@ pub fn delete_orphans_in_vault(
|
|||||||
child.id,
|
child.id,
|
||||||
&child_rel,
|
&child_rel,
|
||||||
local_relative_paths,
|
local_relative_paths,
|
||||||
|
excluded_relative_paths,
|
||||||
|
exclude_patterns,
|
||||||
|
delete_excluded,
|
||||||
dry_run,
|
dry_run,
|
||||||
quiet,
|
quiet,
|
||||||
stats,
|
stats,
|
||||||
@@ -836,6 +865,7 @@ fn sync_pull(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut vault_relative_paths = HashSet::new();
|
let mut vault_relative_paths = HashSet::new();
|
||||||
|
let mut excluded_relative_paths = HashSet::new();
|
||||||
|
|
||||||
collect_and_pull_dir(
|
collect_and_pull_dir(
|
||||||
db,
|
db,
|
||||||
@@ -848,6 +878,7 @@ fn sync_pull(
|
|||||||
options,
|
options,
|
||||||
stats,
|
stats,
|
||||||
&mut vault_relative_paths,
|
&mut vault_relative_paths,
|
||||||
|
&mut excluded_relative_paths,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Spiegelung mit --delete: Lokale verwaiste Dateien entfernen
|
// Spiegelung mit --delete: Lokale verwaiste Dateien entfernen
|
||||||
@@ -856,6 +887,9 @@ fn sync_pull(
|
|||||||
local_target_dir,
|
local_target_dir,
|
||||||
local_target_dir,
|
local_target_dir,
|
||||||
&vault_relative_paths,
|
&vault_relative_paths,
|
||||||
|
&excluded_relative_paths,
|
||||||
|
&options.exclude_patterns,
|
||||||
|
options.delete_excluded,
|
||||||
options.dry_run,
|
options.dry_run,
|
||||||
options.quiet,
|
options.quiet,
|
||||||
stats,
|
stats,
|
||||||
@@ -877,6 +911,7 @@ fn collect_and_pull_dir(
|
|||||||
options: &SyncOptions,
|
options: &SyncOptions,
|
||||||
stats: &mut SyncStats,
|
stats: &mut SyncStats,
|
||||||
vault_relative_paths: &mut HashSet<String>,
|
vault_relative_paths: &mut HashSet<String>,
|
||||||
|
excluded_relative_paths: &mut HashSet<String>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let carrier_id = db.find_carrier_node_id()?.unwrap_or(0);
|
let carrier_id = db.find_carrier_node_id()?.unwrap_or(0);
|
||||||
let children = db.list_children_in_vault(vault_node_id, vault_id, dek)?;
|
let children = db.list_children_in_vault(vault_node_id, vault_id, dek)?;
|
||||||
@@ -899,6 +934,7 @@ fn collect_and_pull_dir(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if is_excluded(&child.name, &child_rel, &options.exclude_patterns) {
|
if is_excluded(&child.name, &child_rel, &options.exclude_patterns) {
|
||||||
|
excluded_relative_paths.insert(child_rel.clone());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -939,6 +975,7 @@ fn collect_and_pull_dir(
|
|||||||
options,
|
options,
|
||||||
stats,
|
stats,
|
||||||
vault_relative_paths,
|
vault_relative_paths,
|
||||||
|
excluded_relative_paths,
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
stats.files_scanned += 1;
|
stats.files_scanned += 1;
|
||||||
@@ -993,6 +1030,9 @@ fn delete_orphans_on_host(
|
|||||||
base_dir: &Path,
|
base_dir: &Path,
|
||||||
current_dir: &Path,
|
current_dir: &Path,
|
||||||
vault_relative_paths: &HashSet<String>,
|
vault_relative_paths: &HashSet<String>,
|
||||||
|
excluded_relative_paths: &HashSet<String>,
|
||||||
|
exclude_patterns: &[String],
|
||||||
|
delete_excluded: bool,
|
||||||
dry_run: bool,
|
dry_run: bool,
|
||||||
quiet: bool,
|
quiet: bool,
|
||||||
stats: &mut SyncStats,
|
stats: &mut SyncStats,
|
||||||
@@ -1004,12 +1044,26 @@ fn delete_orphans_on_host(
|
|||||||
for entry in fs::read_dir(current_dir)? {
|
for entry in fs::read_dir(current_dir)? {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
|
let file_name = entry.file_name().to_string_lossy().to_string();
|
||||||
let rel_path = path
|
let rel_path = path
|
||||||
.strip_prefix(base_dir)
|
.strip_prefix(base_dir)
|
||||||
.unwrap_or(&path)
|
.unwrap_or(&path)
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.replace('\\', "/");
|
.replace('\\', "/");
|
||||||
|
|
||||||
|
// S-01: Ausgeschlossene Dateien und ganze Teilbäume vor dem Löschen schützen
|
||||||
|
if !delete_excluded {
|
||||||
|
if is_excluded(&file_name, &rel_path, exclude_patterns) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if excluded_relative_paths
|
||||||
|
.iter()
|
||||||
|
.any(|ex| rel_path == *ex || rel_path.starts_with(&format!("{}/", ex)))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !vault_relative_paths.contains(&rel_path) {
|
if !vault_relative_paths.contains(&rel_path) {
|
||||||
stats.files_deleted += 1;
|
stats.files_deleted += 1;
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
@@ -1044,7 +1098,17 @@ fn delete_orphans_on_host(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if path.is_dir() {
|
} else if path.is_dir() {
|
||||||
delete_orphans_on_host(base_dir, &path, vault_relative_paths, dry_run, quiet, stats)?;
|
delete_orphans_on_host(
|
||||||
|
base_dir,
|
||||||
|
&path,
|
||||||
|
vault_relative_paths,
|
||||||
|
excluded_relative_paths,
|
||||||
|
exclude_patterns,
|
||||||
|
delete_excluded,
|
||||||
|
dry_run,
|
||||||
|
quiet,
|
||||||
|
stats,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -269,3 +269,196 @@ fn test_sync_delete_and_exclude_flags() {
|
|||||||
|
|
||||||
let _ = fs::remove_dir_all(&temp_root);
|
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