/
/
/
1"""Tests for the MSX Bridge Provider entry point."""
2
3from __future__ import annotations
4
5from typing import Any
6from unittest.mock import Mock
7
8from music_assistant_models.enums import ConfigEntryType, PlayerFeature, ProviderFeature
9
10from music_assistant.providers.msx_bridge import setup
11from music_assistant.providers.msx_bridge.constants import (
12 CONF_ENABLE_GROUPING,
13 CONF_ENABLE_SENDSPIN_BRIDGE,
14 CONF_GROUP_STREAM_MODE,
15 CONF_HTTP_PORT,
16 CONF_OUTPUT_FORMAT,
17 DEFAULT_HTTP_PORT,
18 DEFAULT_OUTPUT_FORMAT,
19 GROUP_STREAM_MODE_INDEPENDENT,
20 GROUP_STREAM_MODE_REDIRECT,
21 GROUP_STREAM_MODE_SHARED,
22)
23from music_assistant.providers.msx_bridge.player import MSXPlayer
24from music_assistant.providers.msx_bridge.provider import MSXBridgeProvider
25
26
27async def test_setup_returns_provider(
28 mass_mock: Mock, manifest_mock: Mock, config_mock: Mock
29) -> None:
30 """setup() should return an MSXBridgeProvider instance."""
31 result = await setup(mass_mock, manifest_mock, config_mock)
32 assert isinstance(result, MSXBridgeProvider)
33
34
35async def test_get_config_entries(provider: MSXBridgeProvider) -> None:
36 """get_config_entries() should return core config entries."""
37 entries = await provider.get_config_entries()
38 assert len(entries) >= 2 # at least http_port, output_format
39
40 port_entry = entries[0]
41 assert port_entry.key == CONF_HTTP_PORT
42 assert port_entry.type == ConfigEntryType.INTEGER
43 assert port_entry.default_value == str(DEFAULT_HTTP_PORT)
44
45 format_entry = entries[1]
46 assert format_entry.key == CONF_OUTPUT_FORMAT
47 assert format_entry.type == ConfigEntryType.STRING
48 assert format_entry.default_value == DEFAULT_OUTPUT_FORMAT
49
50 # Optional: show_stop_notification if present
51 if len(entries) >= 4:
52 show_notification_entry = entries[3]
53 assert show_notification_entry.key == "show_stop_notification"
54 assert show_notification_entry.type == ConfigEntryType.BOOLEAN
55 assert show_notification_entry.default_value is False
56
57 # Verify enable_player_grouping entry exists
58 grouping_entry = next((e for e in entries if e.key == CONF_ENABLE_GROUPING), None)
59 assert grouping_entry is not None
60 assert grouping_entry.type == ConfigEntryType.BOOLEAN
61 assert grouping_entry.default_value is False
62
63
64async def test_sendspin_bridge_and_redirect_enabled_by_default(
65 provider: MSXBridgeProvider,
66) -> None:
67 """Fresh installs get the Sendspin bridge on and redirect stream mode by default."""
68 entries = await provider.get_config_entries()
69 sendspin_entry = next(e for e in entries if e.key == CONF_ENABLE_SENDSPIN_BRIDGE)
70 assert sendspin_entry.default_value is True
71 mode_entry = next(e for e in entries if e.key == CONF_GROUP_STREAM_MODE)
72 assert mode_entry.default_value == GROUP_STREAM_MODE_REDIRECT
73
74
75async def test_group_stream_mode_options_include_redirect(provider: MSXBridgeProvider) -> None:
76 """The redirect (MA streamserver) mode must be selectable in the config UI."""
77 entries = await provider.get_config_entries()
78 entry = next(e for e in entries if e.key == CONF_GROUP_STREAM_MODE)
79 assert entry.options is not None
80 values = [o.value for o in entry.options]
81 assert values == [
82 GROUP_STREAM_MODE_INDEPENDENT,
83 GROUP_STREAM_MODE_SHARED,
84 GROUP_STREAM_MODE_REDIRECT,
85 ]
86
87
88async def test_setup_without_sync_players(
89 mass_mock: Mock, manifest_mock: Mock, config_mock: Mock
90) -> None:
91 """setup() with grouping disabled should not include SYNC_PLAYERS."""
92 # setup() reads the stored grouping value directly from mass.config
93 mass_mock.config.get_raw_provider_config_value = Mock(return_value=False)
94 result = await setup(mass_mock, manifest_mock, config_mock)
95 assert isinstance(result, MSXBridgeProvider)
96 assert ProviderFeature.SYNC_PLAYERS not in result.supported_features
97
98
99async def test_setup_with_sync_players(
100 mass_mock: Mock, manifest_mock: Mock, config_mock: Mock
101) -> None:
102 """setup() with grouping enabled should include SYNC_PLAYERS."""
103 result = await setup(mass_mock, manifest_mock, config_mock)
104 assert isinstance(result, MSXBridgeProvider)
105 assert ProviderFeature.SYNC_PLAYERS in result.supported_features
106
107
108def test_player_grouping_enabled(provider: Any) -> None:
109 """MSXPlayer with grouping_enabled=True should have SET_MEMBERS."""
110 p = MSXPlayer(provider, "msx_g", name="Group TV", output_format="mp3", grouping_enabled=True)
111 p.update_state = Mock() # type: ignore[misc,method-assign]
112 assert PlayerFeature.SET_MEMBERS in p._attr_supported_features
113 assert len(p._attr_can_group_with) > 0
114
115
116def test_player_grouping_disabled(provider: Any) -> None:
117 """MSXPlayer with grouping_enabled=False should NOT have SET_MEMBERS."""
118 p = MSXPlayer(provider, "msx_ng", name="Solo TV", output_format="mp3", grouping_enabled=False)
119 p.update_state = Mock() # type: ignore[misc,method-assign]
120 assert PlayerFeature.SET_MEMBERS not in p._attr_supported_features
121 assert p._attr_can_group_with == set()
122