from __future__ import annotations import sqlite3 from pathlib import Path import pytest from starlette.testclient import TestClient import dispatcher from watchdog_store import ensure_watchdog_tables ROOT = Path(__file__).resolve().parent.parent SCHEMA_SQL = (ROOT / "config" / "schema.sql").read_text() @pytest.fixture def client(tmp_path, monkeypatch): db_path = tmp_path / "wd.db" conn = sqlite3.connect(db_path) conn.executescript(SCHEMA_SQL) ensure_watchdog_tables(conn) conn.close() monkeypatch.setattr(dispatcher.cfg.database, "path", str(db_path)) monkeypatch.setattr(dispatcher.cfg.verification, "local_llm_enabled", False) monkeypatch.setattr(dispatcher.cfg.routing, "require_vision", False) monkeypatch.setenv("NEURALWATT_API_KEY", "test-key") with TestClient(dispatcher.app) as c: yield c def test_watchdog_status_empty_db(client): resp = client.get("/admin/api/watchdog/status") assert resp.status_code == 200 data = resp.json() assert data["last_tick"] is None assert data["open_alerts"] == 0 def test_watchdog_loops_empty_db(client): resp = client.get("/admin/api/watchdog/loops") assert resp.status_code == 200 assert resp.json() == [] def test_watchdog_channels_empty_db(client): resp = client.get("/admin/api/watchdog/channels") assert resp.status_code == 200 assert resp.json() == [] def test_watchdog_channels_create(client): resp = client.post( "/admin/api/watchdog/channels", json={"channel_name": "desktop", "enabled": True, "min_severity": "warning"}, ) assert resp.status_code == 200 assert resp.json()["ok"] is True listing = client.get("/admin/api/watchdog/channels").json() assert len(listing) == 1 assert listing[0]["channel_name"] == "desktop" assert listing[0]["enabled"] == 1 def test_watchdog_channels_update_min_severity(client): client.post( "/admin/api/watchdog/channels", json={"channel_name": "desktop", "enabled": True, "min_severity": "warning"}, ) client.post( "/admin/api/watchdog/channels", json={"channel_name": "desktop", "min_severity": "critical"}, ) listing = client.get("/admin/api/watchdog/channels").json() assert listing[0]["min_severity"] == "critical" assert listing[0]["enabled"] == 1 def test_watchdog_channels_requires_name(client): resp = client.post("/admin/api/watchdog/channels", json={}) assert resp.status_code == 422 def test_watchdog_channels_invalid_severity(client): resp = client.post( "/admin/api/watchdog/channels", json={"channel_name": "x", "min_severity": "bogus"}, ) assert resp.status_code == 422 def test_watchdog_test_alert_no_channels(client): resp = client.post( "/admin/api/watchdog/test-alert", json={"severity": "warning"}, ) assert resp.status_code == 200 data = resp.json() assert data["sent"] == 0 def test_watchdog_test_alert_invalid_severity(client): resp = client.post( "/admin/api/watchdog/test-alert", json={"severity": "bogus"}, ) assert resp.status_code == 422 def test_watchdog_status_after_tick(tmp_path, monkeypatch): import sqlite3 as _sqlite3 db_path = tmp_path / "wd_ticks.db" conn = _sqlite3.connect(db_path) conn.row_factory = _sqlite3.Row conn.executescript(SCHEMA_SQL) ensure_watchdog_tables(conn) conn.execute( "INSERT INTO watchdog_ticks (ticked_at, sessions_seen, outcome) VALUES (?, ?, ?)", ("2026-09-25T10:00:00", 5, "ok"), ) conn.commit() conn.close() monkeypatch.setattr(dispatcher.cfg.database, "path", str(db_path)) monkeypatch.setattr(dispatcher.cfg.verification, "local_llm_enabled", False) monkeypatch.setattr(dispatcher.cfg.routing, "require_vision", False) monkeypatch.setenv("NEURALWATT_API_KEY", "test-key") with TestClient(dispatcher.app) as c: resp = c.get("/admin/api/watchdog/status") assert resp.status_code == 200 data = resp.json() assert data["last_tick"] is not None assert data["last_tick"]["sessions_seen"] == 5