M8-R02: report DSMR MQTT source health
This commit is contained in:
@@ -437,6 +437,88 @@ def test_source_sync_connack_before_connect_returns_subscribes_all_topics() -> N
|
||||
assert client.subscribed == ["telegram/topic", "tariff/topic"]
|
||||
|
||||
|
||||
def test_source_health_tracks_connack_disconnect_and_ignores_stale_callbacks() -> None:
|
||||
"""A source is connecting until CONNACK, and old generations cannot rewrite health."""
|
||||
manager = MqttManager()
|
||||
old_client = MagicMock()
|
||||
new_client = MagicMock()
|
||||
old_states: list[str] = []
|
||||
new_states: list[str] = []
|
||||
kwargs = {
|
||||
"host": "broker.test",
|
||||
"port": 1883,
|
||||
"username": "",
|
||||
"password": "",
|
||||
"tls_enabled": False,
|
||||
"subscriptions": {"topic": lambda _payload: None},
|
||||
}
|
||||
with patch("app.integrations.mqtt.mqtt.Client", side_effect=[old_client, new_client]):
|
||||
assert manager.replace_source(1, **kwargs, state_handler=old_states.append)
|
||||
assert old_states == ["connecting"]
|
||||
assert manager.replace_source(1, **kwargs, state_handler=new_states.append)
|
||||
|
||||
accepted = MagicMock()
|
||||
accepted.is_failure = False
|
||||
old_client.on_connect(old_client, None, MagicMock(), accepted, None)
|
||||
old_client.on_disconnect(old_client, None, MagicMock(), MagicMock(), None)
|
||||
assert old_states == ["connecting"]
|
||||
|
||||
new_client.on_connect(new_client, None, MagicMock(), accepted, None)
|
||||
new_client.on_disconnect(new_client, None, MagicMock(), MagicMock(), None)
|
||||
assert new_states == ["connecting", "online", "error"]
|
||||
|
||||
|
||||
def test_source_health_rejected_connack_reports_error_without_disconnect() -> None:
|
||||
"""A failed CONNACK is distinct from a later disconnect callback."""
|
||||
manager = MqttManager()
|
||||
client = MagicMock()
|
||||
states: list[str] = []
|
||||
with patch("app.integrations.mqtt.mqtt.Client", return_value=client):
|
||||
assert manager.replace_source(
|
||||
1, host="broker.test", port=1883, username="", password="", tls_enabled=False,
|
||||
subscriptions={"topic": lambda _payload: None}, state_handler=states.append,
|
||||
)
|
||||
|
||||
accepted = MagicMock()
|
||||
accepted.is_failure = False
|
||||
refused = MagicMock()
|
||||
refused.is_failure = True
|
||||
client.on_connect(client, None, MagicMock(), accepted, None)
|
||||
client.on_connect(client, None, MagicMock(), refused, None)
|
||||
|
||||
assert states == ["connecting", "online", "error"]
|
||||
assert 1 not in manager._source_connected
|
||||
|
||||
|
||||
def test_source_health_disconnect_then_reconnect_connack_is_ordered_and_isolated() -> None:
|
||||
"""One source's reconnect sequence cannot change another source's health."""
|
||||
manager = MqttManager()
|
||||
first_client = MagicMock()
|
||||
second_client = MagicMock()
|
||||
first_states: list[str] = []
|
||||
second_states: list[str] = []
|
||||
kwargs = {
|
||||
"host": "broker.test",
|
||||
"port": 1883,
|
||||
"username": "",
|
||||
"password": "",
|
||||
"tls_enabled": False,
|
||||
"subscriptions": {"topic": lambda _payload: None},
|
||||
}
|
||||
with patch("app.integrations.mqtt.mqtt.Client", side_effect=[first_client, second_client]):
|
||||
assert manager.replace_source(1, **kwargs, state_handler=first_states.append)
|
||||
assert manager.replace_source(2, **kwargs, state_handler=second_states.append)
|
||||
|
||||
accepted = MagicMock()
|
||||
accepted.is_failure = False
|
||||
first_client.on_connect(first_client, None, MagicMock(), accepted, None)
|
||||
first_client.on_disconnect(first_client, None, MagicMock(), MagicMock(), None)
|
||||
first_client.on_connect(first_client, None, MagicMock(), accepted, None)
|
||||
|
||||
assert first_states == ["connecting", "online", "error", "online"]
|
||||
assert second_states == ["connecting"]
|
||||
|
||||
|
||||
def test_source_teardown_with_joining_loop_stop_waits_for_callback_before_aba() -> None:
|
||||
"""loop_stop may join a callback that needs the manager lock to finish."""
|
||||
manager = MqttManager()
|
||||
|
||||
Reference in New Issue
Block a user