/
/
/
1"""Test ZvukMusicProvider's two-method recommendations contract."""
2
3from __future__ import annotations
4
5from typing import Any
6from unittest.mock import AsyncMock, Mock
7
8import pytest
9from music_assistant_models.media_items import Playlist, UniqueList
10
11from music_assistant.providers.zvuk_music.provider import ZvukMusicProvider
12
13
14def _make_playlist(item_id: str = "1") -> Playlist:
15 """Return a minimal Playlist mock."""
16 pl = Mock(spec=Playlist)
17 pl.item_id = item_id
18 return pl
19
20
21def _make_provider() -> Any:
22 """Create a ZvukMusicProvider mock with helpers stubbed and the real methods bound."""
23 provider = Mock(spec=ZvukMusicProvider)
24 provider.instance_id = "zvuk_music"
25 provider._get_for_you_playlists = AsyncMock(return_value=[_make_playlist("3")])
26 provider._get_editorial_playlists = AsyncMock(return_value=[_make_playlist("99")])
27 provider.get_recommendations = ZvukMusicProvider.get_recommendations.__get__(
28 provider, ZvukMusicProvider
29 )
30 provider.get_recommendation_items = ZvukMusicProvider.get_recommendation_items.__get__(
31 provider, ZvukMusicProvider
32 )
33 return provider
34
35
36@pytest.mark.asyncio
37async def test_get_recommendations_returns_static_rows_without_backend_calls() -> None:
38 """get_recommendations returns both row descriptors without any backend fetch."""
39 provider = _make_provider()
40
41 result = await provider.get_recommendations()
42
43 provider._get_for_you_playlists.assert_not_awaited()
44 provider._get_editorial_playlists.assert_not_awaited()
45 assert [f.item_id for f in result] == ["for_you", "editorial"]
46 assert all(f.provider == "zvuk_music" for f in result)
47 assert all(len(f.items) == 0 for f in result)
48 for_you, editorial = result
49 assert for_you.name == "Made for you"
50 assert for_you.translation_key == "made_for_you"
51 assert for_you.icon == "mdi-playlist-music"
52 assert editorial.name == "Collections"
53 assert editorial.translation_key == "editorial"
54 assert editorial.icon == "mdi-music-box-multiple"
55 assert editorial.subtitle == "Editorial playlists from Zvuk by genre"
56
57
58@pytest.mark.asyncio
59async def test_get_recommendation_items_for_you_fetches_only_for_you() -> None:
60 """The for_you row triggers only the for-you fetch and returns its playlists."""
61 provider = _make_provider()
62
63 result = await provider.get_recommendation_items("for_you")
64
65 provider._get_for_you_playlists.assert_awaited_once()
66 provider._get_editorial_playlists.assert_not_awaited()
67 assert [item.item_id for item in result] == ["3"]
68
69
70@pytest.mark.asyncio
71async def test_get_recommendation_items_editorial_fetches_only_editorial() -> None:
72 """The editorial row triggers only the editorial fetch and returns its playlists."""
73 provider = _make_provider()
74
75 result = await provider.get_recommendation_items("editorial")
76
77 provider._get_for_you_playlists.assert_not_awaited()
78 provider._get_editorial_playlists.assert_awaited_once()
79 assert [item.item_id for item in result] == ["99"]
80
81
82@pytest.mark.asyncio
83async def test_get_recommendation_items_unknown_id_returns_empty() -> None:
84 """An unknown row item_id returns an empty UniqueList without any backend fetch."""
85 provider = _make_provider()
86
87 result = await provider.get_recommendation_items("bogus")
88
89 provider._get_for_you_playlists.assert_not_awaited()
90 provider._get_editorial_playlists.assert_not_awaited()
91 assert isinstance(result, UniqueList)
92 assert len(result) == 0
93