use cloud_disk_sync::config::{AccountConfig, DiffMode, RetryPolicy, SyncPolicy, SyncTask};
use cloud_disk_sync::providers::StorageProvider;
use cloud_disk_sync::providers::WebDavProvider;
use cloud_disk_sync::sync::engine::SyncEngine;
use std::collections::HashMap;
use std::net::SocketAddr;
use std::time::Duration;
mod common;
use common::{generate_deep_structure, start_mock_server_with_seed};
#[tokio::test]
async fn test_consistency_deep_nesting() {
common::init_logging();
let (addr1, _store1) = start_mock_server_with_seed(vec![]).await;
let (addr2, _store2) = start_mock_server_with_seed(vec![]).await;
let src_cfg = create_test_config("src_deep", addr1);
let dst_cfg = create_test_config("dst_deep", addr2);
let src_provider = WebDavProvider::new(&src_cfg).await.unwrap();
let dst_provider = WebDavProvider::new(&dst_cfg).await.unwrap();
let temp_dir = std::env::temp_dir().join("deep_nest_src");
if temp_dir.exists() {
tokio::fs::remove_dir_all(&temp_dir).await.ok();
}
tokio::fs::create_dir_all(&temp_dir).await.unwrap();
common::generate_deep_structure(&temp_dir, 5, 5).await;
upload_recursive(&src_provider, &temp_dir, "/file_root").await;
let mut engine = SyncEngine::new().await.unwrap();
engine.register_provider("src".to_string(), Box::new(src_provider));
engine.register_provider("dst".to_string(), Box::new(dst_provider));
let task = SyncTask {
id: "t_deep".to_string(),
name: "deep sync".to_string(),
source_account: "src".to_string(),
source_path: "/file_root".to_string(),
target_account: "dst".to_string(),
target_path: "/file_root".to_string(),
schedule: None,
filters: vec![],
encryption: None,
diff_mode: DiffMode::Full,
preserve_metadata: false,
verify_integrity: true, sync_policy: Some(SyncPolicy {
delete_orphans: true,
overwrite_existing: true,
scan_cooldown_secs: 0,
}),
};
let report = engine.sync(&task).await.unwrap();
if !report.errors.is_empty() {
println!("Sync Errors: {:#?}", report.errors);
}
assert!(report.errors.is_empty(), "Errors: {:?}", report.errors);
assert_eq!(report.statistics.files_synced, 30);
let dst_check = engine.get_provider("dst").unwrap();
assert!(
dst_check
.exists("/file_root/level_0/test_file_0.dat")
.await
.unwrap()
);
assert!(
dst_check
.exists("/file_root/level_0/level_1/level_2/level_3/level_4/test_file_4.dat")
.await
.unwrap()
);
tokio::fs::remove_dir_all(&temp_dir).await.ok();
}
#[tokio::test]
async fn test_consistency_conflict_skip() {
common::init_logging();
let (addr1, _store1) = start_mock_server_with_seed(vec![
("/file_root/conflict.txt", "source content", false),
("/file_root/new.txt", "new content", false),
])
.await;
let (addr2, _store2) =
start_mock_server_with_seed(vec![("/file_root/conflict.txt", "target content", false)])
.await;
let src_cfg = create_test_config("src_conflict", addr1);
let dst_cfg = create_test_config("dst_conflict", addr2);
let src_provider = WebDavProvider::new(&src_cfg).await.unwrap();
let dst_provider = WebDavProvider::new(&dst_cfg).await.unwrap();
let mut engine = SyncEngine::new().await.unwrap();
engine.register_provider("src".to_string(), Box::new(src_provider));
engine.register_provider("dst".to_string(), Box::new(dst_provider));
let task = SyncTask {
id: "t_conflict".to_string(),
name: "conflict test".to_string(),
source_account: "src".to_string(),
source_path: "/file_root".to_string(),
target_account: "dst".to_string(),
target_path: "/file_root".to_string(),
schedule: None,
filters: vec![],
encryption: None,
diff_mode: DiffMode::Full,
preserve_metadata: false,
verify_integrity: false,
sync_policy: Some(SyncPolicy {
delete_orphans: false,
overwrite_existing: false, scan_cooldown_secs: 0,
}),
};
let report = engine.sync(&task).await.unwrap();
assert_eq!(report.statistics.files_synced, 1);
let dst_check = engine.get_provider("dst").unwrap();
let temp_dl = std::env::temp_dir().join("conflict_check.txt");
dst_check
.download("/file_root/conflict.txt", &temp_dl)
.await
.unwrap();
let content = tokio::fs::read_to_string(&temp_dl).await.unwrap();
assert_eq!(content, "target content"); }
#[tokio::test]
async fn test_consistency_conflict_overwrite() {
common::init_logging();
let (addr1, _store1) = start_mock_server_with_seed(vec![(
"/file_root/conflict.txt",
"source content modified",
false,
)])
.await;
let (addr2, _store2) =
start_mock_server_with_seed(vec![("/file_root/conflict.txt", "target content", false)])
.await;
let src_cfg = create_test_config("src_over", addr1);
let dst_cfg = create_test_config("dst_over", addr2);
let src_provider = WebDavProvider::new(&src_cfg).await.unwrap();
let dst_provider = WebDavProvider::new(&dst_cfg).await.unwrap();
let mut engine = SyncEngine::new().await.unwrap();
engine.register_provider("src".to_string(), Box::new(src_provider));
engine.register_provider("dst".to_string(), Box::new(dst_provider));
let src_p = engine.get_provider("src").unwrap();
let files = src_p.list("/file_root").await.unwrap();
println!("Source files debug overwrite: {:?}", files);
let task = SyncTask {
id: "t_overwrite".to_string(),
name: "overwrite test".to_string(),
source_account: "src".to_string(),
source_path: "/file_root".to_string(),
target_account: "dst".to_string(),
target_path: "/file_root".to_string(),
schedule: None,
filters: vec![],
encryption: None,
diff_mode: DiffMode::Full,
preserve_metadata: false,
verify_integrity: false,
sync_policy: Some(SyncPolicy {
delete_orphans: false,
overwrite_existing: true, scan_cooldown_secs: 0,
}),
};
let report = engine.sync(&task).await.unwrap();
println!("Sync Report: {:?}", report);
assert_eq!(
report.statistics.files_synced, 1,
"Files failed: {}, Errors: {:?}",
report.statistics.files_failed, report.errors
);
let dst_check = engine.get_provider("dst").unwrap();
let temp_dl = std::env::temp_dir().join("overwrite_check.txt");
dst_check
.download("/file_root/conflict.txt", &temp_dl)
.await
.unwrap();
let content = tokio::fs::read_to_string(&temp_dl).await.unwrap();
assert_eq!(content, "source content modified");
}
fn create_test_config(id: &str, addr: SocketAddr) -> AccountConfig {
AccountConfig {
id: id.to_string(),
provider: cloud_disk_sync::config::ProviderType::WebDAV,
name: id.to_string(),
credentials: {
let mut c = HashMap::new();
c.insert("url".to_string(), format!("http://{}", addr));
c.insert("username".to_string(), "u".to_string());
c.insert("password".to_string(), "p".to_string());
c
},
rate_limit: None,
retry_policy: RetryPolicy::default(),
}
}
async fn upload_recursive(
provider: &WebDavProvider,
local_dir: &std::path::Path,
remote_base: &str,
) {
let mut stack = vec![local_dir.to_path_buf()];
while let Some(dir) = stack.pop() {
let mut entries = tokio::fs::read_dir(&dir).await.unwrap();
while let Ok(Some(entry)) = entries.next_entry().await {
let path = entry.path();
let rel_path = path.strip_prefix(local_dir).unwrap();
let remote_path = format!(
"{}/{}",
remote_base.trim_end_matches('/'),
rel_path.to_string_lossy().replace("\\", "/")
);
let remote_path = remote_path.replace("//", "/");
if path.is_dir() {
provider.mkdir(&remote_path).await.ok(); stack.push(path);
} else {
StorageProvider::upload(provider, &path, &remote_path)
.await
.unwrap();
}
}
}
}