/
/
/
1"""Test we can parse Tidal models into Music Assistant models."""
2
3import json
4import pathlib
5from typing import TYPE_CHECKING
6from unittest.mock import Mock
7
8import pytest
9from music_assistant_models.media_items import ItemMapping
10
11from music_assistant.providers.tidal.parsers import (
12 parse_album,
13 parse_artist,
14 parse_playlist,
15 parse_track,
16)
17
18if TYPE_CHECKING:
19 from music_assistant_models.enums import MediaType
20 from syrupy.assertion import SnapshotAssertion
21
22FIXTURES_DIR = pathlib.Path(__file__).parent / "fixtures"
23ARTIST_FIXTURES = list(FIXTURES_DIR.glob("artists/*.json"))
24ALBUM_FIXTURES = list(FIXTURES_DIR.glob("albums/*.json"))
25TRACK_FIXTURES = list(FIXTURES_DIR.glob("tracks/*.json"))
26PLAYLIST_FIXTURES = list(FIXTURES_DIR.glob("playlists/*.json"))
27
28
29@pytest.fixture
30def provider_mock() -> Mock:
31 """Return a mock provider."""
32 provider = Mock()
33 provider.domain = "tidal"
34 provider.instance_id = "tidal_instance"
35 provider.auth.user_id = "12345"
36 provider.auth.user.profile_name = "Test User"
37 provider.auth.user.user_name = "Test User"
38
39 def get_item_mapping(media_type: MediaType, key: str, name: str) -> ItemMapping:
40 return ItemMapping(
41 media_type=media_type,
42 item_id=key,
43 provider=provider.instance_id,
44 name=name,
45 )
46
47 provider.get_item_mapping.side_effect = get_item_mapping
48
49 return provider
50
51
52@pytest.mark.parametrize("example", ARTIST_FIXTURES, ids=lambda val: str(val.stem))
53def test_parse_artist(
54 example: pathlib.Path, provider_mock: Mock, snapshot: SnapshotAssertion
55) -> None:
56 """Test we can parse artists."""
57 with open(example, encoding="utf-8") as f:
58 data = json.load(f)
59 parsed = parse_artist(provider_mock, data).to_dict()
60 assert snapshot == parsed
61
62
63@pytest.mark.parametrize("example", ALBUM_FIXTURES, ids=lambda val: str(val.stem))
64def test_parse_album(
65 example: pathlib.Path, provider_mock: Mock, snapshot: SnapshotAssertion
66) -> None:
67 """Test we can parse albums."""
68 with open(example, encoding="utf-8") as f:
69 data = json.load(f)
70 parsed = parse_album(provider_mock, data).to_dict()
71 assert snapshot == parsed
72
73
74@pytest.mark.parametrize("example", TRACK_FIXTURES, ids=lambda val: str(val.stem))
75def test_parse_track(
76 example: pathlib.Path, provider_mock: Mock, snapshot: SnapshotAssertion
77) -> None:
78 """Test we can parse tracks."""
79 with open(example, encoding="utf-8") as f:
80 data = json.load(f)
81 parsed = parse_track(provider_mock, data).to_dict()
82 assert snapshot == parsed
83
84
85@pytest.mark.parametrize("example", PLAYLIST_FIXTURES, ids=lambda val: str(val.stem))
86def test_parse_playlist(
87 example: pathlib.Path, provider_mock: Mock, snapshot: SnapshotAssertion
88) -> None:
89 """Test we can parse playlists."""
90 with open(example, encoding="utf-8") as f:
91 data = json.load(f)
92
93 is_mix = "mix" in example.name
94 parsed = parse_playlist(provider_mock, data, is_mix=is_mix).to_dict()
95 assert snapshot == parsed
96
97
98def test_parse_track_partial_album(provider_mock: Mock) -> None:
99 """Test track parsing tolerates partial album objects."""
100 track_obj = {
101 "id": 123,
102 "title": "Test Track",
103 "duration": 100,
104 "artists": [{"id": 1, "name": "Test Artist", "picture": None}],
105 "album": {"id": 456}, # No title or cover
106 }
107
108 track = parse_track(provider_mock, track_obj)
109
110 assert track.album is not None
111 assert track.album.item_id == "456"
112 assert track.album.name == ""
113
114 # An album without an id is useless: no mapping should be created
115 track_obj["album"] = {"title": "Test Album"}
116 track = parse_track(provider_mock, track_obj)
117 assert track.album is None
118