MovieMapper/Rust/tests/integration/integration_tests.rs
Jarian Cottingham 6dd4e83ddf feat: Add Rust implementation for performance-critical components
- Implement core Rust backend with FFmpeg integration
- Add TheTVDB API client with token caching
- Implement directory scanner with progress callbacks
- Create file manager with rename and move operations
- Add audit logging functionality
- Implement file mapping for TV episode renaming
- Build Node.js native addon via NAPI
- Include comprehensive unit and integration tests
- Update gitignore to exclude build artifacts and temp files

Resolves #TBD
2026-02-28 09:52:08 -06:00

298 lines
9.4 KiB
Rust

// 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");
}