SWS-12500-custom-component/tests/test_diagnostics.py

156 lines
5.2 KiB
Python

"""Tests for the SWS12500 config entry diagnostics.
These cover both branches of `async_get_config_entry_diagnostics`:
- `runtime_data.health_data` is populated and returned directly.
- `runtime_data.health_data` is None, so it falls back to the live
`health_coordinator.data` snapshot.
In both cases secret keys listed in `TO_REDACT` must be replaced by the
Home Assistant `async_redact_data` sentinel, while non-secret values pass
through untouched.
"""
from __future__ import annotations
from types import SimpleNamespace
from pytest_homeassistant_custom_component.common import MockConfigEntry
from custom_components.sws12500.const import (
API_ID,
API_KEY,
DOMAIN,
POCASI_CZ_API_ID,
POCASI_CZ_API_KEY,
WINDY_STATION_ID,
WINDY_STATION_PW,
)
from custom_components.sws12500.data import SWSRuntimeData
from custom_components.sws12500.diagnostics import async_get_config_entry_diagnostics
REDACTED = "**REDACTED**"
def _make_runtime(*, health_data, health_coordinator) -> SWSRuntimeData:
"""Build a runtime data container with lightweight stub coordinators.
Diagnostics only ever reads `health_data` and `health_coordinator.data`,
so the real coordinators can be replaced with plain stubs.
"""
return SWSRuntimeData(
coordinator=object(), # type: ignore[arg-type]
health_coordinator=health_coordinator, # type: ignore[arg-type]
last_options={},
health_data=health_data,
)
async def test_diagnostics_uses_persisted_health_data(hass) -> None:
"""When `health_data` is present it is returned and secrets are redacted."""
entry = MockConfigEntry(
domain=DOMAIN,
data={
API_ID: "secret-api-id",
API_KEY: "secret-api-key",
"name": "Station",
},
options={
WINDY_STATION_ID: "secret-windy-id",
WINDY_STATION_PW: "secret-windy-pw",
"interval": 60,
},
)
entry.add_to_hass(hass)
health_data = {
"ID": "secret-station-id",
"PASSWORD": "secret-station-pw",
POCASI_CZ_API_ID: "secret-pocasi-id",
POCASI_CZ_API_KEY: "secret-pocasi-key",
"wsid": "secret-wsid",
"wspw": "secret-wspw",
"status": "ok",
}
# A separate coordinator snapshot that must NOT be used in this branch.
health_coordinator = SimpleNamespace(data={"status": "stale-should-not-appear"})
entry.runtime_data = _make_runtime(
health_data=health_data,
health_coordinator=health_coordinator,
)
result = await async_get_config_entry_diagnostics(hass, entry)
# entry_data: secrets redacted, plain value preserved.
assert result["entry_data"][API_ID] == REDACTED
assert result["entry_data"][API_KEY] == REDACTED
assert result["entry_data"]["name"] == "Station"
# entry_options: secrets redacted, plain value preserved.
assert result["entry_options"][WINDY_STATION_ID] == REDACTED
assert result["entry_options"][WINDY_STATION_PW] == REDACTED
assert result["entry_options"]["interval"] == 60
# health_data: the persisted payload is used (not the coordinator snapshot).
assert result["health_data"]["status"] == "ok"
assert result["health_data"]["ID"] == REDACTED
assert result["health_data"]["PASSWORD"] == REDACTED
assert result["health_data"][POCASI_CZ_API_ID] == REDACTED
assert result["health_data"][POCASI_CZ_API_KEY] == REDACTED
assert result["health_data"]["wsid"] == REDACTED
assert result["health_data"]["wspw"] == REDACTED
# The original payload must be untouched (deepcopy is used internally).
assert health_data["ID"] == "secret-station-id"
async def test_diagnostics_falls_back_to_coordinator_data(hass) -> None:
"""When `health_data` is None it falls back to the coordinator snapshot."""
entry = MockConfigEntry(
domain=DOMAIN,
data={API_ID: "secret-api-id", "name": "Station"},
options={},
)
entry.add_to_hass(hass)
coordinator_data = {
"ID": "secret-station-id",
"PASSWORD": "secret-station-pw",
"status": "live",
}
health_coordinator = SimpleNamespace(data=coordinator_data)
entry.runtime_data = _make_runtime(
health_data=None,
health_coordinator=health_coordinator,
)
result = await async_get_config_entry_diagnostics(hass, entry)
assert result["entry_data"][API_ID] == REDACTED
assert result["entry_data"]["name"] == "Station"
assert result["entry_options"] == {}
# Fallback snapshot is used and redacted.
assert result["health_data"]["status"] == "live"
assert result["health_data"]["ID"] == REDACTED
assert result["health_data"]["PASSWORD"] == REDACTED
async def test_diagnostics_empty_health_data(hass) -> None:
"""A falsy fallback snapshot yields an empty health_data dict."""
entry = MockConfigEntry(domain=DOMAIN, data={}, options={})
entry.add_to_hass(hass)
health_coordinator = SimpleNamespace(data=None)
entry.runtime_data = _make_runtime(
health_data=None,
health_coordinator=health_coordinator,
)
result = await async_get_config_entry_diagnostics(hass, entry)
assert result["health_data"] == {}
assert result["entry_data"] == {}
assert result["entry_options"] == {}