M5: use DB-merged runtime settings for MQTT/discovery; add MQTT test button; move Expose panel into config accordion

This commit is contained in:
2026-06-22 20:11:54 +02:00
parent 75d685f04d
commit 35cdc7f22a
11 changed files with 502 additions and 59 deletions
+88 -1
View File
@@ -3,7 +3,7 @@
from __future__ import annotations
import sqlite3
from unittest.mock import patch
from unittest.mock import MagicMock, patch
from fastapi.testclient import TestClient
@@ -656,3 +656,90 @@ def test_put_config_bool_field_roundtrip_true_false(
finally:
conn.close()
assert rows.get("MQTT_ENABLED") == "false"
# ---------------------------------------------------------------------------
# M5-fix2: DB-config → runtime settings (Issue 2 regression assertions)
# ---------------------------------------------------------------------------
def test_put_config_mqtt_reconnect_uses_db_merged_settings(
client: TestClient, test_database_urls
) -> None:
"""PUT /api/config with MQTT keys must reconnect with DB-merged settings,
not bootstrap-only settings. Asserts that mqtt_manager.reconnect receives
settings where mqtt_enabled=True and mqtt_broker_host matches the submitted value.
"""
_login(client)
target_host = "mqtt.example.com"
payload = _full_config_payload({
"MQTT_ENABLED": "true",
"MQTT_BROKER_HOST": target_host,
})
mock_mgr = MagicMock()
mock_mgr.is_configured.return_value = True
with patch("app.api.routes.api.config.mqtt_manager", mock_mgr):
resp = client.put(
"/api/config",
json={"updates": payload},
headers={"X-CSRF-Token": "token"},
)
assert resp.status_code == 200
# reconnect must have been called once
assert mock_mgr.reconnect.call_count == 1, (
f"Expected mqtt_manager.reconnect called once, got {mock_mgr.reconnect.call_count}"
)
reconnect_settings = mock_mgr.reconnect.call_args[0][0]
assert reconnect_settings.mqtt_enabled is True, (
f"reconnect settings.mqtt_enabled must be True, got {reconnect_settings.mqtt_enabled!r}"
)
assert reconnect_settings.mqtt_broker_host == target_host, (
f"reconnect settings.mqtt_broker_host must be {target_host!r}, "
f"got {reconnect_settings.mqtt_broker_host!r}"
)
def test_post_mqtt_test_uses_db_broker_host(
client: TestClient, test_database_urls
) -> None:
"""POST /api/config/mqtt/test must use the DB-stored broker host, not env/bootstrap."""
_login(client)
db_host = "broker.db-configured.local"
# First write the MQTT config to DB via PUT /api/config
payload_save = _full_config_payload({
"MQTT_ENABLED": "true",
"MQTT_BROKER_HOST": db_host,
})
with patch("app.api.routes.api.config.mqtt_manager"):
save_resp = client.put(
"/api/config",
json={"updates": payload_save},
headers={"X-CSRF-Token": "token"},
)
assert save_resp.status_code == 200
# Now POST /api/config/mqtt/test — capture the host that _run_mqtt_test uses.
captured_host: list[str] = []
def _fake_run_mqtt_test(settings):
captured_host.append(settings.mqtt_broker_host)
# simulate success by just returning
return
with patch("app.api.routes.api.config._run_mqtt_test", side_effect=_fake_run_mqtt_test):
resp = client.post(
"/api/config/mqtt/test",
headers={"X-CSRF-Token": "token"},
)
assert resp.status_code == 200
assert len(captured_host) == 1, "Expected _run_mqtt_test to be called once"
assert captured_host[0] == db_host, (
f"Expected mqtt test to use DB host {db_host!r}, got {captured_host[0]!r}"
)