/
/
/
1"""
2Regression tests for create_default_player_config not corrupting existing configs.
3
4Reproduces music-assistant/support#5745: Player.__init__ calls
5create_default_player_config before provider setup has determined the real
6player type, so the passed type can be a transient class default. For
7squeezelite protocol players this rewrote the persisted player_type from
8"protocol" to "player" on every client (re)connect. Normally healed right
9after registration, but when registration was interrupted (e.g. shutdown
10while clients reconnect) the corrupted row hit disk and the universal player
11restore logic deleted the wrapping universal player config on next startup,
12wiping all user customizations.
13"""
14
15from music_assistant_models.enums import PlayerType
16
17from music_assistant.constants import CONF_PLAYERS
18from music_assistant.mass import MusicAssistant
19
20PLAYER_ID = "e4:5f:01:70:ef:67"
21
22
23async def test_existing_player_type_not_rewritten(mass_minimal: MusicAssistant) -> None:
24 """A repeated call with a transient type must not touch the persisted player_type."""
25 mass_minimal.config.create_default_player_config(
26 PLAYER_ID, "squeezelite", PlayerType.PROTOCOL, "solarium-bath-sl"
27 )
28 assert mass_minimal.config.get(f"{CONF_PLAYERS}/{PLAYER_ID}/player_type") == "protocol"
29
30 # simulate reconnect: Player.__init__ runs with the class default type (player)
31 mass_minimal.config.create_default_player_config(PLAYER_ID, "squeezelite", PlayerType.PLAYER)
32
33 assert mass_minimal.config.get(f"{CONF_PLAYERS}/{PLAYER_ID}/player_type") == "protocol"
34
35
36async def test_existing_default_name_still_updated(mass_minimal: MusicAssistant) -> None:
37 """The default_name update for existing configs keeps working."""
38 mass_minimal.config.create_default_player_config(
39 PLAYER_ID, "squeezelite", PlayerType.PROTOCOL, "old-name"
40 )
41 mass_minimal.config.create_default_player_config(
42 PLAYER_ID, "squeezelite", PlayerType.PROTOCOL, "new-name"
43 )
44 assert mass_minimal.config.get(f"{CONF_PLAYERS}/{PLAYER_ID}/default_name") == "new-name"
45
46
47async def test_creation_name_is_stored_as_default_name_only(mass_minimal: MusicAssistant) -> None:
48 """
49 The name a player is created with is stored as its default name only.
50
51 Regression test for music-assistant/support#5888: a stored name used to be
52 written for fresh configs as well, which is indistinguishable from a user
53 rename. It kept shadowing every later default name and was carried over to
54 any replacement player, producing duplicated friendly names.
55 """
56 mass_minimal.config.create_default_player_config(
57 PLAYER_ID, "squeezelite", PlayerType.PROTOCOL, "old-name"
58 )
59 assert mass_minimal.config.get(f"{CONF_PLAYERS}/{PLAYER_ID}/name") is None
60 assert mass_minimal.config.get(f"{CONF_PLAYERS}/{PLAYER_ID}/default_name") == "old-name"
61
62 mass_minimal.config.set_player_default_name(PLAYER_ID, "new-name")
63
64 assert mass_minimal.config.get(f"{CONF_PLAYERS}/{PLAYER_ID}/default_name") == "new-name"
65 assert mass_minimal.config.get(f"{CONF_PLAYERS}/{PLAYER_ID}/name") is None
66
67
68async def test_user_renamed_name_preserved_on_default_name_update(
69 mass_minimal: MusicAssistant,
70) -> None:
71 """A real user rename is never overwritten by a default name update."""
72 mass_minimal.config.create_default_player_config(
73 PLAYER_ID, "squeezelite", PlayerType.PROTOCOL, "old-name"
74 )
75 mass_minimal.config.set(f"{CONF_PLAYERS}/{PLAYER_ID}/name", "My Custom Name")
76
77 mass_minimal.config.set_player_default_name(PLAYER_ID, "new-name")
78
79 assert mass_minimal.config.get(f"{CONF_PLAYERS}/{PLAYER_ID}/default_name") == "new-name"
80 assert mass_minimal.config.get(f"{CONF_PLAYERS}/{PLAYER_ID}/name") == "My Custom Name"
81