//! Integration tests for volume tracking functionality use sd_core::{ infra::action::manager::ActionManager, ops::volumes::{ speed_test::action::{VolumeSpeedTestAction, VolumeSpeedTestInput}, track::action::{VolumeTrackAction, VolumeTrackInput}, untrack::action::{VolumeUntrackAction, VolumeUntrackInput}, }, volume::types::MountType, Core, }; use std::sync::Arc; use tempfile::tempdir; use tracing::info; #[tokio::test] async fn test_volume_tracking_lifecycle() { // Initialize logging let _ = tracing_subscriber::fmt::try_init(); // Setup test environment let data_dir = tempdir().unwrap(); let data_path = data_dir.path().to_path_buf(); // Initialize core - this handles all the setup automatically let core = Arc::new( Core::new(data_path.clone()) .await .expect("Failed to create core"), ); // Create a test library let library = core .libraries .create_library( "Test Library", Some(data_path.join("libraries").join("test-library")), core.context.clone(), ) .await .expect("Failed to create library"); let library_id = library.id(); info!("Created test library: {}", library_id); // Get volume manager let volume_manager = core.volumes.clone(); // Refresh volumes to ensure we have the latest volume_manager .refresh_volumes() .await .expect("Failed to refresh volumes"); // Get all volumes let all_volumes = volume_manager.get_all_volumes().await; info!("Detected {} volumes", all_volumes.len()); // Get first available volume for testing let test_volume = all_volumes .first() .expect("No volumes available for testing") .clone(); info!("Using volume '{}' for testing", test_volume.name); let fingerprint = test_volume.fingerprint.clone(); // Get action manager from core context let action_manager = core .context .get_action_manager() .await .expect("Action manager should be initialized"); // Test 1: Check if volume is already tracked (from auto-tracking) info!("Checking initial tracking status..."); let initial_tracked = volume_manager .is_volume_tracked(&library, &fingerprint) .await .expect("Failed to check tracking status"); if initial_tracked { info!("Volume is already tracked (from auto-tracking), untracking first"); // Untrack it first so we can test tracking let untrack_action = VolumeUntrackAction::new(VolumeUntrackInput { fingerprint: fingerprint.clone(), }); let result = action_manager .dispatch_library(Some(library_id), untrack_action) .await; assert!(result.is_ok(), "Failed to untrack volume: {:?}", result); } // Test 1: Track volume info!("Testing volume tracking..."); { let track_action = VolumeTrackAction::new(VolumeTrackInput { fingerprint: fingerprint.clone(), name: Some("My Test Volume".to_string()), }); let result = action_manager .dispatch_library(Some(library_id), track_action) .await; assert!(result.is_ok(), "Failed to track volume: {:?}", result); if result.is_ok() { info!("Volume tracked successfully"); } // Verify volume is tracked let is_tracked = volume_manager .is_volume_tracked(&library, &fingerprint) .await .expect("Failed to check tracking status"); assert!(is_tracked, "Volume should be tracked"); // Get tracked volumes let tracked_volumes = volume_manager .get_tracked_volumes(&library) .await .expect("Failed to get tracked volumes"); // Find our specific volume (there might be others from auto-tracking) let our_volume = tracked_volumes .iter() .find(|v| v.fingerprint == fingerprint) .expect("Our volume should be in tracked volumes"); assert_eq!(our_volume.display_name, Some("My Test Volume".to_string())); } // Test 2: Try to track same volume again (should fail) info!("Testing duplicate tracking prevention..."); { let track_action = VolumeTrackAction::new(VolumeTrackInput { fingerprint: fingerprint.clone(), name: Some("Another Name".to_string()), }); let result = action_manager .dispatch_library(Some(library_id), track_action) .await; assert!(result.is_err(), "Should not be able to track volume twice"); info!("Duplicate tracking correctly prevented"); } // Test 3: Untrack volume info!("Testing volume untracking..."); { let untrack_action = VolumeUntrackAction::new(VolumeUntrackInput { fingerprint: fingerprint.clone(), }); let result = action_manager .dispatch_library(Some(library_id), untrack_action) .await; assert!(result.is_ok(), "Failed to untrack volume: {:?}", result); if result.is_ok() { info!("Volume untracked successfully"); } // Verify volume is no longer tracked let is_tracked = volume_manager .is_volume_tracked(&library, &fingerprint) .await .expect("Failed to check tracking status"); assert!(!is_tracked, "Volume should not be tracked"); // Get tracked volumes and verify our volume is not there let tracked_volumes = volume_manager .get_tracked_volumes(&library) .await .expect("Failed to get tracked volumes"); let our_volume_still_tracked = tracked_volumes.iter().any(|v| v.fingerprint == fingerprint); assert!( !our_volume_still_tracked, "Our volume should no longer be tracked" ); } // Test 4: Try to untrack volume that's not tracked (should fail) info!("Testing untrack of non-tracked volume..."); { let untrack_action = VolumeUntrackAction::new(VolumeUntrackInput { fingerprint: fingerprint.clone(), }); let result = action_manager .dispatch_library(Some(library_id), untrack_action) .await; assert!( result.is_err(), "Should not be able to untrack non-tracked volume" ); info!("Untrack of non-tracked volume correctly prevented"); } info!("Volume tracking lifecycle test completed successfully"); } #[tokio::test] async fn test_volume_tracking_multiple_libraries() { // Initialize logging let _ = tracing_subscriber::fmt::try_init(); // Setup test environment let data_dir = tempdir().unwrap(); let data_path = data_dir.path().to_path_buf(); // Initialize core - this handles all the setup automatically let core = Arc::new( Core::new(data_path.clone()) .await .expect("Failed to create core"), ); // Create two test libraries let library1 = core .libraries .create_library( "Library 1", Some(data_path.join("libraries").join("library1")), core.context.clone(), ) .await .expect("Failed to create library 1"); let library2 = core .libraries .create_library( "Library 2", Some(data_path.join("libraries").join("library2")), core.context.clone(), ) .await .expect("Failed to create library 2"); let library1_id = library1.id(); let library2_id = library2.id(); info!("Created libraries: {} and {}", library1_id, library2_id); // Get volume manager and refresh let volume_manager = core.volumes.clone(); volume_manager .refresh_volumes() .await .expect("Failed to refresh volumes"); // Get first available volume let test_volume = volume_manager .get_all_volumes() .await .first() .expect("No volumes available for testing") .clone(); let fingerprint = test_volume.fingerprint.clone(); // Get action manager from core context let action_manager = core .context .get_action_manager() .await .expect("Action manager should be initialized"); // Check if volume is already tracked in library 1 (from auto-tracking) let is_tracked_lib1 = volume_manager .is_volume_tracked(&library1, &fingerprint) .await .expect("Failed to check tracking status"); if is_tracked_lib1 { info!("Volume already tracked in library 1, untracking first"); let untrack_action = VolumeUntrackAction::new(VolumeUntrackInput { fingerprint: fingerprint.clone(), }); action_manager .dispatch_library(Some(library1_id), untrack_action) .await .expect("Failed to untrack from library 1"); } // Track volume in library 1 info!("Tracking volume in library 1..."); { let track_action = VolumeTrackAction::new(VolumeTrackInput { fingerprint: fingerprint.clone(), name: Some("Library 1 Volume".to_string()), }); let result = action_manager .dispatch_library(Some(library1_id), track_action) .await; assert!(result.is_ok(), "Failed to track volume in library 1"); } // Check if volume is already tracked in library 2 (from auto-tracking) let is_tracked_lib2 = volume_manager .is_volume_tracked(&library2, &fingerprint) .await .expect("Failed to check tracking status"); if is_tracked_lib2 { info!("Volume already tracked in library 2, untracking first"); let untrack_action = VolumeUntrackAction::new(VolumeUntrackInput { fingerprint: fingerprint.clone(), }); action_manager .dispatch_library(Some(library2_id), untrack_action) .await .expect("Failed to untrack from library 2"); } // Track same volume in library 2 (should succeed) info!("Tracking same volume in library 2..."); { let track_action = VolumeTrackAction::new(VolumeTrackInput { fingerprint: fingerprint.clone(), name: Some("Library 2 Volume".to_string()), }); let result = action_manager .dispatch_library(Some(library2_id), track_action) .await; assert!( result.is_ok(), "Should be able to track volume in different library" ); } // Verify both libraries have the volume tracked let lib1_volumes = volume_manager .get_tracked_volumes(&library1) .await .expect("Failed to get library 1 volumes"); let lib1_our_volume = lib1_volumes .iter() .find(|v| v.fingerprint == fingerprint) .expect("Our volume should be in library 1"); assert_eq!( lib1_our_volume.display_name, Some("Library 1 Volume".to_string()) ); let lib2_volumes = volume_manager .get_tracked_volumes(&library2) .await .expect("Failed to get library 2 volumes"); let lib2_our_volume = lib2_volumes .iter() .find(|v| v.fingerprint == fingerprint) .expect("Our volume should be in library 2"); assert_eq!( lib2_our_volume.display_name, Some("Library 2 Volume".to_string()) ); // Untrack from library 1 info!("Untracking volume from library 1..."); { let untrack_action = VolumeUntrackAction::new(VolumeUntrackInput { fingerprint: fingerprint.clone(), }); let result = action_manager .dispatch_library(Some(library1_id), untrack_action) .await; assert!(result.is_ok(), "Failed to untrack from library 1"); } // Verify library 2 still has it tracked let lib2_volumes = volume_manager .get_tracked_volumes(&library2) .await .expect("Failed to get library 2 volumes"); let lib2_still_has_volume = lib2_volumes.iter().any(|v| v.fingerprint == fingerprint); assert!( lib2_still_has_volume, "Library 2 should still have volume tracked" ); info!("Multiple library volume tracking test completed successfully"); } #[tokio::test] async fn test_automatic_system_volume_tracking() { let _ = tracing_subscriber::fmt::try_init(); let data_dir = tempdir().unwrap(); let data_path = data_dir.path().to_path_buf(); let core = Arc::new( Core::new(data_path.clone()) .await .expect("Failed to create core"), ); // Create library with default settings (auto_track_system_volumes = true) let library = core .libraries .create_library( "Auto Track Test", Some(data_path.join("libraries").join("auto-track")), core.context.clone(), ) .await .expect("Failed to create library"); info!("Created library with auto-tracking enabled"); // Get tracked volumes let tracked_volumes = core .volumes .get_tracked_volumes(&library) .await .expect("Failed to get tracked volumes"); // Get system volumes let system_volumes = core.volumes.get_system_volumes().await; info!( "Found {} system volumes, {} tracked volumes", system_volumes.len(), tracked_volumes.len() ); // Verify all system volumes are tracked for sys_vol in &system_volumes { let is_tracked = tracked_volumes .iter() .any(|tv| tv.fingerprint == sys_vol.fingerprint); assert!( is_tracked, "System volume '{}' should be automatically tracked", sys_vol.name ); } info!("Automatic system volume tracking test completed"); } #[tokio::test] async fn test_auto_tracking_disabled() { let _ = tracing_subscriber::fmt::try_init(); // This test verifies manual control over volume tracking // Since we can't disable auto-tracking via config after creation, // we'll test that we can untrack auto-tracked volumes let data_dir = tempdir().unwrap(); let data_path = data_dir.path().to_path_buf(); let core = Arc::new( Core::new(data_path.clone()) .await .expect("Failed to create core"), ); let library = core .libraries .create_library( "Manual Track Test", Some(data_path.join("libraries").join("manual-track")), core.context.clone(), ) .await .expect("Failed to create library"); // Get auto-tracked system volumes let auto_tracked = core .volumes .get_tracked_volumes(&library) .await .expect("Failed to get tracked volumes"); info!("Found {} auto-tracked volumes", auto_tracked.len()); // Untrack all auto-tracked volumes for volume in &auto_tracked { core.volumes .untrack_volume(&library, &volume.fingerprint) .await .expect("Failed to untrack volume"); } // Verify all volumes are untracked let remaining = core .volumes .get_tracked_volumes(&library) .await .expect("Failed to get tracked volumes"); assert_eq!( remaining.len(), 0, "All volumes should be untracked after manual removal" ); // Now manually track just one non-system volume if available let all_volumes = core.volumes.get_all_volumes().await; if let Some(external_volume) = all_volumes .iter() .find(|v| !matches!(v.mount_type, MountType::System)) { core.volumes .track_volume( &library, &external_volume.fingerprint, Some("Manual Volume".to_string()), ) .await .expect("Failed to manually track volume"); let tracked = core .volumes .get_tracked_volumes(&library) .await .expect("Failed to get tracked volumes"); assert_eq!( tracked.len(), 1, "Should have exactly one manually tracked volume" ); assert_eq!(tracked[0].display_name, Some("Manual Volume".to_string())); } info!("Manual tracking control test completed"); } #[tokio::test] async fn test_volume_state_updates() { let _ = tracing_subscriber::fmt::try_init(); let data_dir = tempdir().unwrap(); let data_path = data_dir.path().to_path_buf(); let core = Arc::new( Core::new(data_path.clone()) .await .expect("Failed to create core"), ); let library = core .libraries .create_library( "State Update Test", Some(data_path.join("libraries").join("state-test")), core.context.clone(), ) .await .expect("Failed to create library"); // Get a volume to track let test_volume = core .volumes .get_all_volumes() .await .first() .cloned() .expect("No volumes available"); let fingerprint = test_volume.fingerprint.clone(); // Track the volume if not already tracked if !core .volumes .is_volume_tracked(&library, &fingerprint) .await .unwrap_or(false) { core.volumes .track_volume( &library, &fingerprint, Some("State Test Volume".to_string()), ) .await .expect("Failed to track volume"); } // Get initial tracked state let initial_tracked = core .volumes .get_tracked_volumes(&library) .await .expect("Failed to get tracked volumes") .into_iter() .find(|v| v.fingerprint == fingerprint) .expect("Volume should be tracked"); info!( "Initial volume state - capacity: {:?}, online: {}", initial_tracked.available_capacity, initial_tracked.is_online ); // Update volume state core.volumes .update_tracked_volume_state(&library, &fingerprint, &test_volume) .await .expect("Failed to update volume state"); // Get updated state let updated_tracked = core .volumes .get_tracked_volumes(&library) .await .expect("Failed to get tracked volumes") .into_iter() .find(|v| v.fingerprint == fingerprint) .expect("Volume should be tracked"); // Verify last_seen_at was updated assert!( updated_tracked.last_seen_at >= initial_tracked.last_seen_at, "last_seen_at should be updated" ); info!("Volume state update test completed"); } #[tokio::test] async fn test_volume_speed_test() { let _ = tracing_subscriber::fmt::try_init(); let data_dir = tempdir().unwrap(); let data_path = data_dir.path().to_path_buf(); let core = Arc::new( Core::new(data_path.clone()) .await .expect("Failed to create core"), ); // Create a library for dispatching library-scoped actions let library = core .libraries .create_library( "Speed Test Library", Some(data_path.join("libraries").join("speed-test")), core.context.clone(), ) .await .expect("Failed to create library"); let library_id = library.id(); // Get first volume for testing let test_volume = core .volumes .get_all_volumes() .await .first() .cloned() .expect("No volumes available"); let fingerprint = test_volume.fingerprint.clone(); info!("Testing speed test on volume '{}'", test_volume.name); // Create speed test action let speed_test_action = VolumeSpeedTestAction::new(VolumeSpeedTestInput { fingerprint: fingerprint.clone(), }); // Get action manager let action_manager = core .context .get_action_manager() .await .expect("Action manager should be initialized"); // Run speed test let result = action_manager .dispatch_library(Some(library_id), speed_test_action) .await; match result { Ok(output) => { let read_speed_mbps = output.read_speed_mbps; let write_speed_mbps = output.write_speed_mbps; info!( "Speed test completed: {:?} MB/s read, {:?} MB/s write", read_speed_mbps, write_speed_mbps ); if let Some(read_speed) = read_speed_mbps { assert!(read_speed > 0, "Read speed should be positive"); } if let Some(write_speed) = write_speed_mbps { assert!(write_speed > 0, "Write speed should be positive"); } } Err(e) => { // Speed test might fail on some volumes (e.g., read-only) info!("Speed test failed (expected for some volumes): {:?}", e); } } info!("Volume speed test completed"); } #[tokio::test] async fn test_volume_types_and_properties() { let _ = tracing_subscriber::fmt::try_init(); let data_dir = tempdir().unwrap(); let data_path = data_dir.path().to_path_buf(); let core = Arc::new( Core::new(data_path.clone()) .await .expect("Failed to create core"), ); // Get all volumes let volumes = core.volumes.get_all_volumes().await; info!("Testing {} volumes for type detection", volumes.len()); // Categorize volumes by type let mut system_count = 0; let mut external_count = 0; let mut network_count = 0; let mut user_count = 0; for volume in &volumes { match volume.mount_type { MountType::System => { system_count += 1; // System volumes should be mounted and have valid paths assert!(volume.is_mounted, "System volume should be mounted"); assert!( volume.mount_point.exists(), "System volume mount point should exist" ); } MountType::External => { external_count += 1; // External volumes might or might not be mounted info!( "External volume '{}' mounted: {}", volume.name, volume.is_mounted ); } MountType::Network => { network_count += 1; // Network volumes have special properties info!("Network volume '{}' detected", volume.name); } MountType::User => { user_count += 1; info!("User volume '{}' detected", volume.name); } } // All volumes should have valid fingerprints assert!( !volume.fingerprint.0.is_empty(), "Volume fingerprint should not be empty" ); // All volumes should have capacity info assert!( volume.total_bytes_capacity() > 0, "Volume should have capacity" ); } info!( "Volume types - System: {}, External: {}, Network: {}", system_count, external_count, network_count ); // Should have at least one system volume assert!(system_count > 0, "Should detect at least one system volume"); } #[tokio::test] async fn test_volume_tracking_persistence() { let _ = tracing_subscriber::fmt::try_init(); let data_dir = tempdir().unwrap(); let data_path = data_dir.path().to_path_buf(); // Create core and library let core = Arc::new( Core::new(data_path.clone()) .await .expect("Failed to create core"), ); let library_path = data_path.join("libraries").join("persist-test.sdlibrary"); let library = core .libraries .create_library( "Persistence Test", Some(library_path.clone()), core.context.clone(), ) .await .expect("Failed to create library"); let library_id = library.id(); // Get a volume and track it let test_volume = core .volumes .get_all_volumes() .await .into_iter() .find(|v| !matches!(v.mount_type, MountType::System)) .unwrap_or_else(|| { futures::executor::block_on(core.volumes.get_all_volumes()) .first() .cloned() .unwrap() }); let fingerprint = test_volume.fingerprint.clone(); let custom_name = "Persisted Volume".to_string(); // If already tracked (from auto-tracking), untrack first if core .volumes .is_volume_tracked(&library, &fingerprint) .await .unwrap_or(false) { core.volumes .untrack_volume(&library, &fingerprint) .await .expect("Failed to untrack volume"); } // Now track with custom name core.volumes .track_volume(&library, &fingerprint, Some(custom_name.clone())) .await .expect("Failed to track volume"); // Get tracked volumes before closing let tracked_before = core .volumes .get_tracked_volumes(&library) .await .expect("Failed to get tracked volumes"); let volume_count_before = tracked_before.len(); info!( "Tracked {} volumes before closing library", volume_count_before ); // Get library path and clone it before closing let saved_library_path = library.path().to_path_buf(); // Close the library core.libraries .close_library(library_id) .await .expect("Failed to close library"); // Drop the library reference to ensure it's fully released drop(library); // Shutdown core drop(core); // Create new core instance let core2 = Arc::new( Core::new(data_path.clone()) .await .expect("Failed to create second core"), ); // Reopen the library let library2 = core2 .libraries .open_library(&saved_library_path, core2.context.clone()) .await .expect("Failed to reopen library"); // Get tracked volumes after reopening let tracked_after = core2 .volumes .get_tracked_volumes(&library2) .await .expect("Failed to get tracked volumes"); // Verify persistence assert_eq!( tracked_after.len(), volume_count_before, "Volume tracking should persist across library reopening" ); // Find our specific volume let persisted_volume = tracked_after.iter().find(|v| v.fingerprint == fingerprint); if let Some(vol) = persisted_volume { assert_eq!( vol.display_name, Some(custom_name), "Custom volume name should persist" ); } info!("Volume tracking persistence test completed"); } #[tokio::test] async fn test_volume_tracking_edge_cases() { let _ = tracing_subscriber::fmt::try_init(); let data_dir = tempdir().unwrap(); let data_path = data_dir.path().to_path_buf(); let core = Arc::new( Core::new(data_path.clone()) .await .expect("Failed to create core"), ); let library = core .libraries .create_library( "Edge Case Test", Some(data_path.join("libraries").join("edge-test")), core.context.clone(), ) .await .expect("Failed to create library"); let library_id = library.id(); // Get a volume for testing let test_volume = core .volumes .get_all_volumes() .await .first() .cloned() .expect("No volumes available"); let fingerprint = test_volume.fingerprint.clone(); // Get action manager let action_manager = core .context .get_action_manager() .await .expect("Action manager should be initialized"); // Ensure volume is not tracked if core .volumes .is_volume_tracked(&library, &fingerprint) .await .unwrap_or(false) { let untrack_action = VolumeUntrackAction::new(VolumeUntrackInput { fingerprint: fingerprint.clone(), }); action_manager .dispatch_library(Some(library_id), untrack_action) .await .ok(); } // Test 1: Track with empty name info!("Testing tracking with empty name..."); { let track_action = VolumeTrackAction::new(VolumeTrackInput { fingerprint: fingerprint.clone(), name: Some("".to_string()), }); let result = action_manager .dispatch_library(Some(library_id), track_action) .await; assert!(result.is_ok(), "Should handle empty name"); // Untrack for next test let untrack_action = VolumeUntrackAction::new(VolumeUntrackInput { fingerprint: fingerprint.clone(), }); action_manager .dispatch_library(Some(library_id), untrack_action) .await .ok(); } // Test 2: Track with None name info!("Testing tracking with None name..."); { let track_action = VolumeTrackAction::new(VolumeTrackInput { fingerprint: fingerprint.clone(), name: None, }); let result = action_manager .dispatch_library(Some(library_id), track_action) .await; assert!(result.is_ok(), "Should handle None name"); // Verify it uses the volume's default name let tracked = core .volumes .get_tracked_volumes(&library) .await .expect("Failed to get tracked volumes") .into_iter() .find(|v| v.fingerprint == fingerprint) .expect("Volume should be tracked"); assert!( tracked.display_name.is_none() || tracked.display_name == Some(test_volume.name.clone()), "Should use default name when None provided" ); } info!("Volume edge cases test completed"); } #[tokio::test] async fn test_volume_refresh_and_detection() { let _ = tracing_subscriber::fmt::try_init(); let data_dir = tempdir().unwrap(); let data_path = data_dir.path().to_path_buf(); let core = Arc::new( Core::new(data_path.clone()) .await .expect("Failed to create core"), ); // Get initial volume count let initial_volumes = core.volumes.get_all_volumes().await; let initial_count = initial_volumes.len(); info!("Initial volume count: {}", initial_count); // Refresh volumes core.volumes .refresh_volumes() .await .expect("Failed to refresh volumes"); // Get volumes after refresh let refreshed_volumes = core.volumes.get_all_volumes().await; let refreshed_count = refreshed_volumes.len(); info!("Volume count after refresh: {}", refreshed_count); // Volume count should remain consistent assert_eq!( initial_count, refreshed_count, "Volume count should be consistent after refresh" ); // Verify all volumes have valid properties for volume in &refreshed_volumes { assert!( !volume.fingerprint.0.is_empty(), "Fingerprint should not be empty" ); assert!(!volume.name.is_empty(), "Volume name should not be empty"); assert!( volume.total_bytes_capacity() > 0, "Capacity should be positive" ); // Verify mount points exist for mounted volumes if volume.is_mounted { assert!( volume.mount_point.exists(), "Mount point should exist for mounted volume '{}'", volume.name ); } } info!("Volume refresh and detection test completed"); } #[tokio::test] async fn test_volume_monitor_service() { let _ = tracing_subscriber::fmt::try_init(); let data_dir = tempdir().unwrap(); let data_path = data_dir.path().to_path_buf(); let core = Arc::new( Core::new(data_path.clone()) .await .expect("Failed to create core"), ); // Create a library let library = core .libraries .create_library( "Monitor Test", Some(data_path.join("libraries").join("monitor-test")), core.context.clone(), ) .await .expect("Failed to create library"); // Get a volume to track let test_volume = core .volumes .get_all_volumes() .await .first() .cloned() .expect("No volumes available"); let fingerprint = test_volume.fingerprint.clone(); // Track the volume if !core .volumes .is_volume_tracked(&library, &fingerprint) .await .unwrap_or(false) { core.volumes .track_volume(&library, &fingerprint, Some("Monitored Volume".to_string())) .await .expect("Failed to track volume"); } // Volume monitor service is already initialized by Core // Just verify it's working by manually triggering updates // The volume monitor may already be running from Core initialization // We'll just work with the existing state info!("Volume monitor service started"); // Wait a bit for the monitor to run tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; // Get tracked volume state let tracked_before = core .volumes .get_tracked_volumes(&library) .await .expect("Failed to get tracked volumes") .into_iter() .find(|v| v.fingerprint == fingerprint) .expect("Volume should be tracked"); let initial_last_seen = tracked_before.last_seen_at; // Wait for monitor to update (monitor runs every 30s by default, but we'll trigger a refresh) core.volumes .refresh_volumes() .await .expect("Failed to refresh volumes"); // Manually trigger an update to simulate monitor behavior core.volumes .update_tracked_volume_state(&library, &fingerprint, &test_volume) .await .expect("Failed to update volume state"); // Get updated state let tracked_after = core .volumes .get_tracked_volumes(&library) .await .expect("Failed to get tracked volumes") .into_iter() .find(|v| v.fingerprint == fingerprint) .expect("Volume should be tracked"); // Verify the monitor would update the state assert!( tracked_after.last_seen_at >= initial_last_seen, "Volume monitor should update last_seen_at" ); // Don't stop the monitor as it's managed by Core info!("Volume monitor service test completed"); }