/
/
/
1"""Tests for NetEase Cloud Music liked-track listing."""
2
3from __future__ import annotations
4
5from typing import TYPE_CHECKING, Any
6from unittest.mock import AsyncMock
7
8import pytest
9from music_assistant_models.enums import MediaType
10
11from music_assistant.models.music_provider import SYNC_RUN_STATE, SyncRunState
12
13if TYPE_CHECKING:
14 from collections.abc import Iterator
15
16 from music_assistant.providers.neteasecloudmusic import NeteaseCloudMusicProvider
17
18
19@pytest.fixture
20def sync_run() -> Iterator[SyncRunState]:
21 """Run the library listing as part of a sync run, so its skips are recorded."""
22 state = SyncRunState()
23 token = SYNC_RUN_STATE.set(state)
24 yield state
25 SYNC_RUN_STATE.reset(token)
26
27
28def _song(song_id: int) -> dict[str, Any]:
29 """Return a minimal song payload NCM would hand back."""
30 return {"id": song_id, "name": f"Track {song_id}", "dt": 1000}
31
32
33async def test_liked_track_the_detail_call_left_out_is_reported(
34 provider: NeteaseCloudMusicProvider, sync_run: SyncRunState
35) -> None:
36 """
37 A liked track missing from the detail response is reported, not treated as removed.
38
39 The liked list is what says the track is in the library, so a detail call that leaves
40 it out must not make the deletion pass drop it.
41 """
42 provider._client = AsyncMock()
43 provider._client.get = AsyncMock(return_value={"ids": [1, 2, 3]})
44 provider._get_song_detail = AsyncMock( # type: ignore[method-assign]
45 return_value=[_song(1), _song(3)]
46 )
47
48 tracks = [track async for track in provider.get_library_tracks()]
49
50 assert [track.item_id for track in tracks] == ["1", "3"]
51 assert sync_run.skipped_item_ids == {MediaType.TRACK: {"2"}}
52 assert not sync_run.incomplete_media_types
53
54
55async def test_complete_detail_response_reports_nothing(
56 provider: NeteaseCloudMusicProvider, sync_run: SyncRunState
57) -> None:
58 """A response holding every liked track reports no skips at all."""
59 provider._client = AsyncMock()
60 provider._client.get = AsyncMock(return_value={"ids": [1, 2]})
61 provider._get_song_detail = AsyncMock( # type: ignore[method-assign]
62 return_value=[_song(1), _song(2)]
63 )
64
65 tracks = [track async for track in provider.get_library_tracks()]
66
67 assert [track.item_id for track in tracks] == ["1", "2"]
68 assert sync_run.skipped_item_ids == {}
69