Import existing LinTunes project
Snapshot of the existing codebase before working through the TASKS.md backlog. Real library data (data/) and the iTunes import fixture (itunes-test-library/) are gitignored. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
105
tests/test_round3.py
Normal file
105
tests/test_round3.py
Normal file
@ -0,0 +1,105 @@
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from lintunes.lastfm import sign, LastFm
|
||||
from lintunes.player import make_shuffle_order
|
||||
from lintunes.preferences import Preferences
|
||||
|
||||
|
||||
class TestLastFmSignature:
|
||||
def test_signature_matches_hand_computed(self):
|
||||
params = {
|
||||
"method": "auth.getMobileSession",
|
||||
"api_key": "KEY",
|
||||
"username": "trav",
|
||||
"password": "hunter2",
|
||||
"format": "json", # must be excluded from the signature
|
||||
}
|
||||
expected = hashlib.md5(
|
||||
("api_keyKEY"
|
||||
"methodauth.getMobileSession"
|
||||
"passwordhunter2"
|
||||
"usernametrav"
|
||||
"SECRET").encode()).hexdigest()
|
||||
assert sign(params, "SECRET") == expected
|
||||
|
||||
def test_format_and_callback_excluded(self):
|
||||
base = {"method": "x", "api_key": "k"}
|
||||
with_extras = dict(base, format="json", callback="cb")
|
||||
assert sign(base, "s") == sign(with_extras, "s")
|
||||
|
||||
|
||||
class TestScrobbleQueue:
|
||||
def test_queue_round_trip(self, qapp, tmp_path):
|
||||
prefs = Preferences(tmp_path)
|
||||
lastfm = LastFm(prefs, tmp_path)
|
||||
entry = {"artist": "A", "track": "T", "album": "L",
|
||||
"timestamp": 123, "duration": 180}
|
||||
lastfm._enqueue(entry)
|
||||
assert lastfm._load_queue() == [entry]
|
||||
lastfm._save_queue([])
|
||||
assert lastfm._load_queue() == []
|
||||
assert not (tmp_path / "scrobble_queue.json").exists()
|
||||
|
||||
def test_queue_capped(self, qapp, tmp_path):
|
||||
prefs = Preferences(tmp_path)
|
||||
lastfm = LastFm(prefs, tmp_path)
|
||||
for i in range(600):
|
||||
lastfm._enqueue({"artist": "A", "track": str(i), "timestamp": i})
|
||||
queue = lastfm._load_queue()
|
||||
assert len(queue) == 500
|
||||
assert queue[-1]["track"] == "599"
|
||||
|
||||
|
||||
class TestShuffleOrder:
|
||||
def test_permutation_starts_at_current(self):
|
||||
order = make_shuffle_order(10, 4)
|
||||
assert order[0] == 4
|
||||
assert sorted(order) == list(range(10))
|
||||
|
||||
def test_single_track(self):
|
||||
assert make_shuffle_order(1, 0) == [0]
|
||||
|
||||
def test_empty(self):
|
||||
assert make_shuffle_order(0, 0) == []
|
||||
|
||||
def test_is_randomized(self):
|
||||
# With 20 tracks, two consecutive shuffles colliding entirely is
|
||||
# vanishingly unlikely (1/19!)
|
||||
orders = {tuple(make_shuffle_order(20, 0)) for _ in range(5)}
|
||||
assert len(orders) > 1
|
||||
|
||||
|
||||
class TestPreferences:
|
||||
def test_defaults(self, qapp, tmp_path):
|
||||
prefs = Preferences(tmp_path)
|
||||
assert prefs.get("ui_scale") == "medium"
|
||||
assert prefs.get("highlight") == "blue"
|
||||
assert prefs.lastfm["scrobble_enabled"] is False
|
||||
|
||||
def test_round_trip(self, qapp, tmp_path):
|
||||
prefs = Preferences(tmp_path)
|
||||
prefs.set("ui_scale", "large")
|
||||
prefs.update_lastfm(username="trav", session_key="abc")
|
||||
|
||||
reloaded = Preferences(tmp_path)
|
||||
assert reloaded.get("ui_scale") == "large"
|
||||
assert reloaded.lastfm["username"] == "trav"
|
||||
assert reloaded.lastfm["session_key"] == "abc"
|
||||
# Unknown/missing keys fall back to defaults
|
||||
assert reloaded.get("highlight") == "blue"
|
||||
|
||||
def test_changed_signal(self, qapp, tmp_path):
|
||||
prefs = Preferences(tmp_path)
|
||||
fired = []
|
||||
prefs.changed.connect(lambda: fired.append(1))
|
||||
prefs.set("highlight", "seafoam")
|
||||
prefs.set("highlight", "seafoam") # no-op, shouldn't fire again
|
||||
assert len(fired) == 1
|
||||
|
||||
def test_corrupt_file_falls_back(self, qapp, tmp_path):
|
||||
(tmp_path / "preferences.json").write_text("{not json")
|
||||
prefs = Preferences(tmp_path)
|
||||
assert prefs.get("ui_scale") == "medium"
|
||||
Reference in New Issue
Block a user