from datetime import UTC, datetime, timedelta
from worker_watcher.alerts import build_alert_summary
NOW = datetime(2026, 8, 1, 20, 0, tzinfo=UTC)
def health(**overrides):
result = {
"watcher_status": "healthy", "jobs_total": 0, "jobs_healthy": 0,
"collectors_failed": 0, "database_status": "ok",
"last_cycle_finished_at": "2026-08-01T19:59:30Z",
"last_successful_cycle_at": "2026-08-01T19:59:30Z",
}
result.update(overrides)
return result
def row(instance_id, job_key, health_status="healthy", observation_status="fresh", severity="info", reason="source is healthy", **extra):
result = {"instance_id": instance_id, "job_key": job_key, "name": job_key, "health_status": health_status,
"observation_status": observation_status, "severity": severity, "status_reason": reason,
"last_failure_at": None}
result.update(extra)
return result
def event(instance_id, health_status, observation_status, occurred_at):
return {"instance_id": instance_id, "new_health_status": health_status,
"new_observation_status": observation_status, "occurred_at": occurred_at}
def test_no_alerts_is_healthy() -> None:
result = build_alert_summary(health(), [], [], NOW, 60)
assert result["overall_status"] == "healthy"
assert result["active_alerts"] == 0
assert result["oldest_problem"] is None
def test_failed_and_unknown_unavailable_are_counted_once_and_oldest_is_selected() -> None:
jobs = [
row(1, "project.youtube.graphiti", "failed", severity="critical", reason="run failed with exit code 15"),
row(2, "project.telefon.agent", "unknown", "unavailable", severity="warning", reason="collector could not read the configured source"),
]
result = build_alert_summary(health(watcher_status="degraded", jobs_total=2), jobs,
[event(1, "failed", "fresh", "2026-08-01T17:00:00Z"),
event(2, "unknown", "unavailable", "2026-08-01T18:00:00Z")], NOW, 60)
assert result["overall_status"] == "critical"
assert result["active_alerts"] == 2
assert result["failed"] == 1
assert result["unknown"] == 1
assert result["unavailable"] == 1
assert result["oldest_problem"]["job_key"] == "project.youtube.graphiti"
assert result["oldest_problem"]["age_seconds"] == 10800
def test_degraded_is_warning_and_paused_healthy_is_not_alert() -> None:
jobs = [row(1, "project.demo.worker", "degraded", severity="warning"),
row(2, "project.paused.worker", "healthy", reason="job is explicitly paused")]
result = build_alert_summary(health(watcher_status="degraded", jobs_total=2), jobs, [], NOW, 60)
assert result["overall_status"] == "warning"
assert result["active_alerts"] == 1
def test_overdue_stale_job_is_one_alert() -> None:
jobs = [row(1, "automation.overdue.worker", "stale", severity="warning", reason="expected run is overdue")]
result = build_alert_summary(health(watcher_status="degraded", jobs_total=1), jobs, [], NOW, 60)
assert result["stale"] == 1
assert result["overdue"] == 1
assert result["active_alerts"] == 1
assert result["overall_status"] == "warning"
def test_stale_watcher_data_is_unavailable_and_unknown_problem_time_stays_null() -> None:
jobs = [row(1, "project.unknown.worker", "unknown", severity="warning")]
old = (NOW - timedelta(seconds=181)).isoformat().replace("+00:00", "Z")
result = build_alert_summary(health(watcher_status="degraded", jobs_total=1, last_cycle_finished_at=old), jobs, [], NOW, 60)
assert result["overall_status"] == "unavailable"
assert result["data_is_current"] is False
assert result["oldest_problem"]["problem_since"] is None
assert result["oldest_problem"]["age_seconds"] is None