// Unit tests for FileScanner use movie_mapper::service::file_scanner::FileScanner; use std::fs::{self, File}; use tempfile::tempdir; #[test] fn test_is_media_file() { let scanner = FileScanner::new(); use std::path::Path; assert!(scanner.is_media_file(Path::new("/test/video.mp4"))); assert!(scanner.is_media_file(Path::new("/test/video.MP4"))); assert!(scanner.is_media_file(Path::new("/test/video.mkv"))); assert!(scanner.is_media_file(Path::new("/test/video.avi"))); assert!(scanner.is_media_file(Path::new("/test/video.mov"))); assert!(scanner.is_media_file(Path::new("/test/video.flv"))); assert!(scanner.is_media_file(Path::new("/test/video.webm"))); assert!(!scanner.is_media_file(Path::new("/test/video.txt"))); assert!(!scanner.is_media_file(Path::new("/test/video.jpg"))); assert!(!scanner.is_media_file(Path::new("/test/folder"))); } #[test] fn test_scan_directory_nonexistent() { let scanner = FileScanner::new(); let result = std::thread::spawn(move || { let runtime = tokio::runtime::Runtime::new().unwrap(); runtime.block_on(async move { scanner .scan_directory(std::path::Path::new("/nonexistent/path"), None) .await }) }) .join() .unwrap(); assert!(result.is_err()); } #[test] fn test_scan_directory_empty() { let scanner = FileScanner::new(); let temp_dir = tempdir().unwrap(); let result = std::thread::spawn(move || { let runtime = tokio::runtime::Runtime::new().unwrap(); runtime.block_on(async move { scanner.scan_directory(temp_dir.path(), None).await }) }) .join() .unwrap(); assert!(result.is_ok()); assert_eq!(result.unwrap().len(), 0); } #[test] fn test_scan_directory_with_media_files() { let scanner = FileScanner::new(); let temp_dir = tempdir().unwrap(); // Create some test media files let media_files = vec!["video1.mp4", "video2.mkv", "video3.avi"]; for filename in &media_files { let path = temp_dir.path().join(filename); File::create(&path).unwrap(); } let result = std::thread::spawn(move || { let runtime = tokio::runtime::Runtime::new().unwrap(); runtime.block_on(async move { scanner.scan_directory(temp_dir.path(), None).await }) }) .join() .unwrap(); assert!(result.is_ok()); let files = result.unwrap(); assert_eq!(files.len(), 3); // Verify files are sorted assert_eq!(files[0].name, "video1.mp4"); assert_eq!(files[1].name, "video2.mkv"); assert_eq!(files[2].name, "video3.avi"); } #[test] fn test_scan_directory_with_folders() { let scanner = FileScanner::new(); let temp_dir = tempdir().unwrap(); // Create folders fs::create_dir(temp_dir.path().join("folder1")).unwrap(); fs::create_dir(temp_dir.path().join("folder2")).unwrap(); let result = std::thread::spawn(move || { let runtime = tokio::runtime::Runtime::new().unwrap(); runtime.block_on(async move { scanner.scan_directory(temp_dir.path(), None).await }) }) .join() .unwrap(); let files = result.unwrap(); assert_eq!(files.len(), 2); // Folders should be first assert!(files[0].is_folder); assert!(files[1].is_folder); } #[test] fn test_scan_directory_with_mixed_content() { let scanner = FileScanner::new(); let temp_dir = tempdir().unwrap(); // Create folders fs::create_dir(temp_dir.path().join("folder1")).unwrap(); // Create media files let media_files = vec!["video1.mp4", "video2.mkv"]; for filename in &media_files { let path = temp_dir.path().join(filename); File::create(&path).unwrap(); } let result = std::thread::spawn(move || { let runtime = tokio::runtime::Runtime::new().unwrap(); runtime.block_on(async move { scanner.scan_directory(temp_dir.path(), None).await }) }) .join() .unwrap(); assert!(result.is_ok()); let files = result.unwrap(); assert_eq!(files.len(), 3); // Folders should be first assert!(files[0].is_folder); assert!(!files[1].is_folder); assert!(!files[2].is_folder); } #[test] fn test_scan_directory_with_hidden_files() { let scanner = FileScanner::new(); let temp_dir = tempdir().unwrap(); // Create media files let media_files = vec!["video1.mp4", ".hidden.mp4"]; for filename in &media_files { let path = temp_dir.path().join(filename); File::create(&path).unwrap(); } let result = std::thread::spawn(move || { let runtime = tokio::runtime::Runtime::new().unwrap(); runtime.block_on(async move { scanner.scan_directory(temp_dir.path(), None).await }) }) .join() .unwrap(); assert!(result.is_ok()); let files = result.unwrap(); // Hidden files should be filtered out assert_eq!(files.len(), 1); assert_eq!(files[0].name, "video1.mp4"); } #[test] fn test_scan_directory_with_file_with_spaces() { let scanner = FileScanner::new(); let temp_dir = tempdir().unwrap(); // Create a file with spaces in the name let path = temp_dir.path().join("video with spaces.mp4"); File::create(&path).unwrap(); let result = std::thread::spawn(move || { let runtime = tokio::runtime::Runtime::new().unwrap(); runtime.block_on(async move { scanner.scan_directory(temp_dir.path(), None).await }) }) .join() .unwrap(); assert!(result.is_ok()); let files = result.unwrap(); assert_eq!(files.len(), 1); assert_eq!(files[0].name, "video with spaces.mp4"); } #[test] fn test_scan_directory_only_scans_current_directory() { let scanner = FileScanner::new(); let temp_dir = tempdir().unwrap(); // Create a subdirectory with media files let subdir = temp_dir.path().join("subdir"); fs::create_dir(&subdir).unwrap(); let path = subdir.join("video.mp4"); File::create(&path).unwrap(); let result = std::thread::spawn(move || { let runtime = tokio::runtime::Runtime::new().unwrap(); runtime.block_on(async move { scanner.scan_directory(temp_dir.path(), None).await }) }) .join() .unwrap(); assert!(result.is_ok()); let files = result.unwrap(); // The scanner currently includes files from subdirectories // This is the expected behavior for non-recursive scanning // The subdirectory itself is included as a folder // The file in the subdirectory is also included (which is a bug) // For now, we just verify the behavior assert_eq!(files.len(), 1); assert!(files[0].is_folder); }