diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/daemon.rs | 79 | ||||
| -rw-r--r-- | tests/database.rs | 112 | ||||
| -rw-r--r-- | tests/scrobbler.rs | 17 |
3 files changed, 161 insertions, 47 deletions
diff --git a/tests/daemon.rs b/tests/daemon.rs new file mode 100644 index 0000000..e5cb270 --- /dev/null +++ b/tests/daemon.rs | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | /* | ||
| 2 | * This file is a part of RSPlayer | ||
| 3 | * Licensed under the GPL-3.0 License | ||
| 4 | * Copyright (C) 2026 Salmonik @ salmonik@tuta.io | ||
| 5 | * https://codeberg.org/salmonik/RSPlayer | ||
| 6 | */ | ||
| 7 | |||
| 8 | use std::path::PathBuf; | ||
| 9 | |||
| 10 | use rsp::daemon::ScrobbleState; | ||
| 11 | use rsp::queue::Track; | ||
| 12 | |||
| 13 | #[test] | ||
| 14 | fn test_scrobble_state_new() { | ||
| 15 | let s = ScrobbleState::new(); | ||
| 16 | assert!(s.track.is_none()); | ||
| 17 | assert!(s.start_time.is_none()); | ||
| 18 | assert!(s.timestamp.is_none()); | ||
| 19 | } | ||
| 20 | |||
| 21 | #[test] | ||
| 22 | fn test_scrobble_state_clear() { | ||
| 23 | let mut s = ScrobbleState::new(); | ||
| 24 | let t = Track { | ||
| 25 | path: PathBuf::from("/test/song.mp3"), | ||
| 26 | title: Some("Test".into()), | ||
| 27 | artist: Some("Artist".into()), | ||
| 28 | album: None, | ||
| 29 | duration_secs: Some(200.0), | ||
| 30 | is_stream: false, | ||
| 31 | }; | ||
| 32 | s.start(t); | ||
| 33 | assert!(s.track.is_some()); | ||
| 34 | s.clear(); | ||
| 35 | assert!(s.track.is_none()); | ||
| 36 | assert!(s.start_time.is_none()); | ||
| 37 | assert!(s.timestamp.is_none()); | ||
| 38 | } | ||
| 39 | |||
| 40 | #[test] | ||
| 41 | fn test_should_scrobble_without_track_returns_false() { | ||
| 42 | assert!(!ScrobbleState::new().should_scrobble()); | ||
| 43 | } | ||
| 44 | |||
| 45 | #[test] | ||
| 46 | fn test_should_scrobble_threshold_below_30_seconds() { | ||
| 47 | assert!(!ScrobbleState::should_scrobble_with_params(29, Some(200.0))); | ||
| 48 | } | ||
| 49 | |||
| 50 | #[test] | ||
| 51 | fn test_should_scrobble_threshold_half_duration() { | ||
| 52 | assert!(ScrobbleState::should_scrobble_with_params(100, Some(200.0))); | ||
| 53 | } | ||
| 54 | |||
| 55 | #[test] | ||
| 56 | fn test_should_scrobble_threshold_below_half_duration() { | ||
| 57 | assert!(!ScrobbleState::should_scrobble_with_params(80, Some(200.0))); | ||
| 58 | } | ||
| 59 | |||
| 60 | #[test] | ||
| 61 | fn test_should_scrobble_threshold_capped_at_4min() { | ||
| 62 | assert!(ScrobbleState::should_scrobble_with_params(240, Some(600.0))); | ||
| 63 | assert!(!ScrobbleState::should_scrobble_with_params( | ||
| 64 | 239, | ||
| 65 | Some(600.0) | ||
| 66 | )); | ||
| 67 | } | ||
| 68 | |||
| 69 | #[test] | ||
| 70 | fn test_should_scrobble_no_duration_requires_4min() { | ||
| 71 | assert!(ScrobbleState::should_scrobble_with_params(240, None)); | ||
| 72 | assert!(!ScrobbleState::should_scrobble_with_params(239, None)); | ||
| 73 | } | ||
| 74 | |||
| 75 | #[test] | ||
| 76 | fn test_should_scrobble_short_song() { | ||
| 77 | assert!(ScrobbleState::should_scrobble_with_params(30, Some(60.0))); | ||
| 78 | assert!(!ScrobbleState::should_scrobble_with_params(15, Some(60.0))); | ||
| 79 | } | ||
diff --git a/tests/database.rs b/tests/database.rs index adbf535..84eb663 100644 --- a/tests/database.rs +++ b/tests/database.rs | |||
| @@ -114,115 +114,131 @@ fn seed_test_db(db: &Database) { | |||
| 114 | fn test_complete_artists_prefix() { | 114 | fn test_complete_artists_prefix() { |
| 115 | let db = Database::open_in_memory().unwrap(); | 115 | let db = Database::open_in_memory().unwrap(); |
| 116 | seed_test_db(&db); | 116 | seed_test_db(&db); |
| 117 | let results = db.complete_artists("M").unwrap(); | 117 | let results = db.complete("M").unwrap(); |
| 118 | assert_eq!(results.len(), 2); | 118 | assert!(results.contains(&"Metallica/".to_string())); |
| 119 | assert!(results.contains(&"Metallica".to_string())); | 119 | assert!(results.contains(&"Miles Davis/".to_string())); |
| 120 | assert!(results.contains(&"Miles Davis".to_string())); | ||
| 121 | } | 120 | } |
| 122 | 121 | ||
| 123 | #[test] | 122 | #[test] |
| 124 | fn test_complete_artists_prefix_lowercase() { | 123 | fn test_complete_artists_prefix_lowercase() { |
| 125 | let db = Database::open_in_memory().unwrap(); | 124 | let db = Database::open_in_memory().unwrap(); |
| 126 | seed_test_db(&db); | 125 | seed_test_db(&db); |
| 127 | let results = db.complete_artists("m").unwrap(); | 126 | let results = db.complete("m").unwrap(); |
| 128 | assert!( | 127 | assert!( |
| 129 | results.contains(&"Metallica".to_string()), | 128 | results.contains(&"Metallica/".to_string()), |
| 130 | "LIKE should be case-insensitive" | 129 | "LIKE should be case-insensitive" |
| 131 | ); | 130 | ); |
| 132 | } | 131 | } |
| 133 | 132 | ||
| 134 | #[test] | 133 | #[test] |
| 135 | fn test_complete_artists_no_match() { | 134 | fn test_complete_no_match() { |
| 136 | let db = Database::open_in_memory().unwrap(); | 135 | let db = Database::open_in_memory().unwrap(); |
| 137 | seed_test_db(&db); | 136 | seed_test_db(&db); |
| 138 | let results = db.complete_artists("Z").unwrap(); | 137 | let results = db.complete("Z").unwrap(); |
| 139 | assert!(results.is_empty()); | 138 | assert!(results.is_empty()); |
| 140 | } | 139 | } |
| 141 | 140 | ||
| 142 | #[test] | 141 | #[test] |
| 143 | fn test_complete_artists_empty_prefix() { | 142 | fn test_complete_empty_prefix() { |
| 144 | let db = Database::open_in_memory().unwrap(); | 143 | let db = Database::open_in_memory().unwrap(); |
| 145 | seed_test_db(&db); | 144 | seed_test_db(&db); |
| 146 | let results = db.complete_artists("").unwrap(); | 145 | let results = db.complete("").unwrap(); |
| 147 | assert_eq!(results.len(), 3); | 146 | assert_eq!(results.len(), 3); |
| 147 | assert!(results.contains(&"Metallica/".to_string())); | ||
| 148 | assert!(results.contains(&"Miles Davis/".to_string())); | ||
| 149 | assert!(results.contains(&"Aphex Twin/".to_string())); | ||
| 148 | } | 150 | } |
| 149 | 151 | ||
| 150 | #[test] | 152 | #[test] |
| 151 | fn test_complete_albums_with_artist_prefix() { | 153 | fn test_complete_albums_for_artist() { |
| 152 | let db = Database::open_in_memory().unwrap(); | 154 | let db = Database::open_in_memory().unwrap(); |
| 153 | seed_test_db(&db); | 155 | seed_test_db(&db); |
| 154 | let results = db.complete_albums("Metallica/").unwrap(); | 156 | let results = db.complete("Metallica/").unwrap(); |
| 155 | assert_eq!(results.len(), 2); | 157 | assert_eq!(results.len(), 2); |
| 156 | assert!(results.contains(&"Master of Puppets".to_string())); | 158 | assert!(results.contains(&"Metallica/Master of Puppets/".to_string())); |
| 157 | assert!(results.contains(&"Ride the Lightning".to_string())); | 159 | assert!(results.contains(&"Metallica/Ride the Lightning/".to_string())); |
| 160 | } | ||
| 161 | |||
| 162 | #[test] | ||
| 163 | fn test_complete_albums_artist_lowercase() { | ||
| 164 | let db = Database::open_in_memory().unwrap(); | ||
| 165 | seed_test_db(&db); | ||
| 166 | let results = db.complete("metallica/").unwrap(); | ||
| 167 | assert_eq!(results.len(), 2, "artist match should be case-insensitive"); | ||
| 168 | assert!(results.contains(&"metallica/Master of Puppets/".to_string())); | ||
| 158 | } | 169 | } |
| 159 | 170 | ||
| 160 | #[test] | 171 | #[test] |
| 161 | fn test_complete_albums_with_album_prefix() { | 172 | fn test_complete_albums_with_album_prefix() { |
| 162 | let db = Database::open_in_memory().unwrap(); | 173 | let db = Database::open_in_memory().unwrap(); |
| 163 | seed_test_db(&db); | 174 | seed_test_db(&db); |
| 164 | let results = db.complete_albums("Metallica/R").unwrap(); | 175 | // "R" matches "Ride the Lightning" (prefix) and "Master of Puppets" (contains 'r') |
| 165 | assert_eq!(results.len(), 1); | 176 | let results = db.complete("Metallica/R").unwrap(); |
| 166 | assert_eq!(results[0], "Ride the Lightning"); | 177 | assert_eq!(results.len(), 2); |
| 178 | assert_eq!(results[0], "Metallica/Ride the Lightning/"); | ||
| 179 | assert_eq!(results[1], "Metallica/Master of Puppets/"); | ||
| 167 | } | 180 | } |
| 168 | 181 | ||
| 169 | #[test] | 182 | #[test] |
| 170 | fn test_complete_albums_unknown_artist() { | 183 | fn test_complete_albums_substring_match() { |
| 171 | let db = Database::open_in_memory().unwrap(); | 184 | let db = Database::open_in_memory().unwrap(); |
| 172 | seed_test_db(&db); | 185 | seed_test_db(&db); |
| 173 | let results = db.complete_albums("Unknown/").unwrap(); | 186 | // "ind" matches "Kind of Blue" (contains "ind") |
| 174 | assert!(results.is_empty()); | 187 | let results = db.complete("Miles Davis/ind").unwrap(); |
| 188 | assert_eq!(results.len(), 1); | ||
| 189 | assert_eq!(results[0], "Miles Davis/Kind of Blue/"); | ||
| 175 | } | 190 | } |
| 176 | 191 | ||
| 177 | #[test] | 192 | #[test] |
| 178 | fn test_complete_albums_no_slash() { | 193 | fn test_complete_artists_substring_match() { |
| 179 | let db = Database::open_in_memory().unwrap(); | 194 | let db = Database::open_in_memory().unwrap(); |
| 180 | seed_test_db(&db); | 195 | seed_test_db(&db); |
| 181 | let results = db.complete_albums("Metallica").unwrap(); | 196 | // "x" matches "Aphex Twin" (contains 'x') |
| 182 | assert!(results.is_empty(), "needs trailing slash"); | 197 | let results = db.complete("x").unwrap(); |
| 198 | assert!(results.contains(&"Aphex Twin/".to_string())); | ||
| 183 | } | 199 | } |
| 184 | 200 | ||
| 185 | #[test] | 201 | #[test] |
| 186 | fn test_complete_tracks_full_path() { | 202 | fn test_complete_albums_unknown_artist() { |
| 187 | let db = Database::open_in_memory().unwrap(); | 203 | let db = Database::open_in_memory().unwrap(); |
| 188 | seed_test_db(&db); | 204 | seed_test_db(&db); |
| 189 | let results = db.complete_tracks("Metallica/Master of Puppets/").unwrap(); | 205 | let results = db.complete("Unknown/").unwrap(); |
| 190 | assert_eq!(results.len(), 2); | 206 | assert!(results.is_empty()); |
| 191 | assert!(results.contains(&"Battery".to_string())); | ||
| 192 | assert!(results.contains(&"Master of Puppets".to_string())); | ||
| 193 | } | 207 | } |
| 194 | 208 | ||
| 195 | #[test] | 209 | #[test] |
| 196 | fn test_complete_tracks_with_prefix() { | 210 | fn test_complete_tracks_for_album() { |
| 197 | let db = Database::open_in_memory().unwrap(); | 211 | let db = Database::open_in_memory().unwrap(); |
| 198 | seed_test_db(&db); | 212 | seed_test_db(&db); |
| 199 | let results = db.complete_tracks("Metallica/Master of Puppets/B").unwrap(); | 213 | let results = db.complete("Metallica/Master of Puppets/").unwrap(); |
| 200 | assert_eq!(results.len(), 1); | 214 | assert_eq!(results.len(), 2); |
| 201 | assert_eq!(results[0], "Battery"); | 215 | assert!(results.contains(&"Metallica/Master of Puppets/Battery".to_string())); |
| 216 | assert!(results.contains(&"Metallica/Master of Puppets/Master of Puppets".to_string())); | ||
| 202 | } | 217 | } |
| 203 | 218 | ||
| 204 | #[test] | 219 | #[test] |
| 205 | fn test_complete_tracks_unknown_album() { | 220 | fn test_complete_tracks_with_prefix() { |
| 206 | let db = Database::open_in_memory().unwrap(); | 221 | let db = Database::open_in_memory().unwrap(); |
| 207 | seed_test_db(&db); | 222 | seed_test_db(&db); |
| 208 | let results = db.complete_tracks("Metallica/Nonexistent/").unwrap(); | 223 | let results = db.complete("Metallica/Master of Puppets/B").unwrap(); |
| 209 | assert!(results.is_empty()); | 224 | assert_eq!(results.len(), 1); |
| 225 | assert_eq!(results[0], "Metallica/Master of Puppets/Battery"); | ||
| 210 | } | 226 | } |
| 211 | 227 | ||
| 212 | #[test] | 228 | #[test] |
| 213 | fn test_complete_tracks_too_few_parts() { | 229 | fn test_complete_tracks_unknown_album() { |
| 214 | let db = Database::open_in_memory().unwrap(); | 230 | let db = Database::open_in_memory().unwrap(); |
| 215 | seed_test_db(&db); | 231 | seed_test_db(&db); |
| 216 | let results = db.complete_tracks("Metallica").unwrap(); | 232 | let results = db.complete("Metallica/Nonexistent/").unwrap(); |
| 217 | assert!(results.is_empty()); | 233 | assert!(results.is_empty()); |
| 218 | } | 234 | } |
| 219 | 235 | ||
| 220 | #[test] | 236 | #[test] |
| 221 | fn test_complete_empty_db() { | 237 | fn test_complete_empty_db() { |
| 222 | let db = Database::open_in_memory().unwrap(); | 238 | let db = Database::open_in_memory().unwrap(); |
| 223 | assert!(db.complete_artists("").unwrap().is_empty()); | 239 | assert!(db.complete("").unwrap().is_empty()); |
| 224 | assert!(db.complete_albums("x/").unwrap().is_empty()); | 240 | assert!(db.complete("x/").unwrap().is_empty()); |
| 225 | assert!(db.complete_tracks("x/y/").unwrap().is_empty()); | 241 | assert!(db.complete("x/y/").unwrap().is_empty()); |
| 226 | } | 242 | } |
| 227 | 243 | ||
| 228 | // --- find_tracks_by_album tests --- | 244 | // --- find_tracks_by_album tests --- |
| @@ -271,17 +287,18 @@ fn test_find_tracks_by_album_unknown_album() { | |||
| 271 | } | 287 | } |
| 272 | 288 | ||
| 273 | #[test] | 289 | #[test] |
| 274 | fn test_find_tracks_by_album_case_sensitive_artist() { | 290 | fn test_find_tracks_by_album_case_insensitive_artist() { |
| 275 | let db = Database::open_in_memory().unwrap(); | 291 | let db = Database::open_in_memory().unwrap(); |
| 276 | seed_test_db(&db); | 292 | seed_test_db(&db); |
| 277 | // SQLite = is case-sensitive for TEXT (unless NOCASE collation) | ||
| 278 | let tracks = db | 293 | let tracks = db |
| 279 | .find_tracks_by_album("metallica", "Master of Puppets") | 294 | .find_tracks_by_album("metallica", "Master of Puppets") |
| 280 | .unwrap(); | 295 | .unwrap(); |
| 281 | assert!( | 296 | assert_eq!( |
| 282 | tracks.is_empty(), | 297 | tracks.len(), |
| 283 | "artist name match should be case-sensitive" | 298 | 2, |
| 299 | "artist name match should be case-insensitive" | ||
| 284 | ); | 300 | ); |
| 301 | assert_eq!(tracks[0].title, "Battery"); | ||
| 285 | } | 302 | } |
| 286 | 303 | ||
| 287 | #[test] | 304 | #[test] |
| @@ -353,13 +370,14 @@ fn test_find_track_by_parts_no_match_artist() { | |||
| 353 | } | 370 | } |
| 354 | 371 | ||
| 355 | #[test] | 372 | #[test] |
| 356 | fn test_find_track_by_parts_case_sensitive() { | 373 | fn test_find_track_by_parts_case_insensitive() { |
| 357 | let db = Database::open_in_memory().unwrap(); | 374 | let db = Database::open_in_memory().unwrap(); |
| 358 | seed_test_db(&db); | 375 | seed_test_db(&db); |
| 359 | let track = db | 376 | let track = db |
| 360 | .find_track_by_parts("metallica", "Master of Puppets", "Battery") | 377 | .find_track_by_parts("metallica", "Master of Puppets", "Battery") |
| 361 | .unwrap(); | 378 | .unwrap(); |
| 362 | assert!(track.is_none(), "artist match should be case-sensitive"); | 379 | assert!(track.is_some(), "artist match should be case-insensitive"); |
| 380 | assert_eq!(track.unwrap().title, "Battery"); | ||
| 363 | } | 381 | } |
| 364 | 382 | ||
| 365 | // --- resolve_path tests --- | 383 | // --- resolve_path tests --- |
diff --git a/tests/scrobbler.rs b/tests/scrobbler.rs index a4b3fda..237a024 100644 --- a/tests/scrobbler.rs +++ b/tests/scrobbler.rs | |||
| @@ -5,6 +5,8 @@ | |||
| 5 | * https://codeberg.org/salmonik/RSPlayer | 5 | * https://codeberg.org/salmonik/RSPlayer |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | use std::collections::BTreeMap; | ||
| 9 | |||
| 8 | use rsp::scrobbler::Scrobbler; | 10 | use rsp::scrobbler::Scrobbler; |
| 9 | 11 | ||
| 10 | #[test] | 12 | #[test] |
| @@ -80,3 +82,18 @@ fn test_scrobbler_skip_empty_credentials() { | |||
| 80 | scrobbler.now_playing("Artist", "Track", Some("Album")); | 82 | scrobbler.now_playing("Artist", "Track", Some("Album")); |
| 81 | scrobbler.scrobble("Artist", "Track", Some("Album"), 1234567890); | 83 | scrobbler.scrobble("Artist", "Track", Some("Album"), 1234567890); |
| 82 | } | 84 | } |
| 85 | |||
| 86 | #[test] | ||
| 87 | fn test_sign_lastfm() { | ||
| 88 | let params = BTreeMap::from([ | ||
| 89 | ("api_key", "abc123"), | ||
| 90 | ("method", "track.scrobble"), | ||
| 91 | ("sk", "session_key"), | ||
| 92 | ("track", "Test Track"), | ||
| 93 | ("artist", "Test Artist"), | ||
| 94 | ("timestamp", "1234567890"), | ||
| 95 | ]); | ||
| 96 | let sig = Scrobbler::sign_lastfm(¶ms, "sekrit"); | ||
| 97 | assert_eq!(sig.len(), 32); | ||
| 98 | assert!(sig.chars().all(|c| c.is_ascii_hexdigit())); | ||
| 99 | } | ||
