diff options
Diffstat (limited to '')
| -rw-r--r-- | tests/database.rs | 411 |
1 files changed, 410 insertions, 1 deletions
diff --git a/tests/database.rs b/tests/database.rs index 6cd870e..84eb663 100644 --- a/tests/database.rs +++ b/tests/database.rs | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | * https://codeberg.org/salmonik/RSPlayer | 5 | * https://codeberg.org/salmonik/RSPlayer |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | use rsp::database::{extract_metadata, is_audio_file}; | 8 | use rsp::database::{Database, extract_metadata, is_audio_file}; |
| 9 | use std::path::PathBuf; | 9 | use std::path::PathBuf; |
| 10 | 10 | ||
| 11 | #[test] | 11 | #[test] |
| @@ -90,3 +90,412 @@ fn test_extract_metadata_no_parent() { | |||
| 90 | assert_eq!(album, "Unknown"); | 90 | assert_eq!(album, "Unknown"); |
| 91 | assert_eq!(title, "song"); | 91 | assert_eq!(title, "song"); |
| 92 | } | 92 | } |
| 93 | |||
| 94 | fn seed_test_db(db: &Database) { | ||
| 95 | let conn: &rusqlite::Connection = db.conn(); | ||
| 96 | conn.execute_batch( | ||
| 97 | "INSERT INTO artists (id, name) VALUES (1, 'Metallica'), (2, 'Miles Davis'), (3, 'Aphex Twin'); | ||
| 98 | INSERT INTO albums (id, artist_id, title) VALUES | ||
| 99 | (1, 1, 'Master of Puppets'), | ||
| 100 | (2, 1, 'Ride the Lightning'), | ||
| 101 | (3, 2, 'Kind of Blue'), | ||
| 102 | (4, 3, 'Selected Ambient Works'); | ||
| 103 | INSERT INTO tracks (id, album_id, title, path, duration, track_number) VALUES | ||
| 104 | (1, 1, 'Battery', '/m/Metallica/Master of Puppets/01-Battery.mp3', 313, 1), | ||
| 105 | (2, 1, 'Master of Puppets', '/m/Metallica/Master of Puppets/02-Master of Puppets.mp3', 515, 2), | ||
| 106 | (3, 2, 'Fight Fire with Fire', '/m/Metallica/Ride the Lightning/01-Fight Fire with Fire.mp3', 285, 1), | ||
| 107 | (4, 3, 'So What', '/m/Miles Davis/Kind of Blue/01-So What.mp3', 562, 1), | ||
| 108 | (5, 4, 'Xtal', '/m/Aphex Twin/Selected Ambient Works/01-Xtal.mp3', 437, 1);", | ||
| 109 | ) | ||
| 110 | .unwrap(); | ||
| 111 | } | ||
| 112 | |||
| 113 | #[test] | ||
| 114 | fn test_complete_artists_prefix() { | ||
| 115 | let db = Database::open_in_memory().unwrap(); | ||
| 116 | seed_test_db(&db); | ||
| 117 | let results = db.complete("M").unwrap(); | ||
| 118 | assert!(results.contains(&"Metallica/".to_string())); | ||
| 119 | assert!(results.contains(&"Miles Davis/".to_string())); | ||
| 120 | } | ||
| 121 | |||
| 122 | #[test] | ||
| 123 | fn test_complete_artists_prefix_lowercase() { | ||
| 124 | let db = Database::open_in_memory().unwrap(); | ||
| 125 | seed_test_db(&db); | ||
| 126 | let results = db.complete("m").unwrap(); | ||
| 127 | assert!( | ||
| 128 | results.contains(&"Metallica/".to_string()), | ||
| 129 | "LIKE should be case-insensitive" | ||
| 130 | ); | ||
| 131 | } | ||
| 132 | |||
| 133 | #[test] | ||
| 134 | fn test_complete_no_match() { | ||
| 135 | let db = Database::open_in_memory().unwrap(); | ||
| 136 | seed_test_db(&db); | ||
| 137 | let results = db.complete("Z").unwrap(); | ||
| 138 | assert!(results.is_empty()); | ||
| 139 | } | ||
| 140 | |||
| 141 | #[test] | ||
| 142 | fn test_complete_empty_prefix() { | ||
| 143 | let db = Database::open_in_memory().unwrap(); | ||
| 144 | seed_test_db(&db); | ||
| 145 | let results = db.complete("").unwrap(); | ||
| 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())); | ||
| 150 | } | ||
| 151 | |||
| 152 | #[test] | ||
| 153 | fn test_complete_albums_for_artist() { | ||
| 154 | let db = Database::open_in_memory().unwrap(); | ||
| 155 | seed_test_db(&db); | ||
| 156 | let results = db.complete("Metallica/").unwrap(); | ||
| 157 | assert_eq!(results.len(), 2); | ||
| 158 | assert!(results.contains(&"Metallica/Master of Puppets/".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())); | ||
| 169 | } | ||
| 170 | |||
| 171 | #[test] | ||
| 172 | fn test_complete_albums_with_album_prefix() { | ||
| 173 | let db = Database::open_in_memory().unwrap(); | ||
| 174 | seed_test_db(&db); | ||
| 175 | // "R" matches "Ride the Lightning" (prefix) and "Master of Puppets" (contains 'r') | ||
| 176 | let results = db.complete("Metallica/R").unwrap(); | ||
| 177 | assert_eq!(results.len(), 2); | ||
| 178 | assert_eq!(results[0], "Metallica/Ride the Lightning/"); | ||
| 179 | assert_eq!(results[1], "Metallica/Master of Puppets/"); | ||
| 180 | } | ||
| 181 | |||
| 182 | #[test] | ||
| 183 | fn test_complete_albums_substring_match() { | ||
| 184 | let db = Database::open_in_memory().unwrap(); | ||
| 185 | seed_test_db(&db); | ||
| 186 | // "ind" matches "Kind of Blue" (contains "ind") | ||
| 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/"); | ||
| 190 | } | ||
| 191 | |||
| 192 | #[test] | ||
| 193 | fn test_complete_artists_substring_match() { | ||
| 194 | let db = Database::open_in_memory().unwrap(); | ||
| 195 | seed_test_db(&db); | ||
| 196 | // "x" matches "Aphex Twin" (contains 'x') | ||
| 197 | let results = db.complete("x").unwrap(); | ||
| 198 | assert!(results.contains(&"Aphex Twin/".to_string())); | ||
| 199 | } | ||
| 200 | |||
| 201 | #[test] | ||
| 202 | fn test_complete_albums_unknown_artist() { | ||
| 203 | let db = Database::open_in_memory().unwrap(); | ||
| 204 | seed_test_db(&db); | ||
| 205 | let results = db.complete("Unknown/").unwrap(); | ||
| 206 | assert!(results.is_empty()); | ||
| 207 | } | ||
| 208 | |||
| 209 | #[test] | ||
| 210 | fn test_complete_tracks_for_album() { | ||
| 211 | let db = Database::open_in_memory().unwrap(); | ||
| 212 | seed_test_db(&db); | ||
| 213 | let results = db.complete("Metallica/Master of Puppets/").unwrap(); | ||
| 214 | assert_eq!(results.len(), 2); | ||
| 215 | assert!(results.contains(&"Metallica/Master of Puppets/Battery".to_string())); | ||
| 216 | assert!(results.contains(&"Metallica/Master of Puppets/Master of Puppets".to_string())); | ||
| 217 | } | ||
| 218 | |||
| 219 | #[test] | ||
| 220 | fn test_complete_tracks_with_prefix() { | ||
| 221 | let db = Database::open_in_memory().unwrap(); | ||
| 222 | seed_test_db(&db); | ||
| 223 | let results = db.complete("Metallica/Master of Puppets/B").unwrap(); | ||
| 224 | assert_eq!(results.len(), 1); | ||
| 225 | assert_eq!(results[0], "Metallica/Master of Puppets/Battery"); | ||
| 226 | } | ||
| 227 | |||
| 228 | #[test] | ||
| 229 | fn test_complete_tracks_unknown_album() { | ||
| 230 | let db = Database::open_in_memory().unwrap(); | ||
| 231 | seed_test_db(&db); | ||
| 232 | let results = db.complete("Metallica/Nonexistent/").unwrap(); | ||
| 233 | assert!(results.is_empty()); | ||
| 234 | } | ||
| 235 | |||
| 236 | #[test] | ||
| 237 | fn test_complete_empty_db() { | ||
| 238 | let db = Database::open_in_memory().unwrap(); | ||
| 239 | assert!(db.complete("").unwrap().is_empty()); | ||
| 240 | assert!(db.complete("x/").unwrap().is_empty()); | ||
| 241 | assert!(db.complete("x/y/").unwrap().is_empty()); | ||
| 242 | } | ||
| 243 | |||
| 244 | // --- find_tracks_by_album tests --- | ||
| 245 | |||
| 246 | #[test] | ||
| 247 | fn test_find_tracks_by_album_all() { | ||
| 248 | let db = Database::open_in_memory().unwrap(); | ||
| 249 | seed_test_db(&db); | ||
| 250 | let tracks = db | ||
| 251 | .find_tracks_by_album("Metallica", "Master of Puppets") | ||
| 252 | .unwrap(); | ||
| 253 | assert_eq!(tracks.len(), 2); | ||
| 254 | assert_eq!(tracks[0].title, "Battery"); | ||
| 255 | assert_eq!(tracks[1].title, "Master of Puppets"); | ||
| 256 | assert_eq!(tracks[0].artist_name.as_deref(), Some("Metallica")); | ||
| 257 | assert_eq!(tracks[0].album_title.as_deref(), Some("Master of Puppets")); | ||
| 258 | } | ||
| 259 | |||
| 260 | #[test] | ||
| 261 | fn test_find_tracks_by_album_single_track() { | ||
| 262 | let db = Database::open_in_memory().unwrap(); | ||
| 263 | seed_test_db(&db); | ||
| 264 | let tracks = db | ||
| 265 | .find_tracks_by_album("Miles Davis", "Kind of Blue") | ||
| 266 | .unwrap(); | ||
| 267 | assert_eq!(tracks.len(), 1); | ||
| 268 | assert_eq!(tracks[0].title, "So What"); | ||
| 269 | } | ||
| 270 | |||
| 271 | #[test] | ||
| 272 | fn test_find_tracks_by_album_unknown_artist() { | ||
| 273 | let db = Database::open_in_memory().unwrap(); | ||
| 274 | seed_test_db(&db); | ||
| 275 | let tracks = db | ||
| 276 | .find_tracks_by_album("Nonexistent", "Master of Puppets") | ||
| 277 | .unwrap(); | ||
| 278 | assert!(tracks.is_empty()); | ||
| 279 | } | ||
| 280 | |||
| 281 | #[test] | ||
| 282 | fn test_find_tracks_by_album_unknown_album() { | ||
| 283 | let db = Database::open_in_memory().unwrap(); | ||
| 284 | seed_test_db(&db); | ||
| 285 | let tracks = db.find_tracks_by_album("Metallica", "Nonexistent").unwrap(); | ||
| 286 | assert!(tracks.is_empty()); | ||
| 287 | } | ||
| 288 | |||
| 289 | #[test] | ||
| 290 | fn test_find_tracks_by_album_case_insensitive_artist() { | ||
| 291 | let db = Database::open_in_memory().unwrap(); | ||
| 292 | seed_test_db(&db); | ||
| 293 | let tracks = db | ||
| 294 | .find_tracks_by_album("metallica", "Master of Puppets") | ||
| 295 | .unwrap(); | ||
| 296 | assert_eq!( | ||
| 297 | tracks.len(), | ||
| 298 | 2, | ||
| 299 | "artist name match should be case-insensitive" | ||
| 300 | ); | ||
| 301 | assert_eq!(tracks[0].title, "Battery"); | ||
| 302 | } | ||
| 303 | |||
| 304 | #[test] | ||
| 305 | fn test_find_tracks_by_album_ordered_by_track_number() { | ||
| 306 | let db = Database::open_in_memory().unwrap(); | ||
| 307 | seed_test_db(&db); | ||
| 308 | let tracks = db | ||
| 309 | .find_tracks_by_album("Metallica", "Master of Puppets") | ||
| 310 | .unwrap(); | ||
| 311 | assert_eq!(tracks[0].track_number, Some(1)); | ||
| 312 | assert_eq!(tracks[1].track_number, Some(2)); | ||
| 313 | } | ||
| 314 | |||
| 315 | // --- find_track_by_parts tests --- | ||
| 316 | |||
| 317 | #[test] | ||
| 318 | fn test_find_track_by_parts_exact() { | ||
| 319 | let db = Database::open_in_memory().unwrap(); | ||
| 320 | seed_test_db(&db); | ||
| 321 | let track = db | ||
| 322 | .find_track_by_parts("Metallica", "Master of Puppets", "Battery") | ||
| 323 | .unwrap(); | ||
| 324 | assert!(track.is_some()); | ||
| 325 | let t = track.unwrap(); | ||
| 326 | assert_eq!(t.title, "Battery"); | ||
| 327 | assert_eq!(t.artist_name.as_deref(), Some("Metallica")); | ||
| 328 | assert_eq!(t.album_title.as_deref(), Some("Master of Puppets")); | ||
| 329 | } | ||
| 330 | |||
| 331 | #[test] | ||
| 332 | fn test_find_track_by_parts_prefix() { | ||
| 333 | let db = Database::open_in_memory().unwrap(); | ||
| 334 | seed_test_db(&db); | ||
| 335 | let track = db | ||
| 336 | .find_track_by_parts("Metallica", "Master of Puppets", "B") | ||
| 337 | .unwrap(); | ||
| 338 | assert!(track.is_some()); | ||
| 339 | assert_eq!(track.unwrap().title, "Battery"); | ||
| 340 | } | ||
| 341 | |||
| 342 | #[test] | ||
| 343 | fn test_find_track_by_parts_no_match_track() { | ||
| 344 | let db = Database::open_in_memory().unwrap(); | ||
| 345 | seed_test_db(&db); | ||
| 346 | let track = db | ||
| 347 | .find_track_by_parts("Metallica", "Master of Puppets", "Z") | ||
| 348 | .unwrap(); | ||
| 349 | assert!(track.is_none()); | ||
| 350 | } | ||
| 351 | |||
| 352 | #[test] | ||
| 353 | fn test_find_track_by_parts_no_match_album() { | ||
| 354 | let db = Database::open_in_memory().unwrap(); | ||
| 355 | seed_test_db(&db); | ||
| 356 | let track = db | ||
| 357 | .find_track_by_parts("Metallica", "Nonexistent", "B") | ||
| 358 | .unwrap(); | ||
| 359 | assert!(track.is_none()); | ||
| 360 | } | ||
| 361 | |||
| 362 | #[test] | ||
| 363 | fn test_find_track_by_parts_no_match_artist() { | ||
| 364 | let db = Database::open_in_memory().unwrap(); | ||
| 365 | seed_test_db(&db); | ||
| 366 | let track = db | ||
| 367 | .find_track_by_parts("Nonexistent", "Master of Puppets", "B") | ||
| 368 | .unwrap(); | ||
| 369 | assert!(track.is_none()); | ||
| 370 | } | ||
| 371 | |||
| 372 | #[test] | ||
| 373 | fn test_find_track_by_parts_case_insensitive() { | ||
| 374 | let db = Database::open_in_memory().unwrap(); | ||
| 375 | seed_test_db(&db); | ||
| 376 | let track = db | ||
| 377 | .find_track_by_parts("metallica", "Master of Puppets", "Battery") | ||
| 378 | .unwrap(); | ||
| 379 | assert!(track.is_some(), "artist match should be case-insensitive"); | ||
| 380 | assert_eq!(track.unwrap().title, "Battery"); | ||
| 381 | } | ||
| 382 | |||
| 383 | // --- resolve_path tests --- | ||
| 384 | |||
| 385 | #[test] | ||
| 386 | fn test_resolve_path_two_parts_album() { | ||
| 387 | let db = Database::open_in_memory().unwrap(); | ||
| 388 | seed_test_db(&db); | ||
| 389 | let result = db.resolve_path("Metallica/Master of Puppets").unwrap(); | ||
| 390 | assert!(result.is_some()); | ||
| 391 | let tracks = result.unwrap(); | ||
| 392 | assert_eq!(tracks.len(), 2); | ||
| 393 | assert_eq!(tracks[0].title, "Battery"); | ||
| 394 | } | ||
| 395 | |||
| 396 | #[test] | ||
| 397 | fn test_resolve_path_two_parts_trailing_slash() { | ||
| 398 | let db = Database::open_in_memory().unwrap(); | ||
| 399 | seed_test_db(&db); | ||
| 400 | let result = db.resolve_path("Metallica/Master of Puppets/").unwrap(); | ||
| 401 | assert!(result.is_some()); | ||
| 402 | let tracks = result.unwrap(); | ||
| 403 | assert_eq!(tracks.len(), 2); | ||
| 404 | } | ||
| 405 | |||
| 406 | #[test] | ||
| 407 | fn test_resolve_path_three_parts_track() { | ||
| 408 | let db = Database::open_in_memory().unwrap(); | ||
| 409 | seed_test_db(&db); | ||
| 410 | let result = db | ||
| 411 | .resolve_path("Metallica/Master of Puppets/Battery") | ||
| 412 | .unwrap(); | ||
| 413 | assert!(result.is_some()); | ||
| 414 | let tracks = result.unwrap(); | ||
| 415 | assert_eq!(tracks.len(), 1); | ||
| 416 | assert_eq!(tracks[0].title, "Battery"); | ||
| 417 | } | ||
| 418 | |||
| 419 | #[test] | ||
| 420 | fn test_resolve_path_three_parts_track_prefix() { | ||
| 421 | let db = Database::open_in_memory().unwrap(); | ||
| 422 | seed_test_db(&db); | ||
| 423 | let result = db.resolve_path("Metallica/Master of Puppets/B").unwrap(); | ||
| 424 | assert!(result.is_some()); | ||
| 425 | let tracks = result.unwrap(); | ||
| 426 | assert_eq!(tracks.len(), 1); | ||
| 427 | assert_eq!(tracks[0].title, "Battery"); | ||
| 428 | } | ||
| 429 | |||
| 430 | #[test] | ||
| 431 | fn test_resolve_path_single_part_returns_none() { | ||
| 432 | let db = Database::open_in_memory().unwrap(); | ||
| 433 | seed_test_db(&db); | ||
| 434 | let result = db.resolve_path("Metallica").unwrap(); | ||
| 435 | assert!( | ||
| 436 | result.is_none(), | ||
| 437 | "single part should return None for fallback" | ||
| 438 | ); | ||
| 439 | } | ||
| 440 | |||
| 441 | #[test] | ||
| 442 | fn test_resolve_path_no_match_album() { | ||
| 443 | let db = Database::open_in_memory().unwrap(); | ||
| 444 | seed_test_db(&db); | ||
| 445 | let result = db.resolve_path("Metallica/Nonexistent").unwrap(); | ||
| 446 | assert!(result.is_some()); | ||
| 447 | assert!(result.unwrap().is_empty()); | ||
| 448 | } | ||
| 449 | |||
| 450 | #[test] | ||
| 451 | fn test_resolve_path_no_match_track() { | ||
| 452 | let db = Database::open_in_memory().unwrap(); | ||
| 453 | seed_test_db(&db); | ||
| 454 | let result = db.resolve_path("Metallica/Master of Puppets/Z").unwrap(); | ||
| 455 | assert!(result.is_some()); | ||
| 456 | assert!(result.unwrap().is_empty()); | ||
| 457 | } | ||
| 458 | |||
| 459 | #[test] | ||
| 460 | fn test_resolve_path_empty_db() { | ||
| 461 | let db = Database::open_in_memory().unwrap(); | ||
| 462 | assert!(db.resolve_path("Artist/Album").unwrap().unwrap().is_empty()); | ||
| 463 | assert!( | ||
| 464 | db.resolve_path("Artist/Album/Track") | ||
| 465 | .unwrap() | ||
| 466 | .unwrap() | ||
| 467 | .is_empty() | ||
| 468 | ); | ||
| 469 | assert!(db.resolve_path("Artist").unwrap().is_none()); | ||
| 470 | } | ||
| 471 | |||
| 472 | // --- Track conversion helper tests --- | ||
| 473 | |||
| 474 | #[test] | ||
| 475 | fn test_find_tracks_by_album_returns_full_metadata() { | ||
| 476 | let db = Database::open_in_memory().unwrap(); | ||
| 477 | seed_test_db(&db); | ||
| 478 | let tracks = db | ||
| 479 | .find_tracks_by_album("Metallica", "Ride the Lightning") | ||
| 480 | .unwrap(); | ||
| 481 | assert_eq!(tracks.len(), 1); | ||
| 482 | let t = &tracks[0]; | ||
| 483 | assert_eq!(t.artist_name.as_deref(), Some("Metallica")); | ||
| 484 | assert_eq!(t.album_title.as_deref(), Some("Ride the Lightning")); | ||
| 485 | assert_eq!(t.title, "Fight Fire with Fire"); | ||
| 486 | assert!(t.path.to_string_lossy().contains("Ride the Lightning")); | ||
| 487 | } | ||
| 488 | |||
| 489 | #[test] | ||
| 490 | fn test_find_track_by_parts_returns_full_metadata() { | ||
| 491 | let db = Database::open_in_memory().unwrap(); | ||
| 492 | seed_test_db(&db); | ||
| 493 | let t = db | ||
| 494 | .find_track_by_parts("Aphex Twin", "Selected Ambient Works", "Xtal") | ||
| 495 | .unwrap() | ||
| 496 | .expect("track should be found"); | ||
| 497 | assert_eq!(t.artist_name.as_deref(), Some("Aphex Twin")); | ||
| 498 | assert_eq!(t.album_title.as_deref(), Some("Selected Ambient Works")); | ||
| 499 | assert_eq!(t.title, "Xtal"); | ||
| 500 | assert_eq!(t.track_number, Some(1)); | ||
| 501 | } | ||
