Explorer
/opt/struktur/worker-watcher/tests/integration/test_api_and_reports.py
← Zurück ↓ Download
import json
from http.client import HTTPConnection
from http.server import ThreadingHTTPServer
from threading import Thread

from tests.conftest import app_config
from worker_watcher.api import create_handler
from worker_watcher.reports import render_json, render_markdown
from worker_watcher.watcher import Watcher


def test_api_health_and_reports(tmp_path) -> None:
    watcher = Watcher(app_config(tmp_path / "api.sqlite"), tmp_path)
    watcher.run_once()
    assert "jobs_total" in watcher.health()
    assert "failed" in render_markdown(watcher)
    assert json.loads(render_json(watcher))["watcher"]
    server = ThreadingHTTPServer(("127.0.0.1", 0), create_handler(watcher))
    thread = Thread(target=server.serve_forever, daemon=True)
    thread.start()
    try:
        connection = HTTPConnection("127.0.0.1", server.server_port, timeout=3)
        connection.request("GET", "/health/live")
        response = connection.getresponse()
        assert response.status == 200
        assert json.loads(response.read())["status"] == "live"
        connection.request("GET", "/")
        html = connection.getresponse().read().decode()
        for label in ("Letzte Aktivität", "Seit", "Betriebszustand", "Gesundheit", "Beobachtung", "Nächster Lauf / Überfällig", "Zuletzt aktualisiert", "Letzte Watcher-Prüfung", "Wird aktualisiert"):
            assert label in html
        for element_id in ("refresh", "refresh-spinner", "refresh-label", "last-updated", "last-watcher-cycle", "refresh-message"):
            assert f'id="{element_id}"' in html
        assert "aria-sort" in html
        assert "data-sort-target" in html
        connection.request("GET", "/api/v1/jobs/999999")
        assert connection.getresponse().status == 404
        agent_payload = {"agent_key": "agent.api.test", "agent_name": "API test", "agent_version": "0.1",
                         "environment": "local", "host": "test-host", "runtime": "python", "capabilities": ["heartbeat"]}
        body = json.dumps(agent_payload).encode()
        connection.request("POST", "/api/v1/agents/register", body=body, headers={"Content-Type": "application/json"})
        assert connection.getresponse().status == 201
        event = {"event_id": "api-event-1", "agent": agent_payload, "worker_key": "worker.api.test",
                 "job_key": "api.test.worker", "event_type": "heartbeat", "occurred_at": "2026-08-01T12:00:00Z",
                 "payload": {"display_name": "API worker", "stale_after_seconds": 60}}
        body = json.dumps(event).encode()
        connection.request("POST", "/api/v1/agents/events", body=body, headers={"Content-Type": "application/json"})
        assert connection.getresponse().status == 200
        connection.request("GET", "/api/v1/workers")
        assert json.loads(connection.getresponse().read())["items"][0]["worker_key"] == "worker.api.test"
        connection.request("GET", "/api/v1/collectors")
        collectors = json.loads(connection.getresponse().read())
        assert collectors["items"][0]["name"] == "generic_worker"
        connection.request("GET", "/api/v1/collectors/runs")
        runs = json.loads(connection.getresponse().read())
        assert runs["items"][0]["collector_name"] == "generic-worker"
        connection.request("GET", "/api/v1/collectors/generic_worker")
        assert json.loads(connection.getresponse().read())["type"] == "generic_worker"
        connection.request("GET", "/api/v1/alerts/summary")
        summary = json.loads(connection.getresponse().read())
        assert summary["overall_status"] == "healthy"
        assert summary["active_alerts"] == 0
        assert summary["oldest_problem"] is None
        assert "generated_at" in summary
    finally:
        server.shutdown()
        server.server_close()
        watcher.close()