Harden bridge state and reauthentication handling
This commit is contained in:
+96
-4
@@ -1,7 +1,7 @@
|
||||
"""Tests for MB-Secure devices and entities."""
|
||||
|
||||
from copy import deepcopy
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
@@ -82,7 +82,12 @@ def snapshot_payload() -> dict[str, object]:
|
||||
}
|
||||
|
||||
|
||||
async def setup_entry(hass: HomeAssistant) -> MockConfigEntry:
|
||||
async def setup_entry(
|
||||
hass: HomeAssistant,
|
||||
*,
|
||||
connected: bool = True,
|
||||
payload: dict[str, object] | None = None,
|
||||
) -> MockConfigEntry:
|
||||
"""Set up an entry against mocked neutral Bridge API responses."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
@@ -94,10 +99,10 @@ async def setup_entry(hass: HomeAssistant) -> MockConfigEntry:
|
||||
bridge_id="bridge-installation-1",
|
||||
bridge_version="0.1.0",
|
||||
api_version=1,
|
||||
connected=True,
|
||||
connected=connected,
|
||||
capabilities=frozenset({"snapshot", "events", "commands"}),
|
||||
)
|
||||
snapshot = BridgeSnapshot.from_dict(snapshot_payload())
|
||||
snapshot = BridgeSnapshot.from_dict(payload or snapshot_payload())
|
||||
with (
|
||||
patch(
|
||||
"custom_components.mb_secure.api.BridgeClient.async_get_info",
|
||||
@@ -190,3 +195,90 @@ async def test_snapshot_topology_changes_add_and_remove_entities(
|
||||
assert "bridge-installation-1:points:point-1" not in unique_ids
|
||||
assert "bridge-installation-1:points:point-2" in unique_ids
|
||||
assert hass.states.get(old_entry) is None
|
||||
|
||||
|
||||
async def test_panel_connection_loss_marks_all_entities_unavailable(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""A reachable bridge does not hide loss of its panel connection."""
|
||||
entry = await setup_entry(hass, connected=False)
|
||||
registry = er.async_get(hass)
|
||||
|
||||
for entity in er.async_entries_for_config_entry(registry, entry.entry_id):
|
||||
state = hass.states.get(entity.entity_id)
|
||||
assert state is not None
|
||||
assert state.state == "unavailable"
|
||||
|
||||
|
||||
async def test_unknown_binary_states_remain_unknown(hass: HomeAssistant) -> None:
|
||||
"""Unknown neutral states are never converted into a false clear state."""
|
||||
payload = snapshot_payload()
|
||||
points = payload["points"]
|
||||
outputs = payload["outputs"]
|
||||
assert isinstance(points, list) and isinstance(outputs, list)
|
||||
points[0]["state"] = "future_state"
|
||||
outputs[0]["state"] = "future_state"
|
||||
entry = await setup_entry(hass, payload=payload)
|
||||
registry = er.async_get(hass)
|
||||
|
||||
for unique_id in (
|
||||
"bridge-installation-1:points:point-1",
|
||||
"bridge-installation-1:outputs:output-1",
|
||||
):
|
||||
entity_id = next(
|
||||
entity.entity_id
|
||||
for entity in er.async_entries_for_config_entry(registry, entry.entry_id)
|
||||
if entity.unique_id == unique_id
|
||||
)
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == "unknown"
|
||||
|
||||
|
||||
async def test_entity_services_use_scoped_bridge_commands(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Entity services send only normalized commands for their own object IDs."""
|
||||
entry = await setup_entry(hass)
|
||||
coordinator = entry.runtime_data
|
||||
coordinator.client.async_arm_area = AsyncMock()
|
||||
coordinator.client.async_disarm_area = AsyncMock()
|
||||
coordinator.client.async_set_output = AsyncMock()
|
||||
coordinator.async_request_refresh = AsyncMock()
|
||||
registry = er.async_get(hass)
|
||||
|
||||
area_entity_id = registry.async_get_entity_id(
|
||||
"alarm_control_panel",
|
||||
DOMAIN,
|
||||
"bridge-installation-1:areas:area-1",
|
||||
)
|
||||
output_entity_id = registry.async_get_entity_id(
|
||||
"switch",
|
||||
DOMAIN,
|
||||
"bridge-installation-1:outputs:output-1",
|
||||
)
|
||||
assert area_entity_id is not None and output_entity_id is not None
|
||||
|
||||
await hass.services.async_call(
|
||||
"alarm_control_panel",
|
||||
"alarm_arm_away",
|
||||
{"entity_id": area_entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.services.async_call(
|
||||
"alarm_control_panel",
|
||||
"alarm_disarm",
|
||||
{"entity_id": area_entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.services.async_call(
|
||||
"switch",
|
||||
"turn_on",
|
||||
{"entity_id": output_entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
coordinator.client.async_arm_area.assert_awaited_once_with("area-1", "away")
|
||||
coordinator.client.async_disarm_area.assert_awaited_once_with("area-1")
|
||||
coordinator.client.async_set_output.assert_awaited_once_with("output-1", "on")
|
||||
assert coordinator.async_request_refresh.await_count == 3
|
||||
|
||||
Reference in New Issue
Block a user