mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2025-12-11 20:15:30 +01:00
1207 lines
29 KiB
Rust
1207 lines
29 KiB
Rust
//! 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");
|
|
}
|