Add anomaly and performance monitoring
Some checks failed
quality / test (3.11) (push) Has been cancelled
quality / test (3.13) (push) Has been cancelled

This commit is contained in:
2026-06-17 18:58:32 +02:00
parent 2ec2c64cba
commit 9419a9cd8c
10 changed files with 419 additions and 15 deletions

View File

@@ -1,13 +1,13 @@
from __future__ import annotations
from time import perf_counter
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from pathlib import Path
from fastapi.testclient import TestClient
from app.actuators.lifecycle import ActuatorReconciliationService
from app.actuators.models import ModelSnapshot
from app.actuators.models import JobStatus, ModelSnapshot
from app.actuators.store import ActuatorStore
from app.behavior.engine import BehaviorEngine
from app.config import Settings
@@ -407,7 +407,7 @@ def test_reconciliation_run_records_visible_job_queue(tmp_path: Path) -> None:
assert payload["jobs"][-1]["status"] == "completed"
def test_dashboard_start_path_stays_within_five_second_budget(tmp_path: Path) -> None:
def test_dashboard_start_path_stays_within_three_second_budget(tmp_path: Path) -> None:
with TestClient(app) as client:
_install_service(tmp_path)
client.get("/v1/actuators/discovery")
@@ -423,8 +423,38 @@ def test_dashboard_start_path_stays_within_five_second_budget(tmp_path: Path) ->
assert root_response.status_code == 200
assert dashboard_response.status_code == 200
assert root_elapsed < 5.0
assert dashboard_elapsed < 5.0
assert root_elapsed < 3.0
assert dashboard_elapsed < 3.0
def test_dashboard_reports_performance_budget_and_anomalies(tmp_path: Path) -> None:
with TestClient(app) as client:
_install_service(tmp_path)
client.get("/v1/actuators/discovery")
client.post("/v1/actuators", json={"actuator_entity_id": "light.abstellkammer"})
store = app.state.actuator_store
job = store.start_job(kind="training", trigger="test", summary="Langsamer Testjob")
queue = store.load_job_queue()
queue.jobs = [
item.model_copy(update={"started_at": datetime.now(timezone.utc) - timedelta(seconds=4)})
if item.job_id == job.job_id
else item
for item in queue.jobs
]
store._persist_job_queue(queue)
store.finish_job(job.job_id, status=JobStatus.COMPLETED, summary="Fertig")
dashboard_response = client.get("/v1/actuators/dashboard")
anomalies_response = client.get("/v1/actuators/anomalies")
assert dashboard_response.status_code == 200
system = dashboard_response.json()["system"]
assert system["performance_budget_ms"] == 3000
assert system["slow_job_count"] == 1
assert system["performance_status"] == "slow"
assert system["anomaly_count"] >= 1
assert anomalies_response.status_code == 200
assert anomalies_response.json()
def test_discovery_reads_entities_once_and_reuses_them(tmp_path: Path) -> None: