/
/
1"""Test Tidal Media Manager."""
2
3from unittest.mock import Mock, patch
4
5from music_assistant_models.enums import MediaType
6from music_assistant_models.errors import RetriesExhausted
7
8from music_assistant.providers.tidal.media import TidalMediaManager
9
10
11@patch("music_assistant.providers.tidal.media.parse_artist")
12@patch("music_assistant.providers.tidal.media.parse_album")
13@patch("music_assistant.providers.tidal.media.parse_track")
14@patch("music_assistant.providers.tidal.media.parse_playlist")
15async def test_search(
16 mock_parse_playlist: Mock,
17 mock_parse_track: Mock,
18 mock_parse_album: Mock,
19 mock_parse_artist: Mock,
20 media_manager: TidalMediaManager,
21 provider_mock: Mock,
22) -> None:
23 """Test search."""
24 provider_mock.api.get.return_value = {
25 "artists": {"items": [{"id": 1}]},
26 "albums": {"items": [{"id": 1}]},
27 "tracks": {"items": [{"id": 1}]},
28 "playlists": {"items": [{"uuid": "1"}]},
29 }
30
31 mock_parse_artist.return_value = Mock(item_id="1", media_type=MediaType.ARTIST)
32 mock_parse_album.return_value = Mock(item_id="1", media_type=MediaType.ALBUM)
33 mock_parse_track.return_value = Mock(item_id="1", media_type=MediaType.TRACK)
34 mock_parse_playlist.return_value = Mock(item_id="1", media_type=MediaType.PLAYLIST)
35
36 results = await media_manager.search(
37 "query", [MediaType.ARTIST, MediaType.ALBUM, MediaType.TRACK, MediaType.PLAYLIST]
38 )
39
40 assert len(results.artists) == 1
41 assert len(results.albums) == 1
42 assert len(results.tracks) == 1
43 assert len(results.playlists) == 1
44
45 mock_parse_artist.assert_called()
46 mock_parse_album.assert_called()
47 mock_parse_track.assert_called()
48 mock_parse_playlist.assert_called()
49
50 provider_mock.api.get.assert_called_with(
51 "search",
52 params={
53 "query": "query",
54 "types": "artists,albums,tracks,playlists",
55 "limit": 5,
56 },
57 )
58
59
60@patch("music_assistant.providers.tidal.media.parse_artist")
61async def test_get_artist(
62 mock_parse_artist: Mock, media_manager: TidalMediaManager, provider_mock: Mock
63) -> None:
64 """Test get_artist."""
65 provider_mock.api.get.return_value = {"id": 1, "name": "Test Artist"}
66 mock_parse_artist.return_value = Mock(item_id="1")
67
68 artist = await media_manager.get_artist("1")
69
70 assert artist.item_id == "1"
71 provider_mock.api.get.assert_called_with("artists/1")
72 mock_parse_artist.assert_called_once()
73
74
75@patch("music_assistant.providers.tidal.media.parse_album")
76async def test_get_album(
77 mock_parse_album: Mock, media_manager: TidalMediaManager, provider_mock: Mock
78) -> None:
79 """Test get_album."""
80 provider_mock.api.get.return_value = {"id": 1, "title": "Test Album"}
81 mock_parse_album.return_value = Mock(item_id="1")
82
83 album = await media_manager.get_album("1")
84
85 assert album.item_id == "1"
86 provider_mock.api.get.assert_called_with("albums/1")
87 mock_parse_album.assert_called_once()
88
89
90@patch("music_assistant.providers.tidal.media.parse_track")
91async def test_get_track(
92 mock_parse_track: Mock, media_manager: TidalMediaManager, provider_mock: Mock
93) -> None:
94 """Test get_track."""
95 provider_mock.api.get.side_effect = [
96 {"id": 1, "title": "Test Track"}, # Track data
97 {"lyrics": "Test Lyrics"}, # Lyrics data
98 ]
99 mock_parse_track.return_value = Mock(item_id="1")
100
101 track = await media_manager.get_track("1")
102
103 assert track.item_id == "1"
104 assert provider_mock.api.get.call_count == 2
105 provider_mock.api.get.assert_any_call("tracks/1")
106 provider_mock.api.get.assert_any_call("tracks/1/lyrics")
107 mock_parse_track.assert_called_once()
108
109
110@patch("music_assistant.providers.tidal.media.parse_track")
111async def test_get_track_tolerates_lyrics_failure(
112 mock_parse_track: Mock, media_manager: TidalMediaManager, provider_mock: Mock
113) -> None:
114 """Test get_track still returns the track when the lyrics lookup fails."""
115 provider_mock.api.get.side_effect = [
116 {"id": 1, "title": "Test Track"}, # Track data
117 RetriesExhausted("lyrics lookup failed"), # Lyrics data
118 ]
119 mock_parse_track.return_value = Mock(item_id="1")
120
121 track = await media_manager.get_track("1")
122
123 assert track.item_id == "1"
124 mock_parse_track.assert_called_once_with(
125 provider_mock, {"id": 1, "title": "Test Track"}, lyrics=None
126 )
127
128
129@patch("music_assistant.providers.tidal.media.parse_playlist")
130async def test_get_playlist(
131 mock_parse_playlist: Mock, media_manager: TidalMediaManager, provider_mock: Mock
132) -> None:
133 """Test get_playlist."""
134 provider_mock.api.get.return_value = {"uuid": "1", "title": "Test Playlist"}
135 mock_parse_playlist.return_value = Mock(item_id="1")
136
137 playlist = await media_manager.get_playlist("1")
138
139 assert playlist.item_id == "1"
140 provider_mock.api.get.assert_called_with("playlists/1")
141 mock_parse_playlist.assert_called_once()
142
143
144@patch("music_assistant.providers.tidal.media.parse_track")
145async def test_get_album_tracks(
146 mock_parse_track: Mock, media_manager: TidalMediaManager, provider_mock: Mock
147) -> None:
148 """Test get_album_tracks."""
149 provider_mock.api.get.return_value = {"items": [{"id": 1}]}
150 mock_parse_track.return_value = Mock(item_id="1")
151
152 tracks = await media_manager.get_album_tracks("1")
153
154 assert len(tracks) == 1
155 assert tracks[0].item_id == "1"
156 provider_mock.api.get.assert_called_with(
157 "albums/1/tracks",
158 params={"limit": 250},
159 )
160
161
162@patch("music_assistant.providers.tidal.media.parse_album")
163async def test_get_artist_albums(
164 mock_parse_album: Mock, media_manager: TidalMediaManager, provider_mock: Mock
165) -> None:
166 """Test get_artist_albums."""
167 provider_mock.api.get.return_value = {"items": [{"id": 1}]}
168 mock_parse_album.return_value = Mock(item_id="1")
169
170 albums = await media_manager.get_artist_albums("1")
171
172 assert len(albums) == 1
173 assert albums[0].item_id == "1"
174 provider_mock.api.get.assert_called_with(
175 "artists/1/albums",
176 params={"limit": 250},
177 )
178
179
180@patch("music_assistant.providers.tidal.media.parse_track")
181async def test_get_artist_toptracks(
182 mock_parse_track: Mock, media_manager: TidalMediaManager, provider_mock: Mock
183) -> None:
184 """Test get_artist_toptracks."""
185 provider_mock.api.get.return_value = {"items": [{"id": 1}]}
186 mock_parse_track.return_value = Mock(item_id="1")
187
188 tracks = await media_manager.get_artist_toptracks("1")
189
190 assert len(tracks) == 1
191 assert tracks[0].item_id == "1"
192 provider_mock.api.get.assert_called_with(
193 "artists/1/toptracks",
194 params={"limit": 10, "offset": 0},
195 )
196
197
198@patch("music_assistant.providers.tidal.media.parse_track")
199async def test_get_playlist_tracks(
200 mock_parse_track: Mock, media_manager: TidalMediaManager, provider_mock: Mock
201) -> None:
202 """Test get_playlist_tracks."""
203 provider_mock.api.get.return_value = {"items": [{"id": 1}]}
204 mock_parse_track.return_value = Mock(item_id="1")
205
206 tracks = await media_manager.get_playlist_tracks("1")
207
208 assert len(tracks) == 1
209 assert tracks[0].item_id == "1"
210 provider_mock.api.get.assert_called_with(
211 "playlists/1/tracks",
212 params={"limit": 200, "offset": 0},
213 )
214
215
216async def test_get_playlist_favorite_tracks(
217 media_manager: TidalMediaManager, provider_mock: Mock
218) -> None:
219 """Test get_playlist returns the virtual favorite tracks playlist without an API call."""
220 playlist = await media_manager.get_playlist("favorite_tracks")
221
222 assert playlist.item_id == "favorite_tracks"
223 assert playlist.name == "Favorite Tracks"
224 assert not playlist.is_editable
225 provider_mock.api.get.assert_not_called()
226
227
228@patch("music_assistant.providers.tidal.media.parse_track")
229async def test_get_playlist_tracks_favorite_tracks(
230 mock_parse_track: Mock, media_manager: TidalMediaManager, provider_mock: Mock
231) -> None:
232 """Test get_playlist_tracks returns favorite tracks ordered by date descending."""
233 provider_mock.api.get.return_value = {"items": [{"item": {"id": 1}}]}
234 mock_parse_track.return_value = Mock(item_id="1")
235
236 tracks = await media_manager.get_playlist_tracks("favorite_tracks", page=0)
237
238 assert len(tracks) == 1
239 assert tracks[0].item_id == "1"
240 provider_mock.api.get.assert_called_with(
241 "users/12345/favorites/tracks",
242 params={"limit": 200, "offset": 0, "order": "DATE", "orderDirection": "DESC"},
243 )
244