feat(tests): mom, take a picture, the tests pass

This commit is contained in:
perf3ct 2025-07-29 02:28:39 +00:00
parent 21c5bbcc47
commit 23c9d68c0b
No known key found for this signature in database
GPG Key ID: 569C4EEC436F5232
5 changed files with 114 additions and 37 deletions

View File

@ -241,7 +241,13 @@ impl TestContext {
let mut retries = 0; let mut retries = 0;
const MAX_RETRIES: u32 = 15; const MAX_RETRIES: u32 = 15;
let db = loop { let db = loop {
match crate::db::Database::new_with_pool_config(&database_url, 5, 1).await { // Use larger pool for error handling tests that need more concurrent connections
let (max_connections, min_connections) = if std::env::var("TEST_REQUIRES_LARGER_POOL").is_ok() {
(15, 3) // Larger pool for error handling tests
} else {
(5, 1) // Standard small pool for regular tests
};
match crate::db::Database::new_with_pool_config(&database_url, max_connections, min_connections).await {
Ok(test_db) => { Ok(test_db) => {
// Run migrations // Run migrations
let migrations = sqlx::migrate!("./migrations"); let migrations = sqlx::migrate!("./migrations");

View File

@ -1,14 +1,24 @@
use std::sync::Arc;
use readur::{ use readur::{
AppState,
models::{CreateWebDAVDirectory, User, AuthProvider}, models::{CreateWebDAVDirectory, User, AuthProvider},
services::webdav::{SmartSyncService, SmartSyncStrategy, SmartSyncDecision, WebDAVService, WebDAVConfig}, services::webdav::{SmartSyncService, SmartSyncStrategy, SmartSyncDecision, WebDAVService, WebDAVConfig},
test_utils::{TestContext, TestAuthHelper}, test_utils::{TestContext, TestAuthHelper},
}; };
/// Helper function to create test database and user /// Helper function to create test database and user with enhanced pool configuration
async fn create_test_setup() -> (Arc<AppState>, User) { async fn create_test_setup() -> (TestContext, User) {
let test_context = TestContext::new().await; // Set environment variable to use larger database pool for error handling tests
std::env::set_var("TEST_REQUIRES_LARGER_POOL", "1");
// Create TestContext with larger connection pool for error handling tests
let config_builder = readur::test_utils::TestConfigBuilder::default()
.with_concurrent_ocr_jobs(2); // Reduce concurrent jobs to prevent pool exhaustion
let test_context = TestContext::with_config(config_builder).await;
// Wait for pool to be ready before proceeding
if let Err(e) = test_context.wait_for_pool_health(10).await {
eprintln!("Warning: Pool health check failed: {}", e);
}
let auth_helper = TestAuthHelper::new(test_context.app().clone()); let auth_helper = TestAuthHelper::new(test_context.app().clone());
let test_user = auth_helper.create_test_user().await; let test_user = auth_helper.create_test_user().await;
@ -27,7 +37,7 @@ async fn create_test_setup() -> (Arc<AppState>, User) {
auth_provider: AuthProvider::Local, auth_provider: AuthProvider::Local,
}; };
(test_context.state().clone(), user) (test_context, user)
} }
/// Helper function to create WebDAV service for testing /// Helper function to create WebDAV service for testing
@ -50,7 +60,8 @@ async fn test_webdav_error_fallback() {
// Integration Test: WebDAV server error scenarios should fall back to traditional sync // Integration Test: WebDAV server error scenarios should fall back to traditional sync
// Expected: When WebDAV service fails, should gracefully handle errors // Expected: When WebDAV service fails, should gracefully handle errors
let (state, user) = create_test_setup().await; let (test_context, user) = create_test_setup().await;
let state = test_context.state().clone();
let smart_sync_service = SmartSyncService::new(state.clone()); let smart_sync_service = SmartSyncService::new(state.clone());
// Create some existing directories to test database robustness // Create some existing directories to test database robustness
@ -117,6 +128,11 @@ async fn test_webdav_error_fallback() {
assert_eq!(root_dir.directory_etag, "existing-root"); assert_eq!(root_dir.directory_etag, "existing-root");
println!("✅ WebDAV error fallback test completed - database remains intact"); println!("✅ WebDAV error fallback test completed - database remains intact");
// Clean up test context
if let Err(e) = test_context.cleanup_and_close().await {
eprintln!("Warning: Test cleanup failed: {}", e);
}
} }
#[tokio::test] #[tokio::test]
@ -124,7 +140,8 @@ async fn test_database_error_handling() {
// Integration Test: Database errors should be handled gracefully // Integration Test: Database errors should be handled gracefully
// This tests the system's resilience to database connectivity issues // This tests the system's resilience to database connectivity issues
let (state, user) = create_test_setup().await; let (test_context, user) = create_test_setup().await;
let state = test_context.state().clone();
let smart_sync_service = SmartSyncService::new(state.clone()); let smart_sync_service = SmartSyncService::new(state.clone());
// Test with invalid user ID (simulates database query errors) // Test with invalid user ID (simulates database query errors)
@ -163,6 +180,11 @@ async fn test_database_error_handling() {
assert_eq!(saved_dirs.len(), 1, "Normal database operations should work after error handling"); assert_eq!(saved_dirs.len(), 1, "Normal database operations should work after error handling");
println!("✅ Database error handling test completed"); println!("✅ Database error handling test completed");
// Clean up test context
if let Err(e) = test_context.cleanup_and_close().await {
eprintln!("Warning: Test cleanup failed: {}", e);
}
} }
#[tokio::test] #[tokio::test]
@ -170,7 +192,8 @@ async fn test_concurrent_smart_sync_operations() {
// Integration Test: Concurrent smart sync operations should not interfere with each other // Integration Test: Concurrent smart sync operations should not interfere with each other
// This tests race conditions and database locking // This tests race conditions and database locking
let (state, user) = create_test_setup().await; let (test_context, user) = create_test_setup().await;
let state = test_context.state().clone();
// Create initial directories // Create initial directories
let initial_dirs = vec![ let initial_dirs = vec![
@ -195,8 +218,8 @@ async fn test_concurrent_smart_sync_operations() {
.expect("Failed to create initial directory"); .expect("Failed to create initial directory");
} }
// Run multiple concurrent operations // Run multiple concurrent operations (reduced from 5 to 3 to prevent pool exhaustion)
let num_concurrent = 5; let num_concurrent = 3;
let mut handles = Vec::new(); let mut handles = Vec::new();
for i in 0..num_concurrent { for i in 0..num_concurrent {
@ -261,6 +284,11 @@ async fn test_concurrent_smart_sync_operations() {
println!(" {} initial directories preserved", initial_dirs.len()); println!(" {} initial directories preserved", initial_dirs.len());
println!(" {} concurrent operations executed", num_concurrent); println!(" {} concurrent operations executed", num_concurrent);
println!(" {} operations successful", success_count); println!(" {} operations successful", success_count);
// Clean up test context
if let Err(e) = test_context.cleanup_and_close().await {
eprintln!("Warning: Test cleanup failed: {}", e);
}
} }
#[tokio::test] #[tokio::test]
@ -268,7 +296,8 @@ async fn test_malformed_data_recovery() {
// Integration Test: System should handle and recover from malformed data gracefully // Integration Test: System should handle and recover from malformed data gracefully
// This tests robustness against data corruption scenarios // This tests robustness against data corruption scenarios
let (state, user) = create_test_setup().await; let (test_context, user) = create_test_setup().await;
let state = test_context.state().clone();
// Create a directory with normal data first // Create a directory with normal data first
let normal_dir = CreateWebDAVDirectory { let normal_dir = CreateWebDAVDirectory {
@ -348,4 +377,9 @@ async fn test_malformed_data_recovery() {
println!(" {} edge cases handled successfully", successful_edge_cases); println!(" {} edge cases handled successfully", successful_edge_cases);
println!(" {} edge cases failed as expected", failed_edge_cases); println!(" {} edge cases failed as expected", failed_edge_cases);
println!(" Database remains functional after edge case testing"); println!(" Database remains functional after edge case testing");
// Clean up test context
if let Err(e) = test_context.cleanup_and_close().await {
eprintln!("Warning: Test cleanup failed: {}", e);
}
} }

View File

@ -1,13 +1,11 @@
use std::sync::Arc;
use readur::{ use readur::{
AppState,
models::{CreateWebDAVDirectory, User, AuthProvider}, models::{CreateWebDAVDirectory, User, AuthProvider},
services::webdav::{SmartSyncService, SmartSyncStrategy, SmartSyncDecision, WebDAVService, WebDAVConfig}, services::webdav::{SmartSyncService, SmartSyncStrategy, SmartSyncDecision, WebDAVService, WebDAVConfig},
test_utils::{TestContext, TestAuthHelper}, test_utils::{TestContext, TestAuthHelper},
}; };
/// Helper function to create test database and user /// Helper function to create test database and user with automatic cleanup
async fn create_test_setup() -> (Arc<AppState>, User) { async fn create_test_setup() -> (TestContext, User) {
let test_context = TestContext::new().await; let test_context = TestContext::new().await;
let auth_helper = TestAuthHelper::new(test_context.app().clone()); let auth_helper = TestAuthHelper::new(test_context.app().clone());
let test_user = auth_helper.create_test_user().await; let test_user = auth_helper.create_test_user().await;
@ -27,7 +25,7 @@ async fn create_test_setup() -> (Arc<AppState>, User) {
auth_provider: AuthProvider::Local, auth_provider: AuthProvider::Local,
}; };
(test_context.state().clone(), user) (test_context, user)
} }
/// Helper function to create WebDAV service for testing /// Helper function to create WebDAV service for testing
@ -50,7 +48,8 @@ async fn test_first_time_sync_full_deep_scan() {
// Integration Test: First-time sync with no existing directory ETags // Integration Test: First-time sync with no existing directory ETags
// Expected: Should perform full deep scan and save all discovered directory ETags // Expected: Should perform full deep scan and save all discovered directory ETags
let (state, user) = create_test_setup().await; let (test_context, user) = create_test_setup().await;
let state = test_context.state().clone();
let smart_sync_service = SmartSyncService::new(state.clone()); let smart_sync_service = SmartSyncService::new(state.clone());
// Verify no existing directories tracked // Verify no existing directories tracked
@ -74,6 +73,11 @@ async fn test_first_time_sync_full_deep_scan() {
} }
println!("✅ First-time sync test completed successfully"); println!("✅ First-time sync test completed successfully");
// Clean up test context
if let Err(e) = test_context.cleanup_and_close().await {
eprintln!("Warning: Test cleanup failed: {}", e);
}
} }
#[tokio::test] #[tokio::test]
@ -81,7 +85,8 @@ async fn test_first_time_sync_saves_directory_etags() {
// Integration Test: First-time sync should save discovered directory ETags to database // Integration Test: First-time sync should save discovered directory ETags to database
// This test focuses on the database persistence aspect // This test focuses on the database persistence aspect
let (state, user) = create_test_setup().await; let (test_context, user) = create_test_setup().await;
let state = test_context.state().clone();
// Manually create directories that would be discovered by WebDAV // Manually create directories that would be discovered by WebDAV
let discovered_directories = vec![ let discovered_directories = vec![
@ -135,4 +140,9 @@ async fn test_first_time_sync_saves_directory_etags() {
assert_eq!(archive_dir.total_size_bytes, 2048000); assert_eq!(archive_dir.total_size_bytes, 2048000);
println!("✅ First-time sync directory ETag persistence test completed successfully"); println!("✅ First-time sync directory ETag persistence test completed successfully");
// Clean up test context
if let Err(e) = test_context.cleanup_and_close().await {
eprintln!("Warning: Test cleanup failed: {}", e);
}
} }

View File

@ -1,13 +1,11 @@
use std::sync::Arc;
use readur::{ use readur::{
AppState,
models::{CreateWebDAVDirectory, User, AuthProvider}, models::{CreateWebDAVDirectory, User, AuthProvider},
services::webdav::{SmartSyncService, SmartSyncStrategy, SmartSyncDecision, WebDAVService, WebDAVConfig}, services::webdav::{SmartSyncService, WebDAVService, WebDAVConfig},
test_utils::{TestContext, TestAuthHelper}, test_utils::{TestContext, TestAuthHelper},
}; };
/// Helper function to create test database and user /// Helper function to create test database and user with automatic cleanup
async fn create_test_setup() -> (Arc<AppState>, User) { async fn create_test_setup() -> (TestContext, User) {
let test_context = TestContext::new().await; let test_context = TestContext::new().await;
let auth_helper = TestAuthHelper::new(test_context.app().clone()); let auth_helper = TestAuthHelper::new(test_context.app().clone());
let test_user = auth_helper.create_test_user().await; let test_user = auth_helper.create_test_user().await;
@ -27,7 +25,7 @@ async fn create_test_setup() -> (Arc<AppState>, User) {
auth_provider: AuthProvider::Local, auth_provider: AuthProvider::Local,
}; };
(test_context.state().clone(), user) (test_context, user)
} }
/// Helper function to create WebDAV service for testing /// Helper function to create WebDAV service for testing
@ -50,7 +48,8 @@ async fn test_smart_sync_no_changes_skip() {
// Integration Test: Smart sync with no directory changes should skip sync entirely // Integration Test: Smart sync with no directory changes should skip sync entirely
// Expected: Should return SkipSync when all directory ETags are unchanged // Expected: Should return SkipSync when all directory ETags are unchanged
let (state, user) = create_test_setup().await; let (test_context, user) = create_test_setup().await;
let state = test_context.state().clone();
let smart_sync_service = SmartSyncService::new(state.clone()); let smart_sync_service = SmartSyncService::new(state.clone());
// Pre-populate database with known directory ETags // Pre-populate database with known directory ETags
@ -112,6 +111,11 @@ async fn test_smart_sync_no_changes_skip() {
assert_eq!(archive_dir.file_count, 25); assert_eq!(archive_dir.file_count, 25);
println!("✅ No changes sync test completed successfully - bulk fetch in {:?}", fetch_duration); println!("✅ No changes sync test completed successfully - bulk fetch in {:?}", fetch_duration);
// Clean up test context
if let Err(e) = test_context.cleanup_and_close().await {
eprintln!("Warning: Test cleanup failed: {}", e);
}
} }
#[tokio::test] #[tokio::test]
@ -119,7 +123,8 @@ async fn test_directory_etag_comparison_efficiency() {
// Integration Test: Directory ETag comparison should be efficient for large numbers of directories // Integration Test: Directory ETag comparison should be efficient for large numbers of directories
// This tests the bulk fetching performance optimization // This tests the bulk fetching performance optimization
let (state, user) = create_test_setup().await; let (test_context, user) = create_test_setup().await;
let state = test_context.state().clone();
// Create a larger number of directories to test performance // Create a larger number of directories to test performance
let num_directories = 100; let num_directories = 100;
@ -169,4 +174,9 @@ async fn test_directory_etag_comparison_efficiency() {
println!("✅ Directory ETag comparison efficiency test completed successfully"); println!("✅ Directory ETag comparison efficiency test completed successfully");
println!(" Created {} directories in {:?}", num_directories, insert_duration); println!(" Created {} directories in {:?}", num_directories, insert_duration);
println!(" Fetched {} directories in {:?}", num_directories, fetch_duration); println!(" Fetched {} directories in {:?}", num_directories, fetch_duration);
// Clean up test context
if let Err(e) = test_context.cleanup_and_close().await {
eprintln!("Warning: Test cleanup failed: {}", e);
}
} }

View File

@ -1,13 +1,12 @@
use std::sync::Arc;
use readur::{ use readur::{
AppState,
models::{CreateWebDAVDirectory, User, AuthProvider}, models::{CreateWebDAVDirectory, User, AuthProvider},
services::webdav::{SmartSyncService, SmartSyncStrategy, SmartSyncDecision, WebDAVService, WebDAVConfig}, services::webdav::{WebDAVService, WebDAVConfig},
test_utils::{TestContext, TestAuthHelper}, test_utils::{TestContext, TestAuthHelper},
}; };
use std::collections::HashMap;
/// Helper function to create test database and user /// Helper function to create test database and user with automatic cleanup
async fn create_test_setup() -> (Arc<AppState>, User) { async fn create_test_setup() -> (TestContext, User) {
let test_context = TestContext::new().await; let test_context = TestContext::new().await;
let auth_helper = TestAuthHelper::new(test_context.app().clone()); let auth_helper = TestAuthHelper::new(test_context.app().clone());
let test_user = auth_helper.create_test_user().await; let test_user = auth_helper.create_test_user().await;
@ -27,7 +26,7 @@ async fn create_test_setup() -> (Arc<AppState>, User) {
auth_provider: AuthProvider::Local, auth_provider: AuthProvider::Local,
}; };
(test_context.state().clone(), user) (test_context, user)
} }
/// Helper function to create WebDAV service for testing /// Helper function to create WebDAV service for testing
@ -50,7 +49,8 @@ async fn test_smart_sync_targeted_scan() {
// Integration Test: Smart sync with single directory changed should use targeted scan // Integration Test: Smart sync with single directory changed should use targeted scan
// Expected: Should return RequiresSync(TargetedScan) when only a few directories have changed // Expected: Should return RequiresSync(TargetedScan) when only a few directories have changed
let (state, user) = create_test_setup().await; let (test_context, user) = create_test_setup().await;
let state = test_context.state().clone();
// Create a scenario with many directories, where only one has changed // Create a scenario with many directories, where only one has changed
let unchanged_directories = vec![ let unchanged_directories = vec![
@ -93,6 +93,11 @@ async fn test_smart_sync_targeted_scan() {
assert!(should_use_targeted, "Should use targeted scan for small changes: {:.1}% change ratio", change_ratio * 100.0); assert!(should_use_targeted, "Should use targeted scan for small changes: {:.1}% change ratio", change_ratio * 100.0);
println!("✅ Targeted scan strategy selection test passed - 10% change triggers targeted scan"); println!("✅ Targeted scan strategy selection test passed - 10% change triggers targeted scan");
// Clean up test context
if let Err(e) = test_context.cleanup_and_close().await {
eprintln!("Warning: Test cleanup failed: {}", e);
}
} }
#[tokio::test] #[tokio::test]
@ -100,7 +105,8 @@ async fn test_targeted_scan_vs_full_scan_thresholds() {
// Integration Test: Test various scenarios for when to use targeted vs full scan // Integration Test: Test various scenarios for when to use targeted vs full scan
// Expected: Strategy should be chosen based on change ratio and new directory count // Expected: Strategy should be chosen based on change ratio and new directory count
let (state, user) = create_test_setup().await; let (test_context, user) = create_test_setup().await;
let state = test_context.state().clone();
// Create base directories for testing different scenarios // Create base directories for testing different scenarios
let base_directories = 20; // Start with 20 directories let base_directories = 20; // Start with 20 directories
@ -150,6 +156,11 @@ async fn test_targeted_scan_vs_full_scan_thresholds() {
println!(" Scenario 2 (40% changes, 2 new): Full scan"); println!(" Scenario 2 (40% changes, 2 new): Full scan");
println!(" Scenario 3 (5% changes, 7 new): Full scan"); println!(" Scenario 3 (5% changes, 7 new): Full scan");
println!(" Scenario 4 (30% changes, 5 new): Targeted scan"); println!(" Scenario 4 (30% changes, 5 new): Targeted scan");
// Clean up test context
if let Err(e) = test_context.cleanup_and_close().await {
eprintln!("Warning: Test cleanup failed: {}", e);
}
} }
#[tokio::test] #[tokio::test]
@ -157,7 +168,8 @@ async fn test_directory_change_detection_logic() {
// Integration Test: Test the logic for detecting changed, new, and unchanged directories // Integration Test: Test the logic for detecting changed, new, and unchanged directories
// This is the core of the targeted scan decision making // This is the core of the targeted scan decision making
let (state, user) = create_test_setup().await; let (test_context, user) = create_test_setup().await;
let state = test_context.state().clone();
// Set up known directories in database // Set up known directories in database
let known_dirs = vec![ let known_dirs = vec![
@ -256,4 +268,9 @@ async fn test_directory_change_detection_logic() {
println!(" Deleted: {} directories", deleted_directories.len()); println!(" Deleted: {} directories", deleted_directories.len());
println!(" Change ratio: {:.1}%", change_ratio * 100.0); println!(" Change ratio: {:.1}%", change_ratio * 100.0);
println!(" Strategy: {}", if should_use_targeted { "Targeted scan" } else { "Full scan" }); println!(" Strategy: {}", if should_use_targeted { "Targeted scan" } else { "Full scan" });
// Clean up test context
if let Err(e) = test_context.cleanup_and_close().await {
eprintln!("Warning: Test cleanup failed: {}", e);
}
} }