homelab-codex-ws/services/ha-diag-agent/tests/conftest.py
Oskar Kapala 20f6761a67 feat(ha-diag-agent): UnavailableEntitiesCheck with root cause dedup
- shared aiohttp ClientSession in HAClient (Phase 1 Flag #2 fixed):
  make_session() factory, session injected at startup, closed on shutdown
- Check.run() → list[CheckResult]: clean multi-event interface
- first real diagnostic check: entity unavailable > 24h
  (INSERT OR IGNORE baseline preserves first-seen timestamp)
- root cause grouping: emit ha_integration_failed instead of N entity
  events when ≥50% of integration's entities are unavailable (≥3 min)
- alert deduplication via SQLite cooldown window (default 6h)
- recovery clears baseline + dedup for immediate re-alert
- configurable thresholds: duration, integration %, cooldown
- 38 unit tests + 7 integration tests (42 pass, 3 skip w/o live HA)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 13:41:55 +02:00

63 lines
1.8 KiB
Python

"""Shared fixtures for ha-diag-agent tests."""
from __future__ import annotations
from pathlib import Path
from typing import AsyncGenerator
from unittest.mock import AsyncMock, MagicMock
import pytest
import pytest_asyncio
from ha_diag.event_emitter import EventEmitter
from ha_diag.storage import Storage
# ---------------------------------------------------------------------------
# Filesystem fixtures
# ---------------------------------------------------------------------------
@pytest.fixture
def tmp_events_dir(tmp_path: Path) -> Path:
events = tmp_path / "events"
events.mkdir()
return events
# ---------------------------------------------------------------------------
# Storage fixture (tmp SQLite — fast, no mocking)
# ---------------------------------------------------------------------------
@pytest_asyncio.fixture
async def storage(tmp_path: Path) -> AsyncGenerator[Storage, None]:
s = Storage(tmp_path / "test.db")
await s.open()
yield s
await s.close()
# ---------------------------------------------------------------------------
# EventEmitter fixture
# ---------------------------------------------------------------------------
@pytest.fixture
def emitter(tmp_events_dir: Path) -> EventEmitter:
return EventEmitter(tmp_events_dir, node_name="test-node")
# ---------------------------------------------------------------------------
# Mock HA client fixture
# ---------------------------------------------------------------------------
@pytest.fixture
def mock_ha_client():
"""Plain HAClient mock — no context manager, just async methods."""
client = MagicMock()
client.get_api_status = AsyncMock(return_value={"message": "API running."})
client.get_states = AsyncMock(return_value=[])
client.get_entity_registry = AsyncMock(return_value=[])
return client