/
/
/
1"""Test MusicMe web search fallback and playerInit extraction."""
2
3from __future__ import annotations
4
5from unittest.mock import AsyncMock, MagicMock
6
7import pytest
8from music_assistant_models.enums import MediaType
9
10from music_assistant.providers.musicme.provider import MusicMeProvider
11
12PLAYER_INIT_HTML = """\
13<html><head><script>
14var viewmode = 'web';
15window.playerInit = {"registeredUser":1,"adSupported":0,"catalogSize":"13253842",\
16"contentOnLoad":{"id":"94585","type":"artist","mode":"radio","autoPlay":0},\
17"userId":"abc123"};
18</script></head></html>
19"""
20
21PLAYER_INIT_ALBUM_HTML = """\
22<html><head><script>
23window.playerInit = {"registeredUser":1,"contentOnLoad":{"id":"5034644330297",\
24"type":"album","mode":"stream","autoPlay":0},"userId":"abc123"};
25</script></head></html>
26"""
27
28NO_PLAYER_INIT_HTML = "<html><body>No init here</body></html>"
29
30
31class TestWebSearchFallback:
32 """Tests for _web_search_fallback."""
33
34 @pytest.mark.asyncio
35 async def test_extracts_artist_from_player_init(self, provider: MusicMeProvider) -> None:
36 """Test that an artist page redirects to the correct artist lookup."""
37 mock_resp = MagicMock()
38 mock_resp.status = 200
39 mock_resp.read = AsyncMock(return_value=PLAYER_INIT_HTML.encode("latin-1"))
40 mock_resp.__aenter__ = AsyncMock(return_value=mock_resp)
41 mock_resp.__aexit__ = AsyncMock(return_value=False)
42
43 provider.http_session = MagicMock()
44 provider.http_session.get = MagicMock(return_value=mock_resp)
45
46 provider._api_get = AsyncMock( # type: ignore[method-assign]
47 return_value={
48 "item": {"id": 94585, "name": "Zaz", "uri": "/Zaz/"},
49 "results": {
50 "albums": [
51 {"barcode": "001", "name": "Album1", "streamable": 2},
52 ]
53 },
54 }
55 )
56
57 result = await provider._web_search_fallback(
58 "Zaz", [MediaType.ARTIST, MediaType.ALBUM], limit=10
59 )
60
61 assert result is not None
62 assert len(result.artists) == 1
63 assert result.artists[0].name == "Zaz"
64 assert len(result.albums) == 1
65
66 @pytest.mark.asyncio
67 async def test_extracts_album_from_player_init(self, provider: MusicMeProvider) -> None:
68 """Test that an album page redirects to the correct album lookup."""
69 mock_resp = MagicMock()
70 mock_resp.status = 200
71 mock_resp.read = AsyncMock(return_value=PLAYER_INIT_ALBUM_HTML.encode("latin-1"))
72 mock_resp.__aenter__ = AsyncMock(return_value=mock_resp)
73 mock_resp.__aexit__ = AsyncMock(return_value=False)
74
75 provider.http_session = MagicMock()
76 provider.http_session.get = MagicMock(return_value=mock_resp)
77
78 provider._api_get = AsyncMock( # type: ignore[method-assign]
79 return_value={
80 "item": {"barcode": "5034644330297", "name": "Veridis Quo", "streamable": 2},
81 "results": {
82 "tracks": [
83 {"barcode": "5034644330297-01_01", "title": "Track1", "streamable": 2},
84 ]
85 },
86 }
87 )
88
89 result = await provider._web_search_fallback(
90 "Veridis Quo", [MediaType.ALBUM, MediaType.TRACK], limit=10
91 )
92
93 assert result is not None
94 assert len(result.albums) == 1
95 assert len(result.tracks) == 1
96
97 @pytest.mark.asyncio
98 async def test_returns_none_when_no_player_init(self, provider: MusicMeProvider) -> None:
99 """Test that missing playerInit returns None."""
100 mock_resp = MagicMock()
101 mock_resp.status = 200
102 mock_resp.read = AsyncMock(return_value=NO_PLAYER_INIT_HTML.encode("latin-1"))
103 mock_resp.__aenter__ = AsyncMock(return_value=mock_resp)
104 mock_resp.__aexit__ = AsyncMock(return_value=False)
105
106 provider.http_session = MagicMock()
107 provider.http_session.get = MagicMock(return_value=mock_resp)
108
109 result = await provider._web_search_fallback("nope", [MediaType.ARTIST], limit=10)
110
111 assert result is None
112
113 @pytest.mark.asyncio
114 async def test_returns_none_on_http_error(self, provider: MusicMeProvider) -> None:
115 """Test that non-200 status returns None."""
116 mock_resp = MagicMock()
117 mock_resp.status = 500
118 mock_resp.__aenter__ = AsyncMock(return_value=mock_resp)
119 mock_resp.__aexit__ = AsyncMock(return_value=False)
120
121 provider.http_session = MagicMock()
122 provider.http_session.get = MagicMock(return_value=mock_resp)
123
124 result = await provider._web_search_fallback("test", [MediaType.ARTIST], limit=10)
125
126 assert result is None
127