from __future__ import annotations from datetime import datetime, timezone import pytest from app.ha.exceptions import HaUnexpectedPayloadError from app.ha.history import normalize_history_payload def test_normalize_history_payload_groups_and_sorts_numeric_states() -> None: payload = [ [ { "entity_id": "sensor.temperature", "state": "22.5", "last_changed": "2026-06-01T12:15:00+00:00", }, { "state": "21.0", "last_changed": "2026-06-01T12:00:00Z", }, ], [ { "entity_id": "sensor.humidity", "state": 45, "last_updated": "2026-06-01T12:00:00+00:00", } ], ] result = normalize_history_payload(payload) assert [series.entity_id for series in result] == [ "sensor.humidity", "sensor.temperature", ] temperature = result[1] assert [point.value for point in temperature.points] == [21.0, 22.5] assert temperature.points[0].timestamp == datetime( 2026, 6, 1, 12, 0, tzinfo=timezone.utc ) def test_normalize_history_payload_skips_non_numeric_and_non_finite_states() -> None: payload = [ [ { "entity_id": "sensor.temperature", "state": state, "last_changed": "2026-06-01T12:00:00+00:00", } for state in ("unknown", "unavailable", "nan", "inf", "-inf", True, None) ] ] assert normalize_history_payload(payload) == [] @pytest.mark.parametrize( "payload", [ {}, [{}], [["invalid"]], [[{"entity_id": "invalid", "state": "21", "last_changed": "2026-06-01"}]], [[{"entity_id": "sensor.a", "state": "21", "last_changed": "invalid"}]], [[{"state": "21", "last_changed": "2026-06-01T12:00:00+00:00"}]], [ [ { "entity_id": "sensor.a", "state": "21", "last_changed": "2026-06-01T12:00:00+00:00", }, { "entity_id": "sensor.b", "state": "22", "last_changed": "2026-06-01T12:01:00+00:00", }, ] ], ], ) def test_normalize_history_payload_rejects_malformed_structure(payload: object) -> None: with pytest.raises(HaUnexpectedPayloadError): normalize_history_payload(payload) def test_normalize_history_payload_accepts_empty_series() -> None: assert normalize_history_payload([[]]) == []