From 2f19af779b39a3a109847fc7bc359384e2860170 Mon Sep 17 00:00:00 2001 From: JohnBaumb <80135794+JohnBaumb@users.noreply.github.com> Date: Sun, 19 Apr 2026 14:35:37 -0700 Subject: [PATCH] test: wishlist SQL pagination and category filter Covers LIMIT/OFFSET paging, album/singles category filtering (including EP/compilation/missing album_type edge cases), COUNT with and without category, profile isolation, backward-compat no-args behavior for existing callers, and date_added ordering. --- tests/test_wishlist_pagination.py | 131 ++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 tests/test_wishlist_pagination.py diff --git a/tests/test_wishlist_pagination.py b/tests/test_wishlist_pagination.py new file mode 100644 index 00000000..7543db4b --- /dev/null +++ b/tests/test_wishlist_pagination.py @@ -0,0 +1,131 @@ +"""Unit tests for SQL-level wishlist pagination and category filtering. + +Fix 1.4: the API endpoint previously loaded the entire wishlist, filtered +by category in Python, then sliced for the requested page. Pagination and +category filtering are now pushed to SQL via LIMIT/OFFSET and json_extract. +""" + +import json + +import pytest + +from database.music_database import MusicDatabase + + +@pytest.fixture +def db(tmp_path): + return MusicDatabase(str(tmp_path / "music.db")) + + +def _insert(db, spotify_track_id, name, album_type, profile_id=1, date_added=None): + spotify_data = { + "id": spotify_track_id, + "name": name, + "artists": [{"name": "Test Artist"}], + "album": {"name": f"Album for {name}", "album_type": album_type, "images": []}, + } + date_added = date_added or f"2026-01-{len(spotify_track_id):02d}T00:00:00" + with db._get_connection() as conn: + conn.execute( + """INSERT INTO wishlist_tracks + (spotify_track_id, spotify_data, failure_reason, retry_count, + date_added, source_type, source_info, profile_id) + VALUES (?, ?, '', 0, ?, 'test', '{}', ?)""", + (spotify_track_id, json.dumps(spotify_data), date_added, profile_id), + ) + conn.commit() + + +def test_pagination_limit_offset(db): + for i in range(5): + _insert(db, f"t{i}", f"Track {i}", "album", date_added=f"2026-01-0{i+1}T00:00:00") + + page1 = db.get_wishlist_tracks(limit=2, offset=0) + page2 = db.get_wishlist_tracks(limit=2, offset=2) + page3 = db.get_wishlist_tracks(limit=2, offset=4) + + assert [t["spotify_track_id"] for t in page1] == ["t0", "t1"] + assert [t["spotify_track_id"] for t in page2] == ["t2", "t3"] + assert [t["spotify_track_id"] for t in page3] == ["t4"] + + +def test_category_filter_albums(db): + _insert(db, "a1", "Album Track 1", "album", date_added="2026-01-01T00:00:00") + _insert(db, "s1", "Single 1", "single", date_added="2026-01-02T00:00:00") + _insert(db, "a2", "Album Track 2", "album", date_added="2026-01-03T00:00:00") + _insert(db, "e1", "EP Track", "ep", date_added="2026-01-04T00:00:00") + + albums = db.get_wishlist_tracks(category="albums") + assert sorted(t["spotify_track_id"] for t in albums) == ["a1", "a2"] + + +def test_category_filter_singles_includes_non_album_types(db): + _insert(db, "a1", "Album", "album", date_added="2026-01-01T00:00:00") + _insert(db, "s1", "Single", "single", date_added="2026-01-02T00:00:00") + _insert(db, "e1", "EP", "ep", date_added="2026-01-03T00:00:00") + _insert(db, "c1", "Compilation", "compilation", date_added="2026-01-04T00:00:00") + + singles = db.get_wishlist_tracks(category="singles") + assert sorted(t["spotify_track_id"] for t in singles) == ["c1", "e1", "s1"] + + +def test_category_filter_singles_includes_missing_album_type(db): + # Manually insert a row whose album has no album_type (malformed) + with db._get_connection() as conn: + conn.execute( + """INSERT INTO wishlist_tracks + (spotify_track_id, spotify_data, failure_reason, retry_count, + date_added, source_type, source_info, profile_id) + VALUES ('x1', ?, '', 0, '2026-01-01', 'test', '{}', 1)""", + (json.dumps({"id": "x1", "name": "X", "album": {"name": "A"}}),), + ) + conn.commit() + + singles = db.get_wishlist_tracks(category="singles") + assert [t["spotify_track_id"] for t in singles] == ["x1"] + + +def test_get_wishlist_count_no_filter(db): + for i in range(7): + _insert(db, f"t{i}", f"Track {i}", "album", date_added=f"2026-01-0{i+1}T00:00:00") + + assert db.get_wishlist_count() == 7 + + +def test_get_wishlist_count_with_category_filter(db): + _insert(db, "a1", "A1", "album", date_added="2026-01-01T00:00:00") + _insert(db, "a2", "A2", "album", date_added="2026-01-02T00:00:00") + _insert(db, "s1", "S1", "single", date_added="2026-01-03T00:00:00") + + assert db.get_wishlist_count(category="albums") == 2 + assert db.get_wishlist_count(category="singles") == 1 + assert db.get_wishlist_count() == 3 + + +def test_profile_isolation(db): + _insert(db, "p1-a", "A", "album", profile_id=1, date_added="2026-01-01T00:00:00") + _insert(db, "p1-b", "B", "album", profile_id=1, date_added="2026-01-02T00:00:00") + _insert(db, "p2-a", "C", "album", profile_id=2, date_added="2026-01-03T00:00:00") + + assert db.get_wishlist_count(profile_id=1) == 2 + assert db.get_wishlist_count(profile_id=2) == 1 + + p1 = db.get_wishlist_tracks(profile_id=1) + assert sorted(t["spotify_track_id"] for t in p1) == ["p1-a", "p1-b"] + + +def test_backward_compat_no_args_returns_all(db): + """Existing callers (wishlist_service) pass no limit/offset — must still work.""" + for i in range(3): + _insert(db, f"t{i}", f"Track {i}", "album", date_added=f"2026-01-0{i+1}T00:00:00") + + rows = db.get_wishlist_tracks() + assert len(rows) == 3 + + +def test_ordering_by_date_added(db): + _insert(db, "newer", "Newer", "album", date_added="2026-02-01T00:00:00") + _insert(db, "older", "Older", "album", date_added="2026-01-01T00:00:00") + + rows = db.get_wishlist_tracks() + assert [r["spotify_track_id"] for r in rows] == ["older", "newer"]