/* * This file is a part of RSPlayer * Licensed under the GPL-3.0 License * Copyright (C) 2026 Salmonik @ salmonik@tuta.io * https://codeberg.org/salmonik/RSPlayer */ use rsp::database::{Database, extract_metadata, is_audio_file}; use std::path::PathBuf; #[test] fn test_is_audio_file_mp3() { let path = PathBuf::from("/music/song.mp3"); assert!(is_audio_file(&path)); } #[test] fn test_is_audio_file_flac() { let path = PathBuf::from("/music/song.flac"); assert!(is_audio_file(&path)); } #[test] fn test_is_audio_file_ogg() { let path = PathBuf::from("/music/song.ogg"); assert!(is_audio_file(&path)); } #[test] fn test_is_audio_file_m4a() { let path = PathBuf::from("/music/song.m4a"); assert!(is_audio_file(&path)); } #[test] fn test_is_audio_file_wav() { let path = PathBuf::from("/music/song.wav"); assert!(is_audio_file(&path)); } #[test] fn test_is_audio_file_opus() { let path = PathBuf::from("/music/song.opus"); assert!(is_audio_file(&path)); } #[test] fn test_is_audio_file_uppercase() { let path = PathBuf::from("/music/song.MP3"); assert!(is_audio_file(&path)); } #[test] fn test_is_audio_file_non_audio() { let path = PathBuf::from("/music/song.txt"); assert!(!is_audio_file(&path)); } #[test] fn test_is_audio_file_no_extension() { let path = PathBuf::from("/music/songfile"); assert!(!is_audio_file(&path)); } #[test] fn test_extract_metadata_standard_path() { let path = PathBuf::from("/music/Artist/Album/Track.mp3"); let (artist, album, title, track_num, duration) = extract_metadata(&path); assert_eq!(artist, "Artist"); assert_eq!(album, "Album"); assert_eq!(title, "Track"); assert_eq!(track_num, None); assert_eq!(duration, None); } #[test] fn test_extract_metadata_deep_path() { let path = PathBuf::from("/music/Artist/Album/Disc1/Track.mp3"); let (artist, album, title, _, _) = extract_metadata(&path); assert_eq!(artist, "Album"); assert_eq!(album, "Disc1"); assert_eq!(title, "Track"); } #[test] fn test_extract_metadata_no_parent() { let path = PathBuf::from("/song.mp3"); let (artist, album, title, _, _) = extract_metadata(&path); assert_eq!(artist, "Unknown"); assert_eq!(album, "Unknown"); assert_eq!(title, "song"); } fn seed_test_db(db: &Database) { let conn: &rusqlite::Connection = db.conn(); conn.execute_batch( "INSERT INTO artists (id, name) VALUES (1, 'Metallica'), (2, 'Miles Davis'), (3, 'Aphex Twin'); INSERT INTO albums (id, artist_id, title) VALUES (1, 1, 'Master of Puppets'), (2, 1, 'Ride the Lightning'), (3, 2, 'Kind of Blue'), (4, 3, 'Selected Ambient Works'); INSERT INTO tracks (id, album_id, title, path, duration, track_number) VALUES (1, 1, 'Battery', '/m/Metallica/Master of Puppets/01-Battery.mp3', 313, 1), (2, 1, 'Master of Puppets', '/m/Metallica/Master of Puppets/02-Master of Puppets.mp3', 515, 2), (3, 2, 'Fight Fire with Fire', '/m/Metallica/Ride the Lightning/01-Fight Fire with Fire.mp3', 285, 1), (4, 3, 'So What', '/m/Miles Davis/Kind of Blue/01-So What.mp3', 562, 1), (5, 4, 'Xtal', '/m/Aphex Twin/Selected Ambient Works/01-Xtal.mp3', 437, 1);", ) .unwrap(); } #[test] fn test_complete_artists_prefix() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let results = db.complete("M").unwrap(); assert!(results.contains(&"Metallica/".to_string())); assert!(results.contains(&"Miles Davis/".to_string())); } #[test] fn test_complete_artists_prefix_lowercase() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let results = db.complete("m").unwrap(); assert!( results.contains(&"Metallica/".to_string()), "LIKE should be case-insensitive" ); } #[test] fn test_complete_no_match() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let results = db.complete("Z").unwrap(); assert!(results.is_empty()); } #[test] fn test_complete_empty_prefix() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let results = db.complete("").unwrap(); assert_eq!(results.len(), 3); assert!(results.contains(&"Metallica/".to_string())); assert!(results.contains(&"Miles Davis/".to_string())); assert!(results.contains(&"Aphex Twin/".to_string())); } #[test] fn test_complete_albums_for_artist() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let results = db.complete("Metallica/").unwrap(); assert_eq!(results.len(), 2); assert!(results.contains(&"Metallica/Master of Puppets/".to_string())); assert!(results.contains(&"Metallica/Ride the Lightning/".to_string())); } #[test] fn test_complete_albums_artist_lowercase() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let results = db.complete("metallica/").unwrap(); assert_eq!(results.len(), 2, "artist match should be case-insensitive"); assert!(results.contains(&"metallica/Master of Puppets/".to_string())); } #[test] fn test_complete_albums_with_album_prefix() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); // "R" matches "Ride the Lightning" (prefix) and "Master of Puppets" (contains 'r') let results = db.complete("Metallica/R").unwrap(); assert_eq!(results.len(), 2); assert_eq!(results[0], "Metallica/Ride the Lightning/"); assert_eq!(results[1], "Metallica/Master of Puppets/"); } #[test] fn test_complete_albums_substring_match() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); // "ind" matches "Kind of Blue" (contains "ind") let results = db.complete("Miles Davis/ind").unwrap(); assert_eq!(results.len(), 1); assert_eq!(results[0], "Miles Davis/Kind of Blue/"); } #[test] fn test_complete_artists_substring_match() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); // "x" matches "Aphex Twin" (contains 'x') let results = db.complete("x").unwrap(); assert!(results.contains(&"Aphex Twin/".to_string())); } #[test] fn test_complete_albums_unknown_artist() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let results = db.complete("Unknown/").unwrap(); assert!(results.is_empty()); } #[test] fn test_complete_tracks_for_album() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let results = db.complete("Metallica/Master of Puppets/").unwrap(); assert_eq!(results.len(), 2); assert!(results.contains(&"Metallica/Master of Puppets/Battery".to_string())); assert!(results.contains(&"Metallica/Master of Puppets/Master of Puppets".to_string())); } #[test] fn test_complete_tracks_with_prefix() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let results = db.complete("Metallica/Master of Puppets/B").unwrap(); assert_eq!(results.len(), 1); assert_eq!(results[0], "Metallica/Master of Puppets/Battery"); } #[test] fn test_complete_tracks_unknown_album() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let results = db.complete("Metallica/Nonexistent/").unwrap(); assert!(results.is_empty()); } #[test] fn test_complete_empty_db() { let db = Database::open_in_memory().unwrap(); assert!(db.complete("").unwrap().is_empty()); assert!(db.complete("x/").unwrap().is_empty()); assert!(db.complete("x/y/").unwrap().is_empty()); } // --- find_tracks_by_album tests --- #[test] fn test_find_tracks_by_album_all() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let tracks = db .find_tracks_by_album("Metallica", "Master of Puppets") .unwrap(); assert_eq!(tracks.len(), 2); assert_eq!(tracks[0].title, "Battery"); assert_eq!(tracks[1].title, "Master of Puppets"); assert_eq!(tracks[0].artist_name.as_deref(), Some("Metallica")); assert_eq!(tracks[0].album_title.as_deref(), Some("Master of Puppets")); } #[test] fn test_find_tracks_by_album_single_track() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let tracks = db .find_tracks_by_album("Miles Davis", "Kind of Blue") .unwrap(); assert_eq!(tracks.len(), 1); assert_eq!(tracks[0].title, "So What"); } #[test] fn test_find_tracks_by_album_unknown_artist() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let tracks = db .find_tracks_by_album("Nonexistent", "Master of Puppets") .unwrap(); assert!(tracks.is_empty()); } #[test] fn test_find_tracks_by_album_unknown_album() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let tracks = db.find_tracks_by_album("Metallica", "Nonexistent").unwrap(); assert!(tracks.is_empty()); } #[test] fn test_find_tracks_by_album_case_insensitive_artist() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let tracks = db .find_tracks_by_album("metallica", "Master of Puppets") .unwrap(); assert_eq!( tracks.len(), 2, "artist name match should be case-insensitive" ); assert_eq!(tracks[0].title, "Battery"); } #[test] fn test_find_tracks_by_album_ordered_by_track_number() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let tracks = db .find_tracks_by_album("Metallica", "Master of Puppets") .unwrap(); assert_eq!(tracks[0].track_number, Some(1)); assert_eq!(tracks[1].track_number, Some(2)); } // --- find_track_by_parts tests --- #[test] fn test_find_track_by_parts_exact() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let track = db .find_track_by_parts("Metallica", "Master of Puppets", "Battery") .unwrap(); assert!(track.is_some()); let t = track.unwrap(); assert_eq!(t.title, "Battery"); assert_eq!(t.artist_name.as_deref(), Some("Metallica")); assert_eq!(t.album_title.as_deref(), Some("Master of Puppets")); } #[test] fn test_find_track_by_parts_prefix() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let track = db .find_track_by_parts("Metallica", "Master of Puppets", "B") .unwrap(); assert!(track.is_some()); assert_eq!(track.unwrap().title, "Battery"); } #[test] fn test_find_track_by_parts_no_match_track() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let track = db .find_track_by_parts("Metallica", "Master of Puppets", "Z") .unwrap(); assert!(track.is_none()); } #[test] fn test_find_track_by_parts_no_match_album() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let track = db .find_track_by_parts("Metallica", "Nonexistent", "B") .unwrap(); assert!(track.is_none()); } #[test] fn test_find_track_by_parts_no_match_artist() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let track = db .find_track_by_parts("Nonexistent", "Master of Puppets", "B") .unwrap(); assert!(track.is_none()); } #[test] fn test_find_track_by_parts_case_insensitive() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let track = db .find_track_by_parts("metallica", "Master of Puppets", "Battery") .unwrap(); assert!(track.is_some(), "artist match should be case-insensitive"); assert_eq!(track.unwrap().title, "Battery"); } // --- resolve_path tests --- #[test] fn test_resolve_path_two_parts_album() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let result = db.resolve_path("Metallica/Master of Puppets").unwrap(); assert!(result.is_some()); let tracks = result.unwrap(); assert_eq!(tracks.len(), 2); assert_eq!(tracks[0].title, "Battery"); } #[test] fn test_resolve_path_two_parts_trailing_slash() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let result = db.resolve_path("Metallica/Master of Puppets/").unwrap(); assert!(result.is_some()); let tracks = result.unwrap(); assert_eq!(tracks.len(), 2); } #[test] fn test_resolve_path_three_parts_track() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let result = db .resolve_path("Metallica/Master of Puppets/Battery") .unwrap(); assert!(result.is_some()); let tracks = result.unwrap(); assert_eq!(tracks.len(), 1); assert_eq!(tracks[0].title, "Battery"); } #[test] fn test_resolve_path_three_parts_track_prefix() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let result = db.resolve_path("Metallica/Master of Puppets/B").unwrap(); assert!(result.is_some()); let tracks = result.unwrap(); assert_eq!(tracks.len(), 1); assert_eq!(tracks[0].title, "Battery"); } #[test] fn test_resolve_path_single_part_returns_none() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let result = db.resolve_path("Metallica").unwrap(); assert!( result.is_none(), "single part should return None for fallback" ); } #[test] fn test_resolve_path_no_match_album() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let result = db.resolve_path("Metallica/Nonexistent").unwrap(); assert!(result.is_some()); assert!(result.unwrap().is_empty()); } #[test] fn test_resolve_path_no_match_track() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let result = db.resolve_path("Metallica/Master of Puppets/Z").unwrap(); assert!(result.is_some()); assert!(result.unwrap().is_empty()); } #[test] fn test_resolve_path_empty_db() { let db = Database::open_in_memory().unwrap(); assert!(db.resolve_path("Artist/Album").unwrap().unwrap().is_empty()); assert!( db.resolve_path("Artist/Album/Track") .unwrap() .unwrap() .is_empty() ); assert!(db.resolve_path("Artist").unwrap().is_none()); } // --- Track conversion helper tests --- #[test] fn test_find_tracks_by_album_returns_full_metadata() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let tracks = db .find_tracks_by_album("Metallica", "Ride the Lightning") .unwrap(); assert_eq!(tracks.len(), 1); let t = &tracks[0]; assert_eq!(t.artist_name.as_deref(), Some("Metallica")); assert_eq!(t.album_title.as_deref(), Some("Ride the Lightning")); assert_eq!(t.title, "Fight Fire with Fire"); assert!(t.path.to_string_lossy().contains("Ride the Lightning")); } #[test] fn test_find_track_by_parts_returns_full_metadata() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); let t = db .find_track_by_parts("Aphex Twin", "Selected Ambient Works", "Xtal") .unwrap() .expect("track should be found"); assert_eq!(t.artist_name.as_deref(), Some("Aphex Twin")); assert_eq!(t.album_title.as_deref(), Some("Selected Ambient Works")); assert_eq!(t.title, "Xtal"); assert_eq!(t.track_number, Some(1)); }