/
/
/
1"""
2Tests that config changes survive a real server shutdown.
3
4These run against an actual MusicAssistant instance rather than a stub, because
5the ordering that matters here is the server's own: ``stop()`` cancels the
6tracked tasks - the save task among them - before it closes the controllers.
7"""
8
9from __future__ import annotations
10
11import json
12from pathlib import Path
13from typing import TYPE_CHECKING
14
15if TYPE_CHECKING:
16 import pytest
17
18 from music_assistant.mass import MusicAssistant
19
20
21async def test_immediate_save_survives_a_shutdown(
22 mass_minimal: MusicAssistant, caplog: pytest.LogCaptureFixture
23) -> None:
24 """A change saved immediately must be on disk after a stop, without errors."""
25 mass_minimal.config.set("test/immediate", "value", immediate=True)
26
27 await mass_minimal.stop()
28
29 settings = json.loads(Path(mass_minimal.config.filename).read_text())
30 assert settings["test"]["immediate"] == "value"
31 assert not [record for record in caplog.records if record.levelname == "ERROR"]
32
33
34async def test_debounced_save_survives_a_shutdown(
35 mass_minimal: MusicAssistant, caplog: pytest.LogCaptureFixture
36) -> None:
37 """A change still waiting out the debounce delay must be on disk after a stop."""
38 mass_minimal.config.set("test/debounced", "value")
39
40 await mass_minimal.stop()
41
42 settings = json.loads(Path(mass_minimal.config.filename).read_text())
43 assert settings["test"]["debounced"] == "value"
44 assert not [record for record in caplog.records if record.levelname == "ERROR"]
45
46
47async def test_shutdown_does_not_rewrite_unchanged_settings(
48 mass_minimal: MusicAssistant,
49) -> None:
50 """A stop without config changes must leave the settings file alone."""
51 mass_minimal.config.set("test/change", "value")
52 await mass_minimal.config._async_save()
53 written_at = Path(mass_minimal.config.filename).stat().st_mtime_ns
54
55 await mass_minimal.stop()
56
57 assert Path(mass_minimal.config.filename).stat().st_mtime_ns == written_at
58