/
/
/
1"""Tests for the library-sync config-entry builder in the config controller."""
2
3from __future__ import annotations
4
5from types import SimpleNamespace
6from typing import TYPE_CHECKING
7
8from music_assistant_models.enums import ProviderFeature, ProviderType
9
10from music_assistant.constants import (
11 CONF_ENTRY_LIBRARY_SYNC_ARTISTS,
12 CONF_ENTRY_LIBRARY_SYNC_BACK,
13 CONF_ENTRY_LIBRARY_SYNC_DELETIONS,
14 CONF_ENTRY_LIBRARY_SYNC_PODCASTS,
15 CONF_ENTRY_LIBRARY_SYNC_TRACKS,
16)
17from music_assistant.controllers.config import ConfigController
18from music_assistant.models.music_provider import MusicProvider
19
20if TYPE_CHECKING:
21 from music_assistant_models.config_entries import ConfigEntry
22
23
24def _controller() -> ConfigController:
25 """Build a bare ConfigController (the builder is pure and needs no mass)."""
26 return ConfigController.__new__(ConfigController)
27
28
29def _keys(entries: list[ConfigEntry]) -> set[str]:
30 return {entry.key for entry in entries}
31
32
33def test_non_music_provider_has_no_sync_entries() -> None:
34 """Sync options only apply to music providers."""
35 manifest = SimpleNamespace(type=ProviderType.PLAYER)
36
37 entries = _controller()._build_sync_entries(
38 manifest, {ProviderFeature.LIBRARY_TRACKS}, provider=None
39 )
40
41 assert entries == []
42
43
44def test_library_features_map_to_sync_entries() -> None:
45 """Each supported library feature contributes its matching sync entry."""
46 manifest = SimpleNamespace(type=ProviderType.MUSIC)
47 features = {ProviderFeature.LIBRARY_ARTISTS, ProviderFeature.LIBRARY_PODCASTS}
48
49 entries = _controller()._build_sync_entries(manifest, features, provider=None)
50
51 keys = _keys(entries)
52 assert CONF_ENTRY_LIBRARY_SYNC_ARTISTS.key in keys
53 assert CONF_ENTRY_LIBRARY_SYNC_PODCASTS.key in keys
54 assert CONF_ENTRY_LIBRARY_SYNC_TRACKS.key not in keys
55
56
57def test_edit_feature_adds_sync_back_entry() -> None:
58 """Any *_EDIT feature surfaces the sync-back (export) option."""
59 manifest = SimpleNamespace(type=ProviderType.MUSIC)
60
61 entries = _controller()._build_sync_entries(
62 manifest, {ProviderFeature.LIBRARY_TRACKS_EDIT}, provider=None
63 )
64
65 assert CONF_ENTRY_LIBRARY_SYNC_BACK.key in _keys(entries)
66
67
68def test_no_features_yields_no_entries() -> None:
69 """A music provider with no library features gets no sync entries."""
70 manifest = SimpleNamespace(type=ProviderType.MUSIC)
71
72 entries = _controller()._build_sync_entries(manifest, set(), provider=None)
73
74 assert entries == []
75
76
77def _music_provider() -> MusicProvider:
78 """Build a bare MusicProvider (is_streaming_provider defaults to True)."""
79 return MusicProvider.__new__(MusicProvider)
80
81
82def test_streaming_provider_with_library_sync_gets_deletions_entry() -> None:
83 """The deletions option shows for streaming providers that sync a library."""
84 manifest = SimpleNamespace(type=ProviderType.MUSIC)
85
86 entries = _controller()._build_sync_entries(
87 manifest, {ProviderFeature.LIBRARY_TRACKS}, provider=_music_provider()
88 )
89
90 assert CONF_ENTRY_LIBRARY_SYNC_DELETIONS.key in _keys(entries)
91
92
93def test_streaming_provider_without_library_sync_has_no_deletions_entry() -> None:
94 """Without any library feature there is nothing to sync, so no deletions option."""
95 manifest = SimpleNamespace(type=ProviderType.MUSIC)
96
97 entries = _controller()._build_sync_entries(
98 manifest, {ProviderFeature.BROWSE, ProviderFeature.SEARCH}, provider=_music_provider()
99 )
100
101 assert CONF_ENTRY_LIBRARY_SYNC_DELETIONS.key not in _keys(entries)
102