From d347d89dfeef54d648ffdea054897617c0016509 Mon Sep 17 00:00:00 2001 From: salmonik Date: Sat, 6 Jun 2026 19:48:22 +0300 Subject: correctly working auto completion for db --- tests/daemon.rs | 79 +++++++++++++++++++++++++++++++++++++ tests/database.rs | 112 +++++++++++++++++++++++++++++++---------------------- tests/scrobbler.rs | 17 ++++++++ 3 files changed, 161 insertions(+), 47 deletions(-) create mode 100644 tests/daemon.rs (limited to 'tests') 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 @@ +/* + * 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 std::path::PathBuf; + +use rsp::daemon::ScrobbleState; +use rsp::queue::Track; + +#[test] +fn test_scrobble_state_new() { + let s = ScrobbleState::new(); + assert!(s.track.is_none()); + assert!(s.start_time.is_none()); + assert!(s.timestamp.is_none()); +} + +#[test] +fn test_scrobble_state_clear() { + let mut s = ScrobbleState::new(); + let t = Track { + path: PathBuf::from("/test/song.mp3"), + title: Some("Test".into()), + artist: Some("Artist".into()), + album: None, + duration_secs: Some(200.0), + is_stream: false, + }; + s.start(t); + assert!(s.track.is_some()); + s.clear(); + assert!(s.track.is_none()); + assert!(s.start_time.is_none()); + assert!(s.timestamp.is_none()); +} + +#[test] +fn test_should_scrobble_without_track_returns_false() { + assert!(!ScrobbleState::new().should_scrobble()); +} + +#[test] +fn test_should_scrobble_threshold_below_30_seconds() { + assert!(!ScrobbleState::should_scrobble_with_params(29, Some(200.0))); +} + +#[test] +fn test_should_scrobble_threshold_half_duration() { + assert!(ScrobbleState::should_scrobble_with_params(100, Some(200.0))); +} + +#[test] +fn test_should_scrobble_threshold_below_half_duration() { + assert!(!ScrobbleState::should_scrobble_with_params(80, Some(200.0))); +} + +#[test] +fn test_should_scrobble_threshold_capped_at_4min() { + assert!(ScrobbleState::should_scrobble_with_params(240, Some(600.0))); + assert!(!ScrobbleState::should_scrobble_with_params( + 239, + Some(600.0) + )); +} + +#[test] +fn test_should_scrobble_no_duration_requires_4min() { + assert!(ScrobbleState::should_scrobble_with_params(240, None)); + assert!(!ScrobbleState::should_scrobble_with_params(239, None)); +} + +#[test] +fn test_should_scrobble_short_song() { + assert!(ScrobbleState::should_scrobble_with_params(30, Some(60.0))); + assert!(!ScrobbleState::should_scrobble_with_params(15, Some(60.0))); +} 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) { fn test_complete_artists_prefix() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); - let results = db.complete_artists("M").unwrap(); - assert_eq!(results.len(), 2); - assert!(results.contains(&"Metallica".to_string())); - assert!(results.contains(&"Miles Davis".to_string())); + 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_artists("m").unwrap(); + let results = db.complete("m").unwrap(); assert!( - results.contains(&"Metallica".to_string()), + results.contains(&"Metallica/".to_string()), "LIKE should be case-insensitive" ); } #[test] -fn test_complete_artists_no_match() { +fn test_complete_no_match() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); - let results = db.complete_artists("Z").unwrap(); + let results = db.complete("Z").unwrap(); assert!(results.is_empty()); } #[test] -fn test_complete_artists_empty_prefix() { +fn test_complete_empty_prefix() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); - let results = db.complete_artists("").unwrap(); + 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_with_artist_prefix() { +fn test_complete_albums_for_artist() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); - let results = db.complete_albums("Metallica/").unwrap(); + let results = db.complete("Metallica/").unwrap(); assert_eq!(results.len(), 2); - assert!(results.contains(&"Master of Puppets".to_string())); - assert!(results.contains(&"Ride the Lightning".to_string())); + 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); - let results = db.complete_albums("Metallica/R").unwrap(); - assert_eq!(results.len(), 1); - assert_eq!(results[0], "Ride the Lightning"); + // "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_unknown_artist() { +fn test_complete_albums_substring_match() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); - let results = db.complete_albums("Unknown/").unwrap(); - assert!(results.is_empty()); + // "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_albums_no_slash() { +fn test_complete_artists_substring_match() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); - let results = db.complete_albums("Metallica").unwrap(); - assert!(results.is_empty(), "needs trailing slash"); + // "x" matches "Aphex Twin" (contains 'x') + let results = db.complete("x").unwrap(); + assert!(results.contains(&"Aphex Twin/".to_string())); } #[test] -fn test_complete_tracks_full_path() { +fn test_complete_albums_unknown_artist() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); - let results = db.complete_tracks("Metallica/Master of Puppets/").unwrap(); - assert_eq!(results.len(), 2); - assert!(results.contains(&"Battery".to_string())); - assert!(results.contains(&"Master of Puppets".to_string())); + let results = db.complete("Unknown/").unwrap(); + assert!(results.is_empty()); } #[test] -fn test_complete_tracks_with_prefix() { +fn test_complete_tracks_for_album() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); - let results = db.complete_tracks("Metallica/Master of Puppets/B").unwrap(); - assert_eq!(results.len(), 1); - assert_eq!(results[0], "Battery"); + 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_unknown_album() { +fn test_complete_tracks_with_prefix() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); - let results = db.complete_tracks("Metallica/Nonexistent/").unwrap(); - assert!(results.is_empty()); + 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_too_few_parts() { +fn test_complete_tracks_unknown_album() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); - let results = db.complete_tracks("Metallica").unwrap(); + 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_artists("").unwrap().is_empty()); - assert!(db.complete_albums("x/").unwrap().is_empty()); - assert!(db.complete_tracks("x/y/").unwrap().is_empty()); + 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 --- @@ -271,17 +287,18 @@ fn test_find_tracks_by_album_unknown_album() { } #[test] -fn test_find_tracks_by_album_case_sensitive_artist() { +fn test_find_tracks_by_album_case_insensitive_artist() { let db = Database::open_in_memory().unwrap(); seed_test_db(&db); - // SQLite = is case-sensitive for TEXT (unless NOCASE collation) let tracks = db .find_tracks_by_album("metallica", "Master of Puppets") .unwrap(); - assert!( - tracks.is_empty(), - "artist name match should be case-sensitive" + assert_eq!( + tracks.len(), + 2, + "artist name match should be case-insensitive" ); + assert_eq!(tracks[0].title, "Battery"); } #[test] @@ -353,13 +370,14 @@ fn test_find_track_by_parts_no_match_artist() { } #[test] -fn test_find_track_by_parts_case_sensitive() { +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_none(), "artist match should be case-sensitive"); + assert!(track.is_some(), "artist match should be case-insensitive"); + assert_eq!(track.unwrap().title, "Battery"); } // --- 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 @@ * https://codeberg.org/salmonik/RSPlayer */ +use std::collections::BTreeMap; + use rsp::scrobbler::Scrobbler; #[test] @@ -80,3 +82,18 @@ fn test_scrobbler_skip_empty_credentials() { scrobbler.now_playing("Artist", "Track", Some("Album")); scrobbler.scrobble("Artist", "Track", Some("Album"), 1234567890); } + +#[test] +fn test_sign_lastfm() { + let params = BTreeMap::from([ + ("api_key", "abc123"), + ("method", "track.scrobble"), + ("sk", "session_key"), + ("track", "Test Track"), + ("artist", "Test Artist"), + ("timestamp", "1234567890"), + ]); + let sig = Scrobbler::sign_lastfm(¶ms, "sekrit"); + assert_eq!(sig.len(), 32); + assert!(sig.chars().all(|c| c.is_ascii_hexdigit())); +} -- cgit v1.2.3