mirror of
https://github.com/readur/readur.git
synced 2025-12-17 20:35:17 -06:00
664 lines
23 KiB
Rust
664 lines
23 KiB
Rust
use readur::services::webdav::{WebDAVService, WebDAVConfig, RetryConfig};
|
|
use readur::webdav_xml_parser::parse_propfind_response;
|
|
use readur::models::FileIngestionInfo;
|
|
use readur::models::source::WebDAVSyncStatus;
|
|
use readur::models::*;
|
|
use chrono::Utc;
|
|
use uuid::Uuid;
|
|
|
|
// Mock WebDAV responses for comprehensive testing
|
|
fn mock_nextcloud_propfind_response() -> String {
|
|
r#"<?xml version="1.0"?>
|
|
<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns">
|
|
<d:response>
|
|
<d:href>/remote.php/dav/files/admin/Documents/</d:href>
|
|
<d:propstat>
|
|
<d:prop>
|
|
<d:displayname>Documents</d:displayname>
|
|
<d:getlastmodified>Tue, 01 Jan 2024 12:00:00 GMT</d:getlastmodified>
|
|
<d:resourcetype>
|
|
<d:collection/>
|
|
</d:resourcetype>
|
|
<d:getetag>"abc123"</d:getetag>
|
|
</d:prop>
|
|
<d:status>HTTP/1.1 200 OK</d:status>
|
|
</d:propstat>
|
|
</d:response>
|
|
<d:response>
|
|
<d:href>/remote.php/dav/files/admin/Documents/report.pdf</d:href>
|
|
<d:propstat>
|
|
<d:prop>
|
|
<d:displayname>report.pdf</d:displayname>
|
|
<d:getcontentlength>2048000</d:getcontentlength>
|
|
<d:getlastmodified>Mon, 15 Jan 2024 14:30:00 GMT</d:getlastmodified>
|
|
<d:getcontenttype>application/pdf</d:getcontenttype>
|
|
<d:getetag>"pdf123"</d:getetag>
|
|
<d:resourcetype/>
|
|
</d:prop>
|
|
<d:status>HTTP/1.1 200 OK</d:status>
|
|
</d:propstat>
|
|
</d:response>
|
|
<d:response>
|
|
<d:href>/remote.php/dav/files/admin/Documents/photo.png</d:href>
|
|
<d:propstat>
|
|
<d:prop>
|
|
<d:displayname>photo.png</d:displayname>
|
|
<d:getcontentlength>768000</d:getcontentlength>
|
|
<d:getlastmodified>Wed, 10 Jan 2024 09:15:00 GMT</d:getlastmodified>
|
|
<d:getcontenttype>image/png</d:getcontenttype>
|
|
<d:getetag>"png456"</d:getetag>
|
|
<d:resourcetype/>
|
|
</d:prop>
|
|
<d:status>HTTP/1.1 200 OK</d:status>
|
|
</d:propstat>
|
|
</d:response>
|
|
<d:response>
|
|
<d:href>/remote.php/dav/files/admin/Documents/unsupported.docx</d:href>
|
|
<d:propstat>
|
|
<d:prop>
|
|
<d:displayname>unsupported.docx</d:displayname>
|
|
<d:getcontentlength>102400</d:getcontentlength>
|
|
<d:getlastmodified>Thu, 20 Jan 2024 16:45:00 GMT</d:getlastmodified>
|
|
<d:getcontenttype>application/vnd.openxmlformats-officedocument.wordprocessingml.document</d:getcontenttype>
|
|
<d:getetag>"docx789"</d:getetag>
|
|
<d:resourcetype/>
|
|
</d:prop>
|
|
<d:status>HTTP/1.1 200 OK</d:status>
|
|
</d:propstat>
|
|
</d:response>
|
|
</d:multistatus>"#.to_string()
|
|
}
|
|
|
|
fn mock_empty_folder_response() -> String {
|
|
r#"<?xml version="1.0"?>
|
|
<d:multistatus xmlns:d="DAV:">
|
|
<d:response>
|
|
<d:href>/webdav/EmptyFolder/</d:href>
|
|
<d:propstat>
|
|
<d:prop>
|
|
<d:displayname>EmptyFolder</d:displayname>
|
|
<d:getlastmodified>Fri, 01 Jan 2024 12:00:00 GMT</d:getlastmodified>
|
|
<d:resourcetype>
|
|
<d:collection/>
|
|
</d:resourcetype>
|
|
</d:prop>
|
|
<d:status>HTTP/1.1 200 OK</d:status>
|
|
</d:propstat>
|
|
</d:response>
|
|
</d:multistatus>"#.to_string()
|
|
}
|
|
|
|
fn mock_malformed_xml_response() -> String {
|
|
r#"<?xml version="1.0"?>
|
|
<d:multistatus xmlns:d="DAV:">
|
|
<d:response>
|
|
<d:href>/webdav/test.pdf</d:href>
|
|
<d:propstat>
|
|
<d:prop>
|
|
<d:displayname>test.pdf
|
|
<!-- Missing closing tag -->
|
|
</d:prop>
|
|
</d:propstat>
|
|
</d:response>
|
|
<!-- Incomplete XML -->"#.to_string()
|
|
}
|
|
|
|
#[test]
|
|
fn test_webdav_config_validation() {
|
|
// Test valid config
|
|
let valid_config = WebDAVConfig {
|
|
server_url: "https://cloud.example.com".to_string(),
|
|
username: "testuser".to_string(),
|
|
password: "testpass".to_string(),
|
|
watch_folders: vec!["/Documents".to_string(), "/Photos".to_string()],
|
|
file_extensions: vec!["pdf".to_string(), "png".to_string(), "jpg".to_string()],
|
|
timeout_seconds: 30,
|
|
server_type: Some("nextcloud".to_string()),
|
|
};
|
|
|
|
assert!(WebDAVService::new(valid_config).is_ok());
|
|
|
|
// Test config with empty server URL - should fail with our enhanced validation
|
|
let invalid_config = WebDAVConfig {
|
|
server_url: "".to_string(),
|
|
username: "testuser".to_string(),
|
|
password: "testpass".to_string(),
|
|
watch_folders: vec!["/Documents".to_string()],
|
|
file_extensions: vec!["pdf".to_string()],
|
|
timeout_seconds: 30,
|
|
server_type: Some("nextcloud".to_string()),
|
|
};
|
|
|
|
// Should fail early with enhanced validation
|
|
assert!(WebDAVService::new(invalid_config).is_err());
|
|
|
|
// Test config with invalid URL scheme - should also fail
|
|
let invalid_scheme_config = WebDAVConfig {
|
|
server_url: "ftp://cloud.example.com".to_string(),
|
|
username: "testuser".to_string(),
|
|
password: "testpass".to_string(),
|
|
watch_folders: vec!["/Documents".to_string()],
|
|
file_extensions: vec!["pdf".to_string()],
|
|
timeout_seconds: 30,
|
|
server_type: Some("nextcloud".to_string()),
|
|
};
|
|
|
|
assert!(WebDAVService::new(invalid_scheme_config).is_err());
|
|
|
|
// Test config with relative URL - should also fail
|
|
let relative_url_config = WebDAVConfig {
|
|
server_url: "/webdav".to_string(),
|
|
username: "testuser".to_string(),
|
|
password: "testpass".to_string(),
|
|
watch_folders: vec!["/Documents".to_string()],
|
|
file_extensions: vec!["pdf".to_string()],
|
|
timeout_seconds: 30,
|
|
server_type: Some("nextcloud".to_string()),
|
|
};
|
|
|
|
assert!(WebDAVService::new(relative_url_config).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_webdav_url_construction_comprehensive() {
|
|
// Test Nextcloud URL construction
|
|
let nextcloud_config = WebDAVConfig {
|
|
server_url: "https://nextcloud.example.com".to_string(),
|
|
username: "admin".to_string(),
|
|
password: "secret".to_string(),
|
|
watch_folders: vec!["/Documents".to_string()],
|
|
file_extensions: vec!["pdf".to_string()],
|
|
timeout_seconds: 30,
|
|
server_type: Some("nextcloud".to_string()),
|
|
};
|
|
|
|
let service = WebDAVService::new(nextcloud_config).unwrap();
|
|
// URL construction is tested implicitly during service creation
|
|
|
|
// Test ownCloud URL construction
|
|
let owncloud_config = WebDAVConfig {
|
|
server_url: "https://cloud.example.com/".to_string(), // With trailing slash
|
|
username: "user123".to_string(),
|
|
password: "pass123".to_string(),
|
|
watch_folders: vec!["/Shared".to_string()],
|
|
file_extensions: vec!["jpg".to_string()],
|
|
timeout_seconds: 60,
|
|
server_type: Some("owncloud".to_string()),
|
|
};
|
|
|
|
assert!(WebDAVService::new(owncloud_config).is_ok());
|
|
|
|
// Test generic WebDAV URL construction
|
|
let generic_config = WebDAVConfig {
|
|
server_url: "https://webdav.example.com".to_string(),
|
|
username: "webdavuser".to_string(),
|
|
password: "webdavpass".to_string(),
|
|
watch_folders: vec!["/Files".to_string()],
|
|
file_extensions: vec!["txt".to_string()],
|
|
timeout_seconds: 45,
|
|
server_type: None, // No server type = generic
|
|
};
|
|
|
|
assert!(WebDAVService::new(generic_config).is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_webdav_response_parsing_comprehensive() {
|
|
let config = WebDAVConfig {
|
|
server_url: "https://cloud.example.com".to_string(),
|
|
username: "admin".to_string(),
|
|
password: "testpass".to_string(),
|
|
watch_folders: vec!["/Documents".to_string()],
|
|
file_extensions: vec!["pdf".to_string(), "png".to_string(), "jpg".to_string()],
|
|
timeout_seconds: 30,
|
|
server_type: Some("nextcloud".to_string()),
|
|
};
|
|
|
|
let service = WebDAVService::new(config.clone()).unwrap();
|
|
|
|
// Test Nextcloud response parsing
|
|
let nextcloud_response = mock_nextcloud_propfind_response();
|
|
let files = parse_propfind_response(&nextcloud_response);
|
|
assert!(files.is_ok());
|
|
|
|
let files = files.unwrap();
|
|
|
|
// Filter files by supported extensions
|
|
let supported_files: Vec<_> = files.iter()
|
|
.filter(|f| {
|
|
if let Some(ext) = std::path::Path::new(&f.name)
|
|
.extension()
|
|
.and_then(|e| e.to_str())
|
|
{
|
|
config.file_extensions.contains(&ext.to_lowercase())
|
|
} else {
|
|
false
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
assert_eq!(supported_files.len(), 2); // Should have 2 files with supported extensions (pdf, png)
|
|
|
|
// Verify first file (report.pdf)
|
|
let pdf_file = files.iter().find(|f| f.name == "report.pdf").unwrap();
|
|
assert_eq!(pdf_file.size, 2048000);
|
|
assert_eq!(pdf_file.mime_type, "application/pdf");
|
|
assert_eq!(pdf_file.etag, "pdf123"); // ETag should be normalized (quotes removed)
|
|
assert!(!pdf_file.is_directory);
|
|
|
|
// Verify second file (photo.png)
|
|
let png_file = files.iter().find(|f| f.name == "photo.png").unwrap();
|
|
assert_eq!(png_file.size, 768000);
|
|
assert_eq!(png_file.mime_type, "image/png");
|
|
assert_eq!(png_file.etag, "png456"); // ETag should be normalized (quotes removed)
|
|
assert!(!png_file.is_directory);
|
|
|
|
// Verify that unsupported file (docx) is not included in supported files
|
|
assert!(supported_files.iter().find(|f| f.name == "unsupported.docx").is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn test_empty_folder_parsing() {
|
|
let config = WebDAVConfig {
|
|
server_url: "https://cloud.example.com".to_string(),
|
|
username: "testuser".to_string(),
|
|
password: "testpass".to_string(),
|
|
watch_folders: vec!["/EmptyFolder".to_string()],
|
|
file_extensions: vec!["pdf".to_string()],
|
|
timeout_seconds: 30,
|
|
server_type: Some("generic".to_string()),
|
|
};
|
|
|
|
let service = WebDAVService::new(config).unwrap();
|
|
let response = mock_empty_folder_response();
|
|
|
|
let files = parse_propfind_response(&response);
|
|
assert!(files.is_ok());
|
|
|
|
let files = files.unwrap();
|
|
assert_eq!(files.len(), 0); // Empty folder should have no files
|
|
}
|
|
|
|
#[test]
|
|
fn test_malformed_xml_handling() {
|
|
let config = WebDAVConfig {
|
|
server_url: "https://cloud.example.com".to_string(),
|
|
username: "testuser".to_string(),
|
|
password: "testpass".to_string(),
|
|
watch_folders: vec!["/Documents".to_string()],
|
|
file_extensions: vec!["pdf".to_string()],
|
|
timeout_seconds: 30,
|
|
server_type: Some("nextcloud".to_string()),
|
|
};
|
|
|
|
let service = WebDAVService::new(config).unwrap();
|
|
let response = mock_malformed_xml_response();
|
|
|
|
// Current simple parser might still extract some data from malformed XML
|
|
let result = parse_propfind_response(&response);
|
|
// It might succeed or fail depending on how robust the parser is
|
|
assert!(result.is_ok() || result.is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_retry_config_custom_values() {
|
|
let custom_retry = RetryConfig {
|
|
max_retries: 5,
|
|
initial_delay_ms: 500,
|
|
max_delay_ms: 15000,
|
|
backoff_multiplier: 1.5,
|
|
timeout_seconds: 90,
|
|
rate_limit_backoff_ms: 10000,
|
|
};
|
|
|
|
assert_eq!(custom_retry.max_retries, 5);
|
|
assert_eq!(custom_retry.initial_delay_ms, 500);
|
|
assert_eq!(custom_retry.max_delay_ms, 15000);
|
|
assert_eq!(custom_retry.backoff_multiplier, 1.5);
|
|
assert_eq!(custom_retry.timeout_seconds, 90);
|
|
assert_eq!(custom_retry.rate_limit_backoff_ms, 10000);
|
|
|
|
let config = WebDAVConfig {
|
|
server_url: "https://cloud.example.com".to_string(),
|
|
username: "testuser".to_string(),
|
|
password: "testpass".to_string(),
|
|
watch_folders: vec!["/Documents".to_string()],
|
|
file_extensions: vec!["pdf".to_string()],
|
|
timeout_seconds: 30,
|
|
server_type: Some("nextcloud".to_string()),
|
|
};
|
|
|
|
assert!(WebDAVService::new_with_retry(config, custom_retry).is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_file_extension_matching() {
|
|
let supported_extensions = vec!["pdf", "png", "jpg", "jpeg", "tiff", "bmp", "txt"];
|
|
|
|
let test_cases = vec![
|
|
("document.pdf", true),
|
|
("image.PNG", true), // Case insensitive
|
|
("photo.jpg", true),
|
|
("photo.JPEG", true),
|
|
("scan.tiff", true),
|
|
("bitmap.bmp", true),
|
|
("readme.txt", true),
|
|
("spreadsheet.xlsx", false),
|
|
("presentation.pptx", false),
|
|
("archive.zip", false),
|
|
("script.sh", false),
|
|
("no_extension", false),
|
|
(".hidden", false),
|
|
];
|
|
|
|
for (filename, should_match) in test_cases {
|
|
let extension = std::path::Path::new(filename)
|
|
.extension()
|
|
.and_then(|ext| ext.to_str())
|
|
.map(|ext| ext.to_lowercase());
|
|
|
|
let matches = extension
|
|
.as_ref()
|
|
.map(|ext| supported_extensions.contains(&ext.as_str()))
|
|
.unwrap_or(false);
|
|
|
|
assert_eq!(matches, should_match,
|
|
"File '{}' extension matching failed. Expected: {}, Got: {}",
|
|
filename, should_match, matches);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_webdav_sync_state_model() {
|
|
let sync_state = WebDAVSyncState {
|
|
id: Uuid::new_v4(),
|
|
user_id: Uuid::new_v4(),
|
|
last_sync_at: Some(Utc::now()),
|
|
sync_cursor: Some("cursor123".to_string()),
|
|
is_running: true,
|
|
files_processed: 42,
|
|
files_remaining: 58,
|
|
current_folder: Some("/Documents".to_string()),
|
|
errors: vec!["Error 1".to_string(), "Error 2".to_string()],
|
|
created_at: Utc::now(),
|
|
updated_at: Utc::now(),
|
|
};
|
|
|
|
assert!(sync_state.is_running);
|
|
assert_eq!(sync_state.files_processed, 42);
|
|
assert_eq!(sync_state.files_remaining, 58);
|
|
assert_eq!(sync_state.current_folder, Some("/Documents".to_string()));
|
|
assert_eq!(sync_state.errors.len(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_webdav_file_model() {
|
|
let document_id = Uuid::new_v4();
|
|
let webdav_file = WebDAVFile {
|
|
id: Uuid::new_v4(),
|
|
user_id: Uuid::new_v4(),
|
|
webdav_path: "/Documents/report.pdf".to_string(),
|
|
etag: "\"abc123\"".to_string(),
|
|
last_modified: Some(Utc::now()),
|
|
file_size: 2048000,
|
|
mime_type: "application/pdf".to_string(),
|
|
document_id: Some(document_id),
|
|
sync_status: "completed".to_string(),
|
|
sync_error: None,
|
|
created_at: Utc::now(),
|
|
updated_at: Utc::now(),
|
|
};
|
|
|
|
assert_eq!(webdav_file.webdav_path, "/Documents/report.pdf");
|
|
assert_eq!(webdav_file.etag, "\"abc123\"");
|
|
assert_eq!(webdav_file.file_size, 2048000);
|
|
assert_eq!(webdav_file.sync_status, "completed");
|
|
assert!(webdav_file.sync_error.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn test_create_webdav_file_model() {
|
|
let user_id = Uuid::new_v4();
|
|
let create_file = CreateWebDAVFile {
|
|
user_id,
|
|
webdav_path: "/Photos/vacation.jpg".to_string(),
|
|
etag: "\"photo123\"".to_string(),
|
|
last_modified: Some(Utc::now()),
|
|
file_size: 1536000,
|
|
mime_type: "image/jpeg".to_string(),
|
|
document_id: None,
|
|
sync_status: "pending".to_string(),
|
|
sync_error: None,
|
|
};
|
|
|
|
assert_eq!(create_file.user_id, user_id);
|
|
assert_eq!(create_file.webdav_path, "/Photos/vacation.jpg");
|
|
assert_eq!(create_file.file_size, 1536000);
|
|
assert_eq!(create_file.sync_status, "pending");
|
|
}
|
|
|
|
#[test]
|
|
fn test_update_webdav_sync_state_model() {
|
|
let update_state = UpdateWebDAVSyncState {
|
|
last_sync_at: Some(Utc::now()),
|
|
sync_cursor: Some("new_cursor".to_string()),
|
|
is_running: false,
|
|
files_processed: 100,
|
|
files_remaining: 0,
|
|
current_folder: None,
|
|
errors: Vec::new(),
|
|
};
|
|
|
|
assert!(!update_state.is_running);
|
|
assert_eq!(update_state.files_processed, 100);
|
|
assert_eq!(update_state.files_remaining, 0);
|
|
assert!(update_state.current_folder.is_none());
|
|
assert!(update_state.errors.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_ocr_priority_calculation_comprehensive() {
|
|
let test_cases = vec![
|
|
// Size boundaries
|
|
(0, 10), // 0 bytes
|
|
(1, 10), // 1 byte
|
|
(1048576, 10), // Exactly 1MB
|
|
(1048577, 8), // 1MB + 1 byte
|
|
(5242880, 8), // Exactly 5MB
|
|
(5242881, 6), // 5MB + 1 byte
|
|
(10485760, 6), // Exactly 10MB
|
|
(10485761, 4), // 10MB + 1 byte
|
|
(52428800, 4), // Exactly 50MB
|
|
(52428801, 2), // 50MB + 1 byte
|
|
(104857600, 2), // 100MB
|
|
(1073741824, 2), // 1GB
|
|
];
|
|
|
|
for (file_size, expected_priority) in test_cases {
|
|
let priority = match file_size {
|
|
0..=1048576 => 10, // <= 1MB
|
|
..=5242880 => 8, // 1-5MB
|
|
..=10485760 => 6, // 5-10MB
|
|
..=52428800 => 4, // 10-50MB
|
|
_ => 2, // > 50MB
|
|
};
|
|
|
|
assert_eq!(priority, expected_priority,
|
|
"Priority calculation failed for file size {} bytes", file_size);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_sync_status_serialization() {
|
|
let sync_status = WebDAVSyncStatus {
|
|
is_running: true,
|
|
last_sync: Some(Utc::now()),
|
|
files_processed: 25,
|
|
files_remaining: 75,
|
|
current_folder: Some("/Documents/Reports".to_string()),
|
|
errors: vec!["Connection timeout".to_string()],
|
|
};
|
|
|
|
// Test that the status can be serialized to JSON
|
|
let json = serde_json::to_string(&sync_status);
|
|
assert!(json.is_ok());
|
|
|
|
let json_str = json.unwrap();
|
|
assert!(json_str.contains("\"is_running\":true"));
|
|
assert!(json_str.contains("\"files_processed\":25"));
|
|
assert!(json_str.contains("\"files_remaining\":75"));
|
|
assert!(json_str.contains("\"current_folder\":\"/Documents/Reports\""));
|
|
}
|
|
|
|
#[test]
|
|
fn test_crawl_estimate_calculation() {
|
|
let folder1 = WebDAVFolderInfo {
|
|
path: "/Documents".to_string(),
|
|
total_files: 100,
|
|
supported_files: 80,
|
|
estimated_time_hours: 0.044, // ~2.6 minutes
|
|
total_size_mb: 150.0,
|
|
};
|
|
|
|
let folder2 = WebDAVFolderInfo {
|
|
path: "/Photos".to_string(),
|
|
total_files: 200,
|
|
supported_files: 150,
|
|
estimated_time_hours: 0.083, // ~5 minutes
|
|
total_size_mb: 500.0,
|
|
};
|
|
|
|
let estimate = WebDAVCrawlEstimate {
|
|
folders: vec![folder1, folder2],
|
|
total_files: 300,
|
|
total_supported_files: 230,
|
|
total_estimated_time_hours: 0.127, // ~7.6 minutes
|
|
total_size_mb: 650.0,
|
|
};
|
|
|
|
assert_eq!(estimate.folders.len(), 2);
|
|
assert_eq!(estimate.total_files, 300);
|
|
assert_eq!(estimate.total_supported_files, 230);
|
|
assert!((estimate.total_estimated_time_hours - 0.127).abs() < 0.001);
|
|
assert_eq!(estimate.total_size_mb, 650.0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_connection_result_variants() {
|
|
// Success case
|
|
let success_result = WebDAVConnectionResult {
|
|
success: true,
|
|
message: "Connected successfully to Nextcloud 28.0.1".to_string(),
|
|
server_version: Some("28.0.1".to_string()),
|
|
server_type: Some("nextcloud".to_string()),
|
|
};
|
|
|
|
assert!(success_result.success);
|
|
assert!(success_result.server_version.is_some());
|
|
assert_eq!(success_result.server_type, Some("nextcloud".to_string()));
|
|
|
|
// Failure case
|
|
let failure_result = WebDAVConnectionResult {
|
|
success: false,
|
|
message: "Authentication failed: 401 Unauthorized".to_string(),
|
|
server_version: None,
|
|
server_type: None,
|
|
};
|
|
|
|
assert!(!failure_result.success);
|
|
assert!(failure_result.server_version.is_none());
|
|
assert!(failure_result.server_type.is_none());
|
|
assert!(failure_result.message.contains("401"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_notification_creation_for_webdav() {
|
|
let notification = CreateNotification {
|
|
notification_type: "info".to_string(),
|
|
title: "WebDAV Sync Started".to_string(),
|
|
message: "Synchronizing files from Nextcloud server".to_string(),
|
|
action_url: Some("/sync-status".to_string()),
|
|
metadata: Some(serde_json::json!({
|
|
"sync_type": "webdav",
|
|
"folders": ["/Documents", "/Photos"],
|
|
"estimated_files": 150
|
|
})),
|
|
};
|
|
|
|
assert_eq!(notification.notification_type, "info");
|
|
assert_eq!(notification.title, "WebDAV Sync Started");
|
|
assert!(notification.action_url.is_some());
|
|
|
|
let metadata = notification.metadata.unwrap();
|
|
assert_eq!(metadata["sync_type"], "webdav");
|
|
assert!(metadata["folders"].is_array());
|
|
assert_eq!(metadata["estimated_files"], 150);
|
|
}
|
|
|
|
#[test]
|
|
fn test_special_characters_in_paths() {
|
|
let test_paths = vec![
|
|
"/Documents/File with spaces.pdf",
|
|
"/Documents/Ñoño/archivo.pdf",
|
|
"/Documents/测试文件.pdf",
|
|
"/Documents/файл.pdf",
|
|
"/Documents/50%.pdf",
|
|
"/Documents/file&name.pdf",
|
|
"/Documents/file#1.pdf",
|
|
];
|
|
|
|
for path in test_paths {
|
|
let file_info = FileIngestionInfo {
|
|
relative_path: path.to_string(),
|
|
full_path: path.to_string(),
|
|
#[allow(deprecated)]
|
|
path: path.to_string(),
|
|
name: std::path::Path::new(path)
|
|
.file_name()
|
|
.unwrap()
|
|
.to_str()
|
|
.unwrap()
|
|
.to_string(),
|
|
size: 1024,
|
|
mime_type: "application/pdf".to_string(),
|
|
last_modified: Some(Utc::now()),
|
|
etag: "\"test123\"".to_string(),
|
|
is_directory: false,
|
|
created_at: None,
|
|
permissions: None,
|
|
owner: None,
|
|
group: None,
|
|
metadata: None,
|
|
};
|
|
|
|
assert!(!file_info.name.is_empty());
|
|
assert!(file_info.name.ends_with(".pdf"));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_backoff_delay_calculation() {
|
|
let retry_config = RetryConfig::default();
|
|
|
|
let mut delays = Vec::new();
|
|
let mut delay = retry_config.initial_delay_ms;
|
|
|
|
for _ in 0..5 {
|
|
delays.push(delay);
|
|
delay = ((delay as f64 * retry_config.backoff_multiplier) as u64)
|
|
.min(retry_config.max_delay_ms);
|
|
}
|
|
|
|
assert_eq!(delays[0], 1000); // 1s
|
|
assert_eq!(delays[1], 2000); // 2s
|
|
assert_eq!(delays[2], 4000); // 4s
|
|
assert_eq!(delays[3], 8000); // 8s
|
|
assert_eq!(delays[4], 10000); // 10s (capped at max_delay_ms)
|
|
|
|
// Verify max delay is respected
|
|
for _ in 0..10 {
|
|
delay = ((delay as f64 * retry_config.backoff_multiplier) as u64)
|
|
.min(retry_config.max_delay_ms);
|
|
}
|
|
assert_eq!(delay, retry_config.max_delay_ms);
|
|
} |