- Let the wishlist service accept both track_data and spotify_track_data - Preserve the backward-compatible wrapper while avoiding the keyword argument crash - Add a regression test for the alias path
324 lines
11 KiB
Python
324 lines
11 KiB
Python
from types import SimpleNamespace
|
|
|
|
from core.wishlist.service import WishlistService
|
|
|
|
|
|
class _FakeWishlistDatabase:
|
|
def __init__(self, tracks=None, count=None):
|
|
self.tracks = list(tracks or [])
|
|
self.count = len(self.tracks) if count is None else count
|
|
self.add_calls = []
|
|
self.track_queries = []
|
|
|
|
def add_to_wishlist(self, **kwargs):
|
|
self.add_calls.append(kwargs)
|
|
return True
|
|
|
|
def get_wishlist_tracks(self, limit=None, profile_id=1):
|
|
self.track_queries.append(("get_wishlist_tracks", limit, profile_id))
|
|
tracks = list(self.tracks)
|
|
return tracks if limit is None else tracks[:limit]
|
|
|
|
def get_wishlist_count(self, profile_id=1):
|
|
self.track_queries.append(("get_wishlist_count", profile_id))
|
|
return self.count
|
|
|
|
|
|
def _build_service(fake_db):
|
|
service = WishlistService(database_path="test.db")
|
|
service._database = fake_db
|
|
return service
|
|
|
|
|
|
def test_add_failed_track_from_modal_normalizes_candidates_and_forwards_payload():
|
|
fake_db = _FakeWishlistDatabase()
|
|
service = _build_service(fake_db)
|
|
|
|
track_info = {
|
|
"failure_reason": "Download cancelled",
|
|
"download_index": 4,
|
|
"table_index": 4,
|
|
"candidates": [
|
|
SimpleNamespace(title="Candidate Song", artist="Candidate Artist", filename="track.flac"),
|
|
{"title": "kept"},
|
|
],
|
|
"spotify_track": {
|
|
"id": "sp-1",
|
|
"name": "Song One",
|
|
"artists": [{"name": "Artist One"}],
|
|
"album": {"name": "Album One"},
|
|
},
|
|
}
|
|
source_context = {"playlist_name": "Playlist One"}
|
|
|
|
result = service.add_failed_track_from_modal(
|
|
track_info,
|
|
source_type="playlist",
|
|
source_context=source_context,
|
|
profile_id=3,
|
|
)
|
|
|
|
assert result is True
|
|
assert fake_db.add_calls[0]["spotify_track_data"]["id"] == "sp-1"
|
|
assert fake_db.add_calls[0]["failure_reason"] == "Download cancelled"
|
|
assert fake_db.add_calls[0]["source_type"] == "playlist"
|
|
assert fake_db.add_calls[0]["profile_id"] == 3
|
|
assert fake_db.add_calls[0]["source_info"]["playlist_name"] == "Playlist One"
|
|
assert fake_db.add_calls[0]["source_info"]["original_modal_data"] == {
|
|
"download_index": 4,
|
|
"table_index": 4,
|
|
"candidates": [
|
|
{
|
|
"title": "Candidate Song",
|
|
"artist": "Candidate Artist",
|
|
"filename": "track.flac",
|
|
},
|
|
{"title": "kept"},
|
|
],
|
|
}
|
|
|
|
|
|
def test_add_failed_track_from_modal_returns_false_when_no_spotify_track_found():
|
|
fake_db = _FakeWishlistDatabase()
|
|
service = _build_service(fake_db)
|
|
|
|
result = service.add_failed_track_from_modal({"track_info": {}}, profile_id=1)
|
|
|
|
assert result is False
|
|
assert fake_db.add_calls == []
|
|
|
|
|
|
def test_add_spotify_track_to_wishlist_accepts_track_data_alias():
|
|
fake_db = _FakeWishlistDatabase()
|
|
service = _build_service(fake_db)
|
|
|
|
result = service.add_spotify_track_to_wishlist(
|
|
track_data={
|
|
"id": "sp-1",
|
|
"name": "Song One",
|
|
"artists": [{"name": "Artist One"}],
|
|
"album": {"name": "Album One"},
|
|
},
|
|
failure_reason="Download failed",
|
|
source_type="manual",
|
|
profile_id=2,
|
|
)
|
|
|
|
assert result is True
|
|
assert fake_db.add_calls[0]["track_data"]["id"] == "sp-1"
|
|
assert fake_db.add_calls[0]["failure_reason"] == "Download failed"
|
|
assert fake_db.add_calls[0]["source_type"] == "manual"
|
|
assert fake_db.add_calls[0]["profile_id"] == 2
|
|
|
|
|
|
def test_get_wishlist_tracks_for_download_formats_modal_shape():
|
|
fake_db = _FakeWishlistDatabase(
|
|
tracks=[
|
|
{
|
|
"id": "wl-1",
|
|
"spotify_track_id": "sp-1",
|
|
"spotify_data": {
|
|
"id": "sp-1",
|
|
"name": "Song One",
|
|
"artists": [{"name": "Artist One"}],
|
|
"album": {"name": "Album One"},
|
|
"duration_ms": 321,
|
|
"preview_url": "https://example.test/preview",
|
|
"external_urls": {"spotify": "https://open.spotify.com/track/sp-1"},
|
|
"popularity": 88,
|
|
"track_number": 7,
|
|
"disc_number": 2,
|
|
},
|
|
"failure_reason": "Download failed",
|
|
"retry_count": 2,
|
|
"date_added": "2024-01-01",
|
|
"last_attempted": "2024-01-02",
|
|
"source_type": "playlist",
|
|
"source_info": {"playlist_name": "Playlist One"},
|
|
}
|
|
]
|
|
)
|
|
service = _build_service(fake_db)
|
|
|
|
formatted_tracks = service.get_wishlist_tracks_for_download(limit=1, profile_id=7)
|
|
|
|
assert fake_db.track_queries == [("get_wishlist_tracks", 1, 7)]
|
|
assert formatted_tracks == [
|
|
{
|
|
"wishlist_id": "wl-1",
|
|
"track_id": "sp-1",
|
|
"spotify_track_id": "sp-1",
|
|
"track_data": {
|
|
"id": "sp-1",
|
|
"name": "Song One",
|
|
"artists": [{"name": "Artist One"}],
|
|
"album": {"name": "Album One"},
|
|
"duration_ms": 321,
|
|
"preview_url": "https://example.test/preview",
|
|
"external_urls": {"spotify": "https://open.spotify.com/track/sp-1"},
|
|
"popularity": 88,
|
|
"track_number": 7,
|
|
"disc_number": 2,
|
|
},
|
|
"track_name": "Song One",
|
|
"artist_name": "Artist One",
|
|
"album_name": "Album One",
|
|
"provider": None,
|
|
"spotify_data": {
|
|
"id": "sp-1",
|
|
"name": "Song One",
|
|
"artists": [{"name": "Artist One"}],
|
|
"album": {"name": "Album One"},
|
|
"duration_ms": 321,
|
|
"preview_url": "https://example.test/preview",
|
|
"external_urls": {"spotify": "https://open.spotify.com/track/sp-1"},
|
|
"popularity": 88,
|
|
"track_number": 7,
|
|
"disc_number": 2,
|
|
},
|
|
"failure_reason": "Download failed",
|
|
"retry_count": 2,
|
|
"date_added": "2024-01-01",
|
|
"last_attempted": "2024-01-02",
|
|
"source_type": "playlist",
|
|
"source_info": {"playlist_name": "Playlist One"},
|
|
"id": "sp-1",
|
|
"name": "Song One",
|
|
"artists": [{"name": "Artist One"}],
|
|
"album": {"name": "Album One"},
|
|
"duration_ms": 321,
|
|
"preview_url": "https://example.test/preview",
|
|
"external_urls": {"spotify": "https://open.spotify.com/track/sp-1"},
|
|
"popularity": 88,
|
|
"track_number": 7,
|
|
"disc_number": 2,
|
|
}
|
|
]
|
|
|
|
|
|
def test_check_track_in_wishlist_and_find_matching_wishlist_track_handle_id_variants():
|
|
fake_db = _FakeWishlistDatabase(
|
|
tracks=[
|
|
{
|
|
"id": "wl-1",
|
|
"spotify_track_id": "sp-1",
|
|
"name": "Song One",
|
|
"artists": [{"name": "Artist One"}],
|
|
"spotify_data": {
|
|
"id": "sp-1",
|
|
"name": "Song One",
|
|
"artists": [{"name": "Artist One"}],
|
|
"album": {"name": "Album One"},
|
|
},
|
|
"failure_reason": "Download failed",
|
|
"retry_count": 1,
|
|
"date_added": "2024-01-01",
|
|
"last_attempted": None,
|
|
"source_type": "playlist",
|
|
"source_info": {},
|
|
},
|
|
{
|
|
"id": "sp-2",
|
|
"spotify_track_id": "sp-2",
|
|
"name": "Song Two",
|
|
"artists": ["Artist Two"],
|
|
"spotify_data": {
|
|
"id": "sp-2",
|
|
"name": "Song Two",
|
|
"artists": [{"name": "Artist Two"}],
|
|
"album": {"name": "Album Two"},
|
|
},
|
|
"failure_reason": "Download failed",
|
|
"retry_count": 1,
|
|
"date_added": "2024-01-02",
|
|
"last_attempted": None,
|
|
"source_type": "manual",
|
|
"source_info": {},
|
|
},
|
|
]
|
|
)
|
|
service = _build_service(fake_db)
|
|
formatted_tracks = service.get_wishlist_tracks_for_download()
|
|
|
|
assert service.check_track_in_wishlist("sp-1") is True
|
|
assert service.check_track_in_wishlist("sp-2") is True
|
|
assert service.check_track_in_wishlist("missing") is False
|
|
assert service.find_matching_wishlist_track("Song One", "Artist One") == formatted_tracks[0]
|
|
assert service.find_matching_wishlist_track("Song Two", "Artist Two") == formatted_tracks[1]
|
|
assert service.find_matching_wishlist_track("Missing", "Nobody") is None
|
|
|
|
|
|
def test_get_wishlist_summary_returns_empty_summary_when_count_is_zero():
|
|
fake_db = _FakeWishlistDatabase(tracks=[], count=0)
|
|
service = _build_service(fake_db)
|
|
|
|
assert service.get_wishlist_summary(profile_id=2) == {
|
|
"total_tracks": 0,
|
|
"by_source_type": {},
|
|
"recent_failures": [],
|
|
}
|
|
|
|
|
|
def test_get_wishlist_summary_groups_by_source_type_and_limits_recent_failures():
|
|
fake_db = _FakeWishlistDatabase(
|
|
tracks=[
|
|
{
|
|
"source_type": "playlist",
|
|
"failure_reason": "f1",
|
|
"retry_count": 1,
|
|
"date_added": "2024-01-01",
|
|
"spotify_data": {"name": "Song A", "artists": [{"name": "Artist A"}]},
|
|
},
|
|
{
|
|
"source_type": "playlist",
|
|
"failure_reason": "f2",
|
|
"retry_count": 2,
|
|
"date_added": "2024-01-02",
|
|
"spotify_data": {"name": "Song B", "artists": ["Artist B"]},
|
|
},
|
|
{
|
|
"source_type": "album",
|
|
"failure_reason": "f3",
|
|
"retry_count": 3,
|
|
"date_added": "2024-01-03",
|
|
"spotify_data": {"name": "Song C", "artists": [{"name": "Artist C"}]},
|
|
},
|
|
{
|
|
"source_type": "manual",
|
|
"failure_reason": "f4",
|
|
"retry_count": 4,
|
|
"date_added": "2024-01-04",
|
|
"spotify_data": {"name": "Song D", "artists": [{"name": "Artist D"}]},
|
|
},
|
|
{
|
|
"source_type": "manual",
|
|
"failure_reason": "f5",
|
|
"retry_count": 5,
|
|
"date_added": "2024-01-05",
|
|
"spotify_data": {"name": "Song E", "artists": [{"name": "Artist E"}]},
|
|
},
|
|
{
|
|
"source_type": "manual",
|
|
"failure_reason": "f6",
|
|
"retry_count": 6,
|
|
"date_added": "2024-01-06",
|
|
"spotify_data": {"name": "Song F", "artists": [{"name": "Artist F"}]},
|
|
},
|
|
]
|
|
)
|
|
service = _build_service(fake_db)
|
|
|
|
summary = service.get_wishlist_summary(profile_id=4)
|
|
|
|
assert summary["total_tracks"] == 6
|
|
assert summary["by_source_type"] == {"playlist": 2, "album": 1, "manual": 3}
|
|
assert len(summary["recent_failures"]) == 5
|
|
assert summary["recent_failures"][0] == {
|
|
"name": "Song A",
|
|
"artist": "Artist A",
|
|
"failure_reason": "f1",
|
|
"retry_count": 1,
|
|
"date_added": "2024-01-01",
|
|
}
|
|
assert summary["recent_failures"][1]["artist"] == "Artist B"
|
|
assert summary["recent_failures"][-1]["name"] == "Song E"
|