- Add tests/integration/test_mqtt_reconnect.py with in-memory broker/client fakes - Add tests/integration/test_config_push_integration.py for POST /config flow and reconnect trigger - Add tests/integration/test_event_delivery.py for retry queue delivery behavior - Add httpx dependency required by FastAPI/Starlette TestClient - Update OPTIMIZATION_PLAN.md to mark 4.2 tasks complete Validation: - pytest tests/integration/test_mqtt_reconnect.py tests/integration/test_config_push_integration.py tests/integration/test_event_delivery.py - mypy tests/integration/test_mqtt_reconnect.py tests/integration/test_config_push_integration.py tests/integration/test_event_delivery.py
86 lines
2.7 KiB
Python
86 lines
2.7 KiB
Python
"""Integration tests for config push flow via FastAPI config server."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from fastapi.testclient import TestClient
|
|
|
|
from api.models import BridgeConfig
|
|
from api.server import ConfigServer
|
|
|
|
|
|
def _config_payload(broker: str, port: int = 1883) -> dict[str, Any]:
|
|
return {
|
|
"mqtt": {
|
|
"broker": broker,
|
|
"port": port,
|
|
"username": "",
|
|
"password": "",
|
|
"client_id": "iot_bridge",
|
|
"keepalive": 60,
|
|
"use_tls": False,
|
|
},
|
|
"devices": [
|
|
{
|
|
"device_id": "dev-1",
|
|
"machine_name": "Machine 1",
|
|
"mqtt_topic": "dev-1/status/pm1:0",
|
|
"parser_type": "shelly_pm",
|
|
"device_status_timeout_s": 90,
|
|
"session_config": {
|
|
"standby_threshold_w": 20,
|
|
"working_threshold_w": 100,
|
|
"start_debounce_s": 3,
|
|
"stop_debounce_s": 15,
|
|
"message_timeout_s": 90,
|
|
"heartbeat_interval_s": 300,
|
|
},
|
|
}
|
|
],
|
|
"timestamp": "2026-02-19T18:00:00Z",
|
|
"version": "1.0",
|
|
}
|
|
|
|
|
|
def test_config_push_applies_and_returns_request_id(monkeypatch) -> None:
|
|
received: list[BridgeConfig] = []
|
|
|
|
async def on_config(config: BridgeConfig) -> None:
|
|
received.append(config)
|
|
|
|
server = ConfigServer(config_callback=on_config)
|
|
monkeypatch.setattr(server, "_persist_config", lambda _cfg: None)
|
|
|
|
client = TestClient(server.app)
|
|
response = client.post("/config", json=_config_payload("broker-a"))
|
|
|
|
assert response.status_code == 200
|
|
assert response.json()["status"] == "success"
|
|
assert "X-Request-ID" in response.headers
|
|
assert len(received) == 1
|
|
assert received[0].mqtt is not None
|
|
assert received[0].mqtt.broker == "broker-a"
|
|
|
|
|
|
def test_config_push_triggers_mqtt_reconnect_on_broker_change(monkeypatch) -> None:
|
|
reconnect_calls: list[str] = []
|
|
|
|
async def on_config(_config: BridgeConfig) -> None:
|
|
return None
|
|
|
|
async def on_reconnect(mqtt_config) -> None:
|
|
reconnect_calls.append(f"{mqtt_config.broker}:{mqtt_config.port}")
|
|
|
|
server = ConfigServer(config_callback=on_config, mqtt_reconnect_callback=on_reconnect)
|
|
monkeypatch.setattr(server, "_persist_config", lambda _cfg: None)
|
|
|
|
client = TestClient(server.app)
|
|
first = client.post("/config", json=_config_payload("broker-a"))
|
|
assert first.status_code == 200
|
|
|
|
second = client.post("/config", json=_config_payload("broker-b"))
|
|
assert second.status_code == 200
|
|
|
|
assert reconnect_calls == ["broker-b:1883"]
|