/
/
/
1"""Tests for Soundcloud search."""
2
3from __future__ import annotations
4
5from typing import Any
6
7from music_assistant_models.enums import MediaType
8
9from music_assistant.providers.soundcloud import SoundcloudMusicProvider
10
11ALL_MEDIA_TYPES = [MediaType.ARTIST, MediaType.TRACK, MediaType.PLAYLIST]
12
13
14def _track_hit(
15 track_id: int, duration: int = 235818, full_duration: int = 235771
16) -> dict[str, Any]:
17 """Build a track search hit."""
18 return {
19 "kind": "track",
20 "id": track_id,
21 "title": "Some Track",
22 "duration": duration,
23 "full_duration": full_duration,
24 "permalink_url": f"https://soundcloud.com/artist/{track_id}",
25 "user": {"id": 1, "username": "Some Artist", "permalink": "some-artist"},
26 }
27
28
29async def _search(
30 provider: SoundcloudMusicProvider,
31 collection: list[dict[str, Any]],
32 media_types: list[MediaType],
33) -> Any:
34 """Run a search against the given API collection."""
35 provider._soundcloud.search.return_value = {"collection": collection}
36 search: Any = SoundcloudMusicProvider.search.__wrapped__ # type: ignore[attr-defined]
37 return await search(provider, "query", media_types, 10)
38
39
40async def test_search_returns_all_media_types(provider: SoundcloudMusicProvider) -> None:
41 """Artists, tracks and playlists are all parsed from one search response."""
42 result = await _search(
43 provider,
44 [
45 {"kind": "user", "id": 5, "username": "Some Artist", "permalink": "some-artist"},
46 _track_hit(1),
47 {"kind": "playlist", "id": 10, "title": "Summer Mix"},
48 ],
49 ALL_MEDIA_TYPES,
50 )
51
52 assert [artist.item_id for artist in result.artists] == ["5"]
53 assert [track.item_id for track in result.tracks] == ["1"]
54 assert [playlist.item_id for playlist in result.playlists] == ["10"]
55
56
57async def test_search_skips_preview_tracks(provider: SoundcloudMusicProvider) -> None:
58 """A track that only offers a short preview is not returned as a full track."""
59 result = await _search(
60 provider, [_track_hit(1, duration=30000, full_duration=235771)], [MediaType.TRACK]
61 )
62
63 assert result.tracks == []
64
65
66async def test_search_without_supported_media_types(provider: SoundcloudMusicProvider) -> None:
67 """A search for unsupported media types does not hit the API."""
68 search: Any = SoundcloudMusicProvider.search.__wrapped__ # type: ignore[attr-defined]
69 result = await search(provider, "query", [MediaType.AUDIOBOOK], 10)
70
71 assert result.tracks == []
72 provider._soundcloud.search.assert_not_awaited()
73
74
75async def test_search_ignores_unwanted_media_types(provider: SoundcloudMusicProvider) -> None:
76 """Results outside the requested media types are left out."""
77 result = await _search(
78 provider,
79 [
80 {"kind": "user", "id": 5, "username": "Some Artist", "permalink": "some-artist"},
81 _track_hit(1),
82 ],
83 [MediaType.TRACK],
84 )
85
86 assert result.artists == []
87 assert [track.item_id for track in result.tracks] == ["1"]
88