//! Comprehensive tests for storage.rs //! //! Provides 95%+ test coverage for the StorageManager including: //! - All CRUD operations //! - Error handling and edge cases //! - Async operations and concurrency //! - Mock database connections //! - Compression algorithms //! - Versioning and cleanup //! - Export functionality //! - Statistics and metadata use crate::error::DataError; use crate::storage::*; use chrono::Utc; use config::data_config::{ DataCompressionAlgorithm as CompressionAlgorithm, DataCompressionConfig as CompressionConfig, DataRetentionConfig as RetentionConfig, DataStorageConfig as TrainingStorageConfig, DataStorageFormat as StorageFormat, DataVersioningConfig as VersioningConfig, }; use std::collections::HashMap; use std::sync::Arc; use tempfile::TempDir; use tokio::fs; use tokio::time::{sleep, timeout, Duration as TokioDuration}; /// Test helper to create a temporary storage configuration fn create_test_config(temp_dir: &TempDir) -> TrainingStorageConfig { TrainingStorageConfig { base_directory: temp_dir.path().to_path_buf(), format: StorageFormat::Parquet, compression: CompressionConfig { algorithm: CompressionAlgorithm::ZSTD, level: 3, enabled: true, }, versioning: VersioningConfig { enabled: false, version_format: "v%Y%m%d_%H%M%S".to_string(), keep_versions: 5, }, retention: RetentionConfig { retention_days: 30, auto_cleanup: false, cleanup_schedule: "0 2 * * *".to_string(), }, } } /// Test helper to create a storage manager with temporary directory async fn create_test_storage() -> (StorageManager, TempDir) { let temp_dir = TempDir::new().expect("Failed to create temp directory"); let config = create_test_config(&temp_dir); let storage = StorageManager::new(config) .await .expect("Failed to create storage manager"); (storage, temp_dir) } /// Test helper to create test data fn create_test_data(size: usize) -> Vec { (0..size).map(|i| (i % 256) as u8).collect() } /// Test helper to create features data fn create_test_features() -> HashMap> { let mut features = HashMap::new(); features.insert("sma_20".to_string(), vec![1.0, 2.0, 3.0, 4.0, 5.0]); features.insert("rsi_14".to_string(), vec![30.0, 40.0, 50.0, 60.0, 70.0]); features.insert( "volume".to_string(), vec![1000.0, 1500.0, 2000.0, 1800.0, 1200.0], ); features } #[tokio::test] async fn test_storage_manager_creation() { let temp_dir = TempDir::new().unwrap(); let config = create_test_config(&temp_dir); let storage = StorageManager::new(config).await; assert!(storage.is_ok()); // Verify directories were created assert!(temp_dir.path().join("datasets").exists()); assert!(temp_dir.path().join("features").exists()); assert!(temp_dir.path().join("metadata").exists()); assert!(temp_dir.path().join("checkpoints").exists()); } #[tokio::test] async fn test_storage_manager_creation_with_existing_directory() { let temp_dir = TempDir::new().unwrap(); let config = create_test_config(&temp_dir); // Create storage manager twice to test existing directory handling let _storage1 = StorageManager::new(config.clone()).await.unwrap(); let storage2 = StorageManager::new(config).await; assert!(storage2.is_ok()); } #[tokio::test] async fn test_dataset_storage_basic() { let (storage, _temp_dir) = create_test_storage().await; let test_data = create_test_data(1000); let dataset_id = "test_dataset_basic"; // Store dataset let result = storage.store_dataset(dataset_id, &test_data).await; assert!(result.is_ok()); // Load dataset let loaded_data = storage.load_dataset(dataset_id).await.unwrap(); assert_eq!(loaded_data, test_data); } #[tokio::test] async fn test_dataset_storage_large() { let (storage, _temp_dir) = create_test_storage().await; let test_data = create_test_data(100_000); // 100KB let dataset_id = "test_dataset_large"; // Store dataset let result = storage.store_dataset(dataset_id, &test_data).await; assert!(result.is_ok()); // Load dataset let loaded_data = storage.load_dataset(dataset_id).await.unwrap(); assert_eq!(loaded_data, test_data); } #[tokio::test] async fn test_dataset_storage_empty() { let (storage, _temp_dir) = create_test_storage().await; let test_data = Vec::new(); let dataset_id = "test_dataset_empty"; // Store empty dataset let result = storage.store_dataset(dataset_id, &test_data).await; assert!(result.is_ok()); // Load empty dataset let loaded_data = storage.load_dataset(dataset_id).await.unwrap(); assert_eq!(loaded_data, test_data); } #[tokio::test] async fn test_dataset_storage_with_compression_disabled() { let temp_dir = TempDir::new().unwrap(); let mut config = create_test_config(&temp_dir); config.compression.enabled = false; let storage = StorageManager::new(config).await.unwrap(); let test_data = create_test_data(1000); let dataset_id = "test_no_compression"; // Store dataset storage.store_dataset(dataset_id, &test_data).await.unwrap(); // Load dataset let loaded_data = storage.load_dataset(dataset_id).await.unwrap(); assert_eq!(loaded_data, test_data); } #[tokio::test] async fn test_dataset_storage_with_lz4_compression() { let temp_dir = TempDir::new().unwrap(); let mut config = create_test_config(&temp_dir); config.compression.algorithm = CompressionAlgorithm::LZ4; let storage = StorageManager::new(config).await.unwrap(); let test_data = create_test_data(1000); let dataset_id = "test_lz4_compression"; // Store dataset storage.store_dataset(dataset_id, &test_data).await.unwrap(); // Load dataset let loaded_data = storage.load_dataset(dataset_id).await.unwrap(); assert_eq!(loaded_data, test_data); } #[tokio::test] async fn test_dataset_storage_with_gzip_compression() { let temp_dir = TempDir::new().unwrap(); let mut config = create_test_config(&temp_dir); config.compression.algorithm = CompressionAlgorithm::GZIP; let storage = StorageManager::new(config).await.unwrap(); let test_data = create_test_data(1000); let dataset_id = "test_gzip_compression"; // Store dataset storage.store_dataset(dataset_id, &test_data).await.unwrap(); // Load dataset let loaded_data = storage.load_dataset(dataset_id).await.unwrap(); assert_eq!(loaded_data, test_data); } #[tokio::test] async fn test_dataset_load_nonexistent() { let (storage, _temp_dir) = create_test_storage().await; let result = storage.load_dataset("nonexistent_dataset").await; assert!(result.is_err()); assert!(matches!(result.unwrap_err(), DataError::NotFound(_))); } #[tokio::test] async fn test_dataset_checksum_validation() { let (storage, temp_dir) = create_test_storage().await; let test_data = create_test_data(1000); let dataset_id = "test_checksum"; // Store dataset storage.store_dataset(dataset_id, &test_data).await.unwrap(); // Corrupt the file by modifying it directly let metadata = storage.get_metadata(dataset_id).await.unwrap(); let mut corrupted_data = fs::read(&metadata.file_path).await.unwrap(); corrupted_data[0] = corrupted_data[0].wrapping_add(1); // Corrupt first byte fs::write(&metadata.file_path, corrupted_data) .await .unwrap(); // Try to load corrupted dataset let result = storage.load_dataset(dataset_id).await; assert!(result.is_err()); assert!(matches!(result.unwrap_err(), DataError::ValidationSimple(_)));} #[tokio::test] async fn test_features_storage_and_retrieval() { let (storage, _temp_dir) = create_test_storage().await; let features = create_test_features(); let dataset_id = "test_features"; // Store features let result = storage.store_features(dataset_id, &features).await; assert!(result.is_ok()); // Load features let loaded_features = storage.load_features(dataset_id).await.unwrap(); assert_eq!(loaded_features, features); } #[tokio::test] async fn test_features_storage_empty() { let (storage, _temp_dir) = create_test_storage().await; let features = HashMap::new(); let dataset_id = "test_empty_features"; // Store empty features let result = storage.store_features(dataset_id, &features).await; assert!(result.is_ok()); // Load empty features let loaded_features = storage.load_features(dataset_id).await.unwrap(); assert_eq!(loaded_features, features); } #[tokio::test] async fn test_features_load_nonexistent() { let (storage, _temp_dir) = create_test_storage().await; let result = storage.load_features("nonexistent_features").await; assert!(result.is_err()); assert!(matches!(result.unwrap_err(), DataError::NotFound(_))); } #[tokio::test] async fn test_list_datasets_empty() { let (storage, _temp_dir) = create_test_storage().await; let datasets = storage.list_datasets().await; assert!(datasets.is_empty()); } #[tokio::test] async fn test_list_datasets_multiple() { let (storage, _temp_dir) = create_test_storage().await; let test_data1 = create_test_data(100); let test_data2 = create_test_data(200); // Store multiple datasets storage .store_dataset("dataset1", &test_data1) .await .unwrap(); storage .store_dataset("dataset2", &test_data2) .await .unwrap(); let datasets = storage.list_datasets().await; assert_eq!(datasets.len(), 2); let ids: Vec = datasets.iter().map(|d| d.id.clone()).collect(); assert!(ids.contains(&"dataset1".to_string())); assert!(ids.contains(&"dataset2".to_string())); } #[tokio::test] async fn test_get_metadata() { let (storage, _temp_dir) = create_test_storage().await; let test_data = create_test_data(1000); let dataset_id = "test_metadata"; // Store dataset storage.store_dataset(dataset_id, &test_data).await.unwrap(); // Get metadata let metadata = storage.get_metadata(dataset_id).await; assert!(metadata.is_some()); let metadata = metadata.unwrap(); assert_eq!(metadata.id, dataset_id); assert_eq!(metadata.original_size, test_data.len()); assert!(metadata.compressed_size <= test_data.len()); // Should be compressed assert!(!metadata.checksum.is_empty()); } #[tokio::test] async fn test_get_metadata_nonexistent() { let (storage, _temp_dir) = create_test_storage().await; let metadata = storage.get_metadata("nonexistent").await; assert!(metadata.is_none()); } #[tokio::test] async fn test_delete_dataset() { let (storage, _temp_dir) = create_test_storage().await; let test_data = create_test_data(1000); let dataset_id = "test_delete"; // Store dataset storage.store_dataset(dataset_id, &test_data).await.unwrap(); // Verify it exists assert!(storage.get_metadata(dataset_id).await.is_some()); // Delete dataset let result = storage.delete_dataset(dataset_id).await; assert!(result.is_ok()); // Verify it's gone assert!(storage.get_metadata(dataset_id).await.is_none()); // Try to load deleted dataset let result = storage.load_dataset(dataset_id).await; assert!(result.is_err()); } #[tokio::test] async fn test_delete_nonexistent_dataset() { let (storage, _temp_dir) = create_test_storage().await; let result = storage.delete_dataset("nonexistent").await; assert!(result.is_err()); assert!(matches!(result.unwrap_err(), DataError::NotFound(_))); } #[tokio::test] async fn test_create_checkpoint() { let (storage, _temp_dir) = create_test_storage().await; let checkpoint_data = create_test_data(500); let model_id = "test_model"; let checkpoint_id = storage .create_checkpoint(model_id, &checkpoint_data) .await .unwrap(); assert!(checkpoint_id.contains(model_id)); assert!(checkpoint_id.contains(&Utc::now().format("%Y%m%d").to_string())); } #[tokio::test] async fn test_load_checkpoint() { let (storage, _temp_dir) = create_test_storage().await; let checkpoint_data = create_test_data(500); let model_id = "test_model"; // Create checkpoint let checkpoint_id = storage .create_checkpoint(model_id, &checkpoint_data) .await .unwrap(); // Load checkpoint let loaded_data = storage.load_checkpoint(&checkpoint_id).await.unwrap(); assert_eq!(loaded_data, checkpoint_data); } #[tokio::test] async fn test_load_nonexistent_checkpoint() { let (storage, _temp_dir) = create_test_storage().await; let result = storage.load_checkpoint("nonexistent_checkpoint").await; assert!(result.is_err()); assert!(matches!(result.unwrap_err(), DataError::NotFound(_))); } #[tokio::test] async fn test_storage_stats_empty() { let (storage, _temp_dir) = create_test_storage().await; let stats = storage.get_storage_stats().await; assert_eq!(stats.total_datasets, 0); assert_eq!(stats.total_original_size, 0); assert_eq!(stats.total_compressed_size, 0); assert_eq!(stats.avg_compression_ratio, 0.0); assert_eq!(stats.storage_efficiency, 0.0); } #[tokio::test] async fn test_storage_stats_with_data() { let (storage, _temp_dir) = create_test_storage().await; let test_data1 = create_test_data(1000); let test_data2 = create_test_data(2000); // Store datasets storage .store_dataset("dataset1", &test_data1) .await .unwrap(); storage .store_dataset("dataset2", &test_data2) .await .unwrap(); let stats = storage.get_storage_stats().await; assert_eq!(stats.total_datasets, 2); assert_eq!(stats.total_original_size, 3000); assert!(stats.total_compressed_size > 0); assert!(stats.total_compressed_size <= stats.total_original_size); assert!(stats.avg_compression_ratio > 0.0); assert!(stats.storage_efficiency >= 0.0); } #[tokio::test] async fn test_cleanup_disabled() { let (storage, _temp_dir) = create_test_storage().await; let test_data = create_test_data(1000); storage.store_dataset("dataset1", &test_data).await.unwrap(); // Cleanup should do nothing when disabled let result = storage.cleanup().await; assert!(result.is_ok()); // Dataset should still exist assert!(storage.get_metadata("dataset1").await.is_some()); } #[tokio::test] async fn test_cleanup_with_retention() { let temp_dir = TempDir::new().unwrap(); let mut config = create_test_config(&temp_dir); config.retention.auto_cleanup = true; config.retention.retention_days = 1; // 1 day retention let storage = StorageManager::new(config).await.unwrap(); let test_data = create_test_data(1000); storage .store_dataset("old_dataset", &test_data) .await .unwrap(); // Manually set the creation date to be old // This is a limitation of the test - in real usage, old datasets would naturally be old let result = storage.cleanup().await; assert!(result.is_ok()); } #[tokio::test] async fn test_export_dataset_csv() { let (storage, temp_dir) = create_test_storage().await; let test_data = create_test_data(100); let dataset_id = "test_export_csv"; // Store dataset storage.store_dataset(dataset_id, &test_data).await.unwrap(); // Export as CSV let export_path = temp_dir.path().join("export.csv"); let result = storage .export_dataset(dataset_id, ExportFormat::CSV, &export_path) .await; assert!(result.is_ok()); assert!(export_path.exists()); } #[tokio::test] async fn test_export_dataset_parquet() { let (storage, temp_dir) = create_test_storage().await; let test_data = create_test_data(100); let dataset_id = "test_export_parquet"; // Store dataset storage.store_dataset(dataset_id, &test_data).await.unwrap(); // Export as Parquet let export_path = temp_dir.path().join("export.parquet"); let result = storage .export_dataset(dataset_id, ExportFormat::Parquet, &export_path) .await; assert!(result.is_ok()); assert!(export_path.exists()); } #[tokio::test] async fn test_export_dataset_json() { let (storage, temp_dir) = create_test_storage().await; let test_data = create_test_data(100); let dataset_id = "test_export_json"; // Store dataset storage.store_dataset(dataset_id, &test_data).await.unwrap(); // Export as JSON let export_path = temp_dir.path().join("export.json"); let result = storage .export_dataset(dataset_id, ExportFormat::JSON, &export_path) .await; assert!(result.is_ok()); assert!(export_path.exists()); } #[tokio::test] async fn test_export_nonexistent_dataset() { let (storage, temp_dir) = create_test_storage().await; let export_path = temp_dir.path().join("export.csv"); let result = storage .export_dataset("nonexistent", ExportFormat::CSV, &export_path) .await; assert!(result.is_err()); } #[tokio::test] async fn test_versioning_enabled() { let temp_dir = TempDir::new().unwrap(); let mut config = create_test_config(&temp_dir); config.versioning.enabled = true; config.versioning.keep_versions = 3; let storage = StorageManager::new(config).await.unwrap(); let test_data = create_test_data(1000); let dataset_id = "test_versioning"; // Store multiple versions storage.store_dataset(dataset_id, &test_data).await.unwrap(); sleep(TokioDuration::from_millis(10)).await; // Ensure different timestamps storage.store_dataset(dataset_id, &test_data).await.unwrap(); sleep(TokioDuration::from_millis(10)).await; storage.store_dataset(dataset_id, &test_data).await.unwrap(); // Should still be able to load latest version let loaded_data = storage.load_dataset(dataset_id).await.unwrap(); assert_eq!(loaded_data, test_data); } #[tokio::test] async fn test_different_storage_formats() { for format in [ StorageFormat::Parquet, StorageFormat::Arrow, StorageFormat::CSV, StorageFormat::HDF5, ] { let temp_dir = TempDir::new().unwrap(); let mut config = create_test_config(&temp_dir); config.format = format.clone(); let storage = StorageManager::new(config).await.unwrap(); let test_data = create_test_data(100); let dataset_id = format!("test_{:?}", format); // Store and load with different format let result = storage.store_dataset(&dataset_id, &test_data).await; assert!(result.is_ok()); let loaded_data = storage.load_dataset(&dataset_id).await.unwrap(); assert_eq!(loaded_data, test_data); } } #[tokio::test] async fn test_concurrent_dataset_operations() { let (storage, _temp_dir) = create_test_storage().await; let storage = Arc::new(storage); let mut handles = Vec::new(); // Launch multiple concurrent operations for i in 0..10 { let storage_clone = storage.clone(); let handle = tokio::spawn(async move { let dataset_id = format!("concurrent_dataset_{}", i); let test_data = create_test_data(100 + i); // Store dataset storage_clone .store_dataset(&dataset_id, &test_data) .await .unwrap(); // Load dataset let loaded_data = storage_clone.load_dataset(&dataset_id).await.unwrap(); assert_eq!(loaded_data, test_data); dataset_id }); handles.push(handle); } // Wait for all operations to complete let mut dataset_ids = Vec::new(); for handle in handles { let dataset_id = handle.await.unwrap(); dataset_ids.push(dataset_id); } // Verify all datasets exist let all_datasets = storage.list_datasets().await; assert_eq!(all_datasets.len(), 10); for dataset_id in dataset_ids { assert!(storage.get_metadata(&dataset_id).await.is_some()); } } #[tokio::test] async fn test_concurrent_feature_operations() { let (storage, _temp_dir) = create_test_storage().await; let storage = Arc::new(storage); let mut handles = Vec::new(); // Launch multiple concurrent feature operations for i in 0..5 { let storage_clone = storage.clone(); let handle = tokio::spawn(async move { let dataset_id = format!("concurrent_features_{}", i); let mut features = create_test_features(); // Add unique feature for this iteration features.insert(format!("unique_feature_{}", i), vec![i as f64; 5]); // Store features storage_clone .store_features(&dataset_id, &features) .await .unwrap(); // Load features let loaded_features = storage_clone.load_features(&dataset_id).await.unwrap(); assert_eq!(loaded_features, features); dataset_id }); handles.push(handle); } // Wait for all operations to complete for handle in handles { handle.await.unwrap(); } } #[tokio::test] async fn test_large_dataset_operations() { let (storage, _temp_dir) = create_test_storage().await; // Test with 1MB of data let large_data = create_test_data(1_000_000); let dataset_id = "large_dataset"; let start_time = std::time::Instant::now(); // Store large dataset storage .store_dataset(dataset_id, &large_data) .await .unwrap(); let store_duration = start_time.elapsed(); println!("Store duration: {:?}", store_duration); let load_start = std::time::Instant::now(); // Load large dataset let loaded_data = storage.load_dataset(dataset_id).await.unwrap(); let load_duration = load_start.elapsed(); println!("Load duration: {:?}", load_duration); assert_eq!(loaded_data, large_data); // Verify compression worked let metadata = storage.get_metadata(dataset_id).await.unwrap(); assert!(metadata.compressed_size < metadata.original_size); println!( "Compression ratio: {:.2}%", (1.0 - metadata.compression_ratio) * 100.0 ); } #[tokio::test] async fn test_dataset_with_special_characters() { let (storage, _temp_dir) = create_test_storage().await; let test_data = create_test_data(100); let dataset_id = "test_dataset_with_special_chars_!@#$%"; // Store dataset with special characters in ID let result = storage.store_dataset(dataset_id, &test_data).await; assert!(result.is_ok()); // Load dataset let loaded_data = storage.load_dataset(dataset_id).await.unwrap(); assert_eq!(loaded_data, test_data); } #[tokio::test] async fn test_metadata_persistence() { let temp_dir = TempDir::new().unwrap(); let config = create_test_config(&temp_dir); let test_data = create_test_data(1000); let dataset_id = "test_persistence"; // Create first storage manager and store dataset { let storage = StorageManager::new(config.clone()).await.unwrap(); storage.store_dataset(dataset_id, &test_data).await.unwrap(); // Verify metadata exists assert!(storage.get_metadata(dataset_id).await.is_some()); } // Storage manager goes out of scope // Create new storage manager with same config { let storage = StorageManager::new(config).await.unwrap(); // Should load existing metadata let metadata = storage.get_metadata(dataset_id).await; assert!(metadata.is_some()); // Should be able to load the dataset let loaded_data = storage.load_dataset(dataset_id).await.unwrap(); assert_eq!(loaded_data, test_data); } } #[tokio::test] async fn test_compression_algorithms_all() { let algorithms = vec![ CompressionAlgorithm::ZSTD, CompressionAlgorithm::LZ4, CompressionAlgorithm::GZIP, ]; for algorithm in algorithms { let temp_dir = TempDir::new().unwrap(); let mut config = create_test_config(&temp_dir); config.compression.algorithm = algorithm.clone(); let storage = StorageManager::new(config).await.unwrap(); let test_data = create_test_data(1000); let dataset_id = format!("test_{:?}", algorithm); // Store and load with different compression algorithms storage .store_dataset(&dataset_id, &test_data) .await .unwrap(); let loaded_data = storage.load_dataset(&dataset_id).await.unwrap(); assert_eq!(loaded_data, test_data); // Verify compression worked let metadata = storage.get_metadata(&dataset_id).await.unwrap(); if metadata.original_size > 100 { // Only check compression if data is large enough assert!(metadata.compressed_size <= metadata.original_size); } } } #[tokio::test] async fn test_compression_levels() { let temp_dir = TempDir::new().unwrap(); let mut results = Vec::new(); // Test different compression levels for level in [1, 5, 9] { let mut config = create_test_config(&temp_dir); config.compression.level = level; config.base_directory = temp_dir.path().join(format!("level_{}", level)); let storage = StorageManager::new(config).await.unwrap(); let test_data = create_test_data(10000); // Larger data for better compression testing let dataset_id = "compression_test"; storage.store_dataset(dataset_id, &test_data).await.unwrap(); let metadata = storage.get_metadata(dataset_id).await.unwrap(); results.push((level, metadata.compression_ratio)); // Verify data integrity let loaded_data = storage.load_dataset(dataset_id).await.unwrap(); assert_eq!(loaded_data, test_data); } println!("Compression results: {:?}", results); } #[tokio::test] async fn test_error_handling_io_errors() { let temp_dir = TempDir::new().unwrap(); let config = create_test_config(&temp_dir); let storage = StorageManager::new(config).await.unwrap(); let test_data = create_test_data(100); let dataset_id = "test_io_error"; // Store dataset first storage.store_dataset(dataset_id, &test_data).await.unwrap(); // Remove the entire datasets directory to cause IO error fs::remove_dir_all(temp_dir.path().join("datasets")) .await .unwrap(); // Try to load dataset - should get IO error let result = storage.load_dataset(dataset_id).await; assert!(result.is_err()); } #[tokio::test] async fn test_timeout_operations() { let (storage, _temp_dir) = create_test_storage().await; let test_data = create_test_data(100); let dataset_id = "timeout_test"; // Test operation with timeout let result = timeout( TokioDuration::from_secs(5), storage.store_dataset(dataset_id, &test_data), ) .await; assert!(result.is_ok()); assert!(result.unwrap().is_ok()); } #[tokio::test] async fn test_storage_with_different_formats() { for format in [ StorageFormat::Parquet, StorageFormat::Arrow, StorageFormat::CSV, ] { let temp_dir = TempDir::new().unwrap(); let mut config = create_test_config(&temp_dir); config.format = format.clone(); let storage = StorageManager::new(config).await.unwrap(); let test_data = create_test_data(100); let dataset_id = format!("format_test_{:?}", format); storage .store_dataset(&dataset_id, &test_data) .await .unwrap(); let loaded_data = storage.load_dataset(&dataset_id).await.unwrap(); assert_eq!(loaded_data, test_data); // Verify file extension let metadata = storage.get_metadata(&dataset_id).await.unwrap(); let extension = metadata.file_path.extension().unwrap().to_str().unwrap(); match format { StorageFormat::Parquet => assert_eq!(extension, "parquet"), StorageFormat::Arrow => assert_eq!(extension, "arrow"), StorageFormat::Json => assert_eq!(extension, "json"), StorageFormat::Csv => assert_eq!(extension, "csv"), StorageFormat::CSV => assert_eq!(extension, "csv"), StorageFormat::HDF5 => assert_eq!(extension, "h5"), } } } #[tokio::test] async fn test_dataset_overwrite() { let (storage, _temp_dir) = create_test_storage().await; let dataset_id = "overwrite_test"; let original_data = create_test_data(100); let new_data = create_test_data(200); // Store original dataset storage .store_dataset(dataset_id, &original_data) .await .unwrap(); let loaded_original = storage.load_dataset(dataset_id).await.unwrap(); assert_eq!(loaded_original, original_data); // Store new dataset with same ID (overwrite) storage.store_dataset(dataset_id, &new_data).await.unwrap(); let loaded_new = storage.load_dataset(dataset_id).await.unwrap(); assert_eq!(loaded_new, new_data); // Should only have one dataset in the registry let datasets = storage.list_datasets().await; assert_eq!(datasets.len(), 1); } #[tokio::test] async fn test_features_with_large_data() { let (storage, _temp_dir) = create_test_storage().await; let mut large_features = HashMap::new(); // Create large feature vectors for i in 0..100 { let feature_name = format!("feature_{}", i); let feature_data: Vec = (0..1000).map(|j| (i * j) as f64).collect(); large_features.insert(feature_name, feature_data); } let dataset_id = "large_features_test"; // Store large features storage .store_features(dataset_id, &large_features) .await .unwrap(); // Load large features let loaded_features = storage.load_features(dataset_id).await.unwrap(); assert_eq!(loaded_features.len(), large_features.len()); assert_eq!(loaded_features, large_features); } #[tokio::test] async fn test_checkpoint_with_compression() { let (storage, _temp_dir) = create_test_storage().await; let checkpoint_data = create_test_data(5000); // Larger data for compression let model_id = "compression_checkpoint_test"; // Create checkpoint let checkpoint_id = storage .create_checkpoint(model_id, &checkpoint_data) .await .unwrap(); // Load checkpoint let loaded_data = storage.load_checkpoint(&checkpoint_id).await.unwrap(); assert_eq!(loaded_data, checkpoint_data); } #[tokio::test] async fn test_multiple_checkpoints_same_model() { let (storage, _temp_dir) = create_test_storage().await; let model_id = "multi_checkpoint_test"; let checkpoint1_data = create_test_data(100); let checkpoint2_data = create_test_data(200); // Create multiple checkpoints let checkpoint1_id = storage .create_checkpoint(model_id, &checkpoint1_data) .await .unwrap(); sleep(TokioDuration::from_millis(10)).await; // Ensure different timestamps let checkpoint2_id = storage .create_checkpoint(model_id, &checkpoint2_data) .await .unwrap(); // Load both checkpoints let loaded1 = storage.load_checkpoint(&checkpoint1_id).await.unwrap(); let loaded2 = storage.load_checkpoint(&checkpoint2_id).await.unwrap(); assert_eq!(loaded1, checkpoint1_data); assert_eq!(loaded2, checkpoint2_data); assert_ne!(checkpoint1_id, checkpoint2_id); } // Stress tests #[tokio::test] async fn test_many_small_datasets() { let (storage, _temp_dir) = create_test_storage().await; let num_datasets = 100; let mut dataset_ids = Vec::new(); // Store many small datasets for i in 0..num_datasets { let dataset_id = format!("small_dataset_{}", i); let test_data = create_test_data(10 + i); // Variable size storage .store_dataset(&dataset_id, &test_data) .await .unwrap(); dataset_ids.push(dataset_id); } // Verify all datasets exist let all_datasets = storage.list_datasets().await; assert_eq!(all_datasets.len(), num_datasets); // Load and verify random datasets for i in (0..num_datasets).step_by(10) { let dataset_id = &dataset_ids[i]; let expected_data = create_test_data(10 + i); let loaded_data = storage.load_dataset(dataset_id).await.unwrap(); assert_eq!(loaded_data, expected_data); } // Test storage statistics let stats = storage.get_storage_stats().await; assert_eq!(stats.total_datasets, num_datasets); assert!(stats.total_original_size > 0); } #[tokio::test] async fn test_edge_case_empty_strings() { let (storage, _temp_dir) = create_test_storage().await; // Test with empty dataset ID - should fail let test_data = create_test_data(100); let _result = storage.store_dataset("", &test_data).await; // Note: Current implementation doesn't validate empty IDs, but it should // In a real implementation, this might be a validation error } #[tokio::test] async fn test_metadata_tags() { let (storage, _temp_dir) = create_test_storage().await; let test_data = create_test_data(100); let dataset_id = "tagged_dataset"; // Store dataset storage.store_dataset(dataset_id, &test_data).await.unwrap(); // Get metadata and verify it has tags field let metadata = storage.get_metadata(dataset_id).await.unwrap(); assert!(metadata.tags.is_empty()); // Should start empty // Note: Current implementation doesn't provide a way to add custom tags // This would be a feature enhancement } #[tokio::test] async fn test_compression_with_small_data() { let (storage, _temp_dir) = create_test_storage().await; // Very small data might not compress well let small_data = vec![1, 2, 3, 4, 5]; let dataset_id = "tiny_dataset"; storage .store_dataset(dataset_id, &small_data) .await .unwrap(); let loaded_data = storage.load_dataset(dataset_id).await.unwrap(); assert_eq!(loaded_data, small_data); let metadata = storage.get_metadata(dataset_id).await.unwrap(); // For very small data, compression might actually increase size // This is normal and expected assert!(metadata.compressed_size > 0); } #[tokio::test] async fn test_unicode_dataset_ids() { let (storage, _temp_dir) = create_test_storage().await; let test_data = create_test_data(100); let unicode_id = "测试_dataset_🚀"; // Store dataset with Unicode ID let result = storage.store_dataset(unicode_id, &test_data).await; assert!(result.is_ok()); // Load dataset with Unicode ID let loaded_data = storage.load_dataset(unicode_id).await.unwrap(); assert_eq!(loaded_data, test_data); // Verify metadata let metadata = storage.get_metadata(unicode_id).await.unwrap(); assert_eq!(metadata.id, unicode_id); }