/
/
/
1"""
2Integration tests for the PlaylistController.
3
4Uses a database-only MusicAssistant instance with a real SQLite database in a
5temporary directory to verify that a
6playlist's ``translation_key`` survives the library round-trip.
7"""
8
9from __future__ import annotations
10
11import pytest
12from music_assistant_models.media_items import Playlist, ProviderMapping
13
14from music_assistant.controllers.music.media.playlists import PlaylistController
15from music_assistant.mass import MusicAssistant
16
17
18@pytest.fixture(scope="class", name="mass")
19def mass_fixture(music_mass_class: MusicAssistant) -> MusicAssistant:
20 """Return the class-scoped database-only Music Assistant fixture."""
21 return music_mass_class
22
23
24@pytest.fixture(scope="class")
25async def playlist_ctrl(mass: MusicAssistant) -> PlaylistController:
26 """Get the playlist controller from a running MusicAssistant instance."""
27 return mass.music.playlists
28
29
30def _make_playlist(
31 item_id: str,
32 name: str,
33 *,
34 translation_key: str | None = None,
35 translation_params: list[str] | None = None,
36) -> Playlist:
37 """Create a provider-mapped Playlist for adding to the library."""
38 return Playlist(
39 item_id=item_id,
40 provider="builtin",
41 name=name,
42 translation_key=translation_key,
43 translation_params=translation_params,
44 provider_mappings={
45 ProviderMapping(item_id=item_id, provider_domain="builtin", provider_instance="builtin")
46 },
47 owner="Music Assistant",
48 is_editable=False,
49 )
50
51
52class TestPlaylistTranslationKey:
53 """The translation_key column survives the library round-trip (mirrors genres)."""
54
55 async def test_parameterless_key_survives_round_trip(
56 self, playlist_ctrl: PlaylistController
57 ) -> None:
58 """A static (parameterless) translation_key is persisted and read back."""
59 created = await playlist_ctrl.add_item_to_library(
60 _make_playlist("infinite_mix", "Infinite Mix (library)", translation_key="infinite_mix")
61 )
62 fetched = await playlist_ctrl.get_library_item(int(created.item_id))
63 assert fetched.translation_key == "infinite_mix"
64
65 async def test_key_and_params_survive_round_trip(
66 self, playlist_ctrl: PlaylistController
67 ) -> None:
68 """A key + translation_params (e.g. Spotify's per-account Liked Songs) both persist."""
69 created = await playlist_ctrl.add_item_to_library(
70 _make_playlist(
71 "liked_songs",
72 "Liked Songs Alice",
73 translation_key="liked_songs",
74 translation_params=["Alice"],
75 )
76 )
77 fetched = await playlist_ctrl.get_library_item(int(created.item_id))
78 assert fetched.translation_key == "liked_songs"
79 assert fetched.translation_params == ["Alice"]
80
81 async def test_update_backfills_key_on_resync(self, playlist_ctrl: PlaylistController) -> None:
82 """A row added without a key adopts one when the provider later supplies it."""
83 created = await playlist_ctrl.add_item_to_library(
84 _make_playlist("recently_played", "Recently played tracks")
85 )
86 assert (await playlist_ctrl.get_library_item(int(created.item_id))).translation_key is None
87 # re-sync: same provider item, now carrying a translation_key -> update path adopts it
88 await playlist_ctrl.add_item_to_library(
89 _make_playlist(
90 "recently_played", "Recently played tracks", translation_key="recently_played"
91 )
92 )
93 fetched = await playlist_ctrl.get_library_item(int(created.item_id))
94 assert fetched.translation_key == "recently_played"
95