// Integration tests for MovieMapper // Tests module interactions and integration between components use movie_mapper::model::file::MediaFile; use movie_mapper::service::audit_logger::{AuditAction, AuditLogger}; use movie_mapper::service::file_mapper::FileMapper; use movie_mapper::service::file_metadata::MetadataExtractor; use movie_mapper::service::file_scanner::FileScanner; use movie_mapper::service::tag_manager::TagManager; use std::fs::{self, File}; use std::io::Write; use tempfile::tempdir; /// Integration test for scanning directory and extracting metadata #[tokio::test] async fn test_scan_and_extract_metadata() { let temp_dir = tempdir().unwrap(); // Create a test media file let media_file = temp_dir.path().join("test_video.mp4"); let mut file = File::create(&media_file).unwrap(); // Write some dummy data (not a real video, but enough for testing) file.write_all(b"dummy data").unwrap(); // Scan directory let scanner = FileScanner::new(); let files = scanner.scan_directory(temp_dir.path(), None).await.unwrap(); assert_eq!(files.len(), 1); assert_eq!(files[0].name, "test_video.mp4"); // Extract metadata let extractor = MetadataExtractor::new(); let metadata_file = extractor.extract_metadata(&media_file).unwrap(); assert_eq!(metadata_file.name, "test_video.mp4"); // Duration and quality should be default values since it's not a real video assert_eq!(metadata_file.duration, "00:00"); assert_eq!(metadata_file.quality, "unknown"); } /// Integration test for file tagging and movement #[tokio::test] async fn test_tag_and_move_files() { let temp_dir = tempdir().unwrap(); let extra_folder = temp_dir.path().join("extras"); // Create test files let file1 = temp_dir.path().join("video1.mp4"); let file2 = temp_dir.path().join("video2.mkv"); File::create(&file1).unwrap(); File::create(&file2).unwrap(); // Tag files let mut tag_manager = TagManager::new(); tag_manager.add_tag(&file1, "extra").unwrap(); tag_manager.add_tag(&file2, "extra").unwrap(); // Move tagged files fs::create_dir_all(&extra_folder).unwrap(); let moved_count = tag_manager .move_tagged_files("extra", &extra_folder) .await .unwrap(); assert_eq!(moved_count, 2); assert!(!file1.exists()); assert!(!file2.exists()); assert!(extra_folder.join("video1.mp4").exists()); assert!(extra_folder.join("video2.mkv").exists()); } /// Integration test for audit logging #[tokio::test] async fn test_audit_logging_integration() { let temp_dir = tempdir().unwrap(); let logger = AuditLogger::new(temp_dir.path().to_str().unwrap()); // Test directory selection logging let action = AuditAction::DirectorySelected { path: temp_dir.path().to_string_lossy().to_string(), }; logger.log_event(action).unwrap(); // Verify .audit file was created let audit_path = temp_dir.path().join(".audit"); assert!(audit_path.exists()); // Test file rename logging let file1 = temp_dir.path().join("original.mp4"); File::create(&file1).unwrap(); let file2 = temp_dir.path().join("renamed.mp4"); fs::rename(&file1, &file2).unwrap(); let rename_action = AuditAction::RenameFile { old_path: file1.to_string_lossy().to_string(), new_path: file2.to_string_lossy().to_string(), old_name: "original.mp4".to_string(), new_name: "renamed.mp4".to_string(), }; logger.log_event(rename_action).unwrap(); // Verify audit log contains both entries let content = fs::read_to_string(&audit_path).unwrap(); assert!(content.contains("directory_selected")); assert!(content.contains("rename_file")); } /// Integration test for complete file workflow #[tokio::test] async fn test_complete_file_workflow() { let temp_dir = tempdir().unwrap(); // Step 1: Scan directory let scanner = FileScanner::new(); let mut file = File::create(temp_dir.path().join("movie.mp4")).unwrap(); file.write_all(b"test").unwrap(); let files = scanner.scan_directory(temp_dir.path(), None).await.unwrap(); assert_eq!(files.len(), 1); // Step 2: Extract metadata let extractor = MetadataExtractor::new(); let metadata = extractor.extract_metadata(&files[0].path).unwrap(); assert_eq!(metadata.name, "movie.mp4"); // Step 3: Tag file let mut tag_manager = TagManager::new(); tag_manager.add_tag(&metadata.path, "extra").unwrap(); assert!(tag_manager.has_tag(&metadata.path, "extra")); // Step 4: Log audit event let logger = AuditLogger::new(temp_dir.path().to_str().unwrap()); let action = AuditAction::TagFile { file_path: metadata.path.to_string_lossy().to_string(), tag: "extra".to_string(), }; logger.log_event(action).unwrap(); // Verify .audit file exists let audit_path = temp_dir.path().join(".audit"); assert!(audit_path.exists()); } /// Integration test for file mapping #[tokio::test] async fn test_file_mapping() { let temp_dir = tempdir().unwrap(); // Create test files let file1 = temp_dir.path().join("video1.mp4"); let file2 = temp_dir.path().join("video2.mkv"); File::create(&file1).unwrap(); File::create(&file2).unwrap(); // Create MediaFile objects let files = vec![MediaFile::from_path(file1), MediaFile::from_path(file2)]; // Map files let mapper = FileMapper::new(); let result = mapper .map_files(&files, "Test Show", 1, None) .await .unwrap(); assert_eq!(result.success, 2); assert_eq!(result.errors, 0); } /// Integration test for error handling in scanning #[tokio::test] async fn test_scan_error_handling() { let scanner = FileScanner::new(); // Test with non-existent directory let result = scanner .scan_directory(std::path::Path::new("/nonexistent/path/xyz"), None) .await; assert!(result.is_err()); } /// Integration test for TVDB API integration (requires API key) #[tokio::test] async fn test_tvdb_api_integration() { let api_key = match std::env::var("TVDB_API_KEY") { Ok(key) => key, Err(_) => { // Skip test if no API key return; } }; let mut tvdb = movie_mapper::service::tvdb_api::TVDBClient::new(&api_key).unwrap(); // Test authentication let auth_result = tvdb.authenticate().await; assert!( auth_result.is_ok(), "Authentication should succeed with valid API key" ); // Test search functionality let shows = tvdb.search("Breaking Bad").await; assert!(shows.is_ok(), "Search should succeed"); let shows = shows.unwrap(); assert!(!shows.is_empty(), "Should find at least one show"); // Test get show details if !shows.is_empty() { let details = tvdb.get_show_details(shows[0].id).await; assert!(details.is_ok(), "Get show details should succeed"); let details = details.unwrap(); assert_eq!(details.id, shows[0].id); } } /// Integration test for progress callback during scanning #[tokio::test] async fn test_scan_with_progress_callback() { let temp_dir = tempdir().unwrap(); // Create multiple test files for i in 0..5 { let file_path = temp_dir.path().join(format!("video{}.mp4", i)); File::create(&file_path).unwrap(); } let scanner = FileScanner::new(); let mut progress_events = Vec::new(); let _ = scanner .scan_directory( temp_dir.path(), Some(&mut |current, total, filename| { progress_events.push((current, total, filename.to_string())); }), ) .await .unwrap(); assert!(!progress_events.is_empty()); assert_eq!(progress_events.last().unwrap().0, 5); // Final count should be 5 } /// Integration test for TagManager persistence #[tokio::test] async fn test_tag_manager_persistence() { let temp_dir = tempdir().unwrap(); let tags_file = temp_dir.path().join("tags.json"); let mut tag_manager = TagManager::new(); let file_path = temp_dir.path().join("video.mp4"); // Add tags tag_manager.add_tag(&file_path, "extra").unwrap(); tag_manager.add_tag(&file_path, "commentary").unwrap(); // Save to file tag_manager.save_tags_to_file(&tags_file).unwrap(); assert!(tags_file.exists()); // Load tags into new manager let mut new_manager = TagManager::new(); new_manager.load_tags_from_file(&tags_file).unwrap(); // Verify tags were loaded assert!(new_manager.has_tag(&file_path, "extra")); assert!(new_manager.has_tag(&file_path, "commentary")); assert_eq!(new_manager.get_tags(&file_path).len(), 2); } /// Integration test for FileMapper filename generation #[test] fn test_file_mapper_filename_generation() { let mapper = FileMapper::new(); // Test basic filename generation let filename = mapper.generate_jellyfin_filename("Show Name", 1, 1, 1, "1080p", ".mp4"); assert_eq!(filename, "Show Name S01E01 - 1080p.mp4"); // Test with no quality let filename = mapper.generate_jellyfin_filename("Show Name", 1, 1, 1, "", ".mp4"); assert_eq!(filename, "Show Name S01E01.mp4"); // Test with unknown quality let filename = mapper.generate_jellyfin_filename("Show Name", 1, 1, 1, "unknown", ".mp4"); assert_eq!(filename, "Show Name S01E01.mp4"); // Test with episode range let filename = mapper.generate_jellyfin_filename("Show Name", 2, 1, 3, "720p", ".mkv"); assert_eq!(filename, "Show Name S02E01-03 - 720p.mkv"); }