Add Arkteos diagnostics
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
"""Tests hors ligne des diagnostics Arkteos."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
from pytest_homeassistant_custom_component.common import MockConfigEntry
|
||||
|
||||
from custom_components.arkteos.client import ArkteosClient
|
||||
from custom_components.arkteos.const import CONF_HOST, CONF_PORT, DEFAULT_PORT, DOMAIN
|
||||
from custom_components.arkteos.diagnostics import async_get_config_entry_diagnostics
|
||||
from custom_components.arkteos.parser import filter_values, parse_frame
|
||||
|
||||
|
||||
FIXTURES = Path(__file__).parent / "fixtures"
|
||||
HOST = "arkteos.local"
|
||||
|
||||
|
||||
def _entry() -> MockConfigEntry:
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="Arkteos arkteos.local:9641",
|
||||
data={CONF_HOST: "arkteos.local", CONF_PORT: DEFAULT_PORT},
|
||||
)
|
||||
|
||||
|
||||
def _client() -> ArkteosClient:
|
||||
return ArkteosClient(HOST, DEFAULT_PORT)
|
||||
|
||||
|
||||
def _values(name: str) -> dict[str, int | float | str]:
|
||||
return filter_values(parse_frame((FIXTURES / name).read_bytes())).accepted
|
||||
|
||||
|
||||
async def test_diagnostics_are_redacted_and_serializable(hass) -> None:
|
||||
entry = _entry()
|
||||
client = _client()
|
||||
client.available = True
|
||||
client.running = True
|
||||
client.connected = True
|
||||
client.bytes_received = 123
|
||||
client.frames_received = 4
|
||||
client.frames_rejected = 2
|
||||
client.reconnect_count = 1
|
||||
entry.runtime_data = client
|
||||
|
||||
diagnostics = await async_get_config_entry_diagnostics(hass, entry)
|
||||
|
||||
assert isinstance(diagnostics, dict)
|
||||
assert diagnostics["integration"]["domain"] == DOMAIN
|
||||
assert diagnostics["integration"]["version"] == "0.1.0"
|
||||
assert diagnostics["integration"]["configuration"][CONF_PORT] == DEFAULT_PORT
|
||||
assert HOST not in json.dumps(diagnostics)
|
||||
assert diagnostics["client"] == {
|
||||
"available": True,
|
||||
"running": True,
|
||||
"connected": True,
|
||||
"bytes_received": 123,
|
||||
"frames_received": 4,
|
||||
"frames_rejected": 2,
|
||||
"reconnect_count": 1,
|
||||
"last_frame_type": None,
|
||||
}
|
||||
assert diagnostics["received_data"] == {
|
||||
"metadata": {"received": False, "data": {}},
|
||||
"frigo": {"received": False, "data": {}},
|
||||
"regulation": {"received": False, "data": {}},
|
||||
}
|
||||
json.dumps(diagnostics)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("attribute", "fixture_name", "section"),
|
||||
(
|
||||
("latest_metadata", "metadata_95.bin", "metadata"),
|
||||
("latest_frigo_data", "frigo_163.bin", "frigo"),
|
||||
("latest_regulation_data", "regulation_227.bin", "regulation"),
|
||||
),
|
||||
)
|
||||
async def test_decoded_data_is_separated_without_raw_frames(
|
||||
hass, attribute: str, fixture_name: str, section: str
|
||||
) -> None:
|
||||
entry = _entry()
|
||||
client = _client()
|
||||
setattr(client, attribute, _values(fixture_name))
|
||||
entry.runtime_data = client
|
||||
|
||||
diagnostics = await async_get_config_entry_diagnostics(hass, entry)
|
||||
|
||||
assert diagnostics["received_data"][section]["received"]
|
||||
assert diagnostics["received_data"][section]["data"] == getattr(client, attribute)
|
||||
assert all(
|
||||
not values["received"]
|
||||
for name, values in diagnostics["received_data"].items()
|
||||
if name != section
|
||||
)
|
||||
assert (FIXTURES / fixture_name).read_bytes() not in json.dumps(diagnostics).encode()
|
||||
|
||||
|
||||
async def test_fallback_hass_data_and_missing_optional_attributes(hass) -> None:
|
||||
entry = SimpleNamespace(
|
||||
entry_id="fallback-entry",
|
||||
title=f"Arkteos {HOST}:9641",
|
||||
data={CONF_HOST: HOST},
|
||||
)
|
||||
client = _client()
|
||||
del client.connected
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = client
|
||||
|
||||
diagnostics = await async_get_config_entry_diagnostics(hass, entry)
|
||||
|
||||
assert diagnostics["client"]["available"] is False
|
||||
assert "connected" not in diagnostics["client"]
|
||||
assert diagnostics["integration"]["configuration"][CONF_PORT] == DEFAULT_PORT
|
||||
assert HOST not in json.dumps(diagnostics)
|
||||
|
||||
|
||||
async def test_sensitive_and_nonserializable_values_are_not_exposed(hass) -> None:
|
||||
entry = _entry()
|
||||
client = _client()
|
||||
client.latest_frigo_data = {
|
||||
"frame_type": "frigo",
|
||||
"exterieur_temp": 12.3,
|
||||
"token": "secret-token",
|
||||
"password": "secret-password",
|
||||
"raw": b"raw-frame",
|
||||
"reader": object(),
|
||||
}
|
||||
client._writer = object()
|
||||
entry.runtime_data = client
|
||||
|
||||
diagnostics = await async_get_config_entry_diagnostics(hass, entry)
|
||||
|
||||
serialized = json.dumps(diagnostics)
|
||||
assert "secret-token" not in serialized
|
||||
assert "secret-password" not in serialized
|
||||
assert "raw-frame" not in serialized
|
||||
assert "reader" not in diagnostics["received_data"]["frigo"]["data"]
|
||||
assert "writer" not in serialized
|
||||
assert "_task" not in serialized
|
||||
|
||||
|
||||
async def test_diagnostics_do_not_start_stop_or_connect_client(hass, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
entry = _entry()
|
||||
client = _client()
|
||||
entry.runtime_data = client
|
||||
|
||||
async def forbidden(*_args, **_kwargs) -> None:
|
||||
raise AssertionError("accès réseau ou cycle de vie interdit")
|
||||
|
||||
monkeypatch.setattr(client, "start", forbidden)
|
||||
monkeypatch.setattr(client, "stop", forbidden)
|
||||
diagnostics = await async_get_config_entry_diagnostics(hass, entry)
|
||||
|
||||
assert diagnostics["client"]["frames_rejected"] == 0
|
||||
assert "rejected_values" not in diagnostics
|
||||
Reference in New Issue
Block a user