121 lines
4.0 KiB
Python
121 lines
4.0 KiB
Python
"""Tests for webhook health dashboard generation."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import json
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
PROJECT_ROOT = Path(__file__).parent.parent
|
|
|
|
_spec = importlib.util.spec_from_file_location(
|
|
"webhook_health_dashboard_test",
|
|
PROJECT_ROOT / "bin" / "webhook_health_dashboard.py",
|
|
)
|
|
_mod = importlib.util.module_from_spec(_spec)
|
|
sys.modules["webhook_health_dashboard_test"] = _mod
|
|
_spec.loader.exec_module(_mod)
|
|
|
|
AgentHealth = _mod.AgentHealth
|
|
check_agents = _mod.check_agents
|
|
load_history = _mod.load_history
|
|
parse_targets = _mod.parse_targets
|
|
save_history = _mod.save_history
|
|
sort_key = None
|
|
to_markdown = _mod.to_markdown
|
|
write_dashboard = _mod.write_dashboard
|
|
main = _mod.main
|
|
|
|
|
|
class TestParseTargets:
|
|
def test_defaults_when_none(self):
|
|
targets = parse_targets(None)
|
|
assert targets["allegro"].endswith(":8651/health")
|
|
assert targets["ezra"].endswith(":8652/health")
|
|
|
|
def test_parse_csv_mapping(self):
|
|
targets = parse_targets("alpha=http://a/health,beta=http://b/health")
|
|
assert targets == {
|
|
"alpha": "http://a/health",
|
|
"beta": "http://b/health",
|
|
}
|
|
|
|
|
|
class TestCheckAgents:
|
|
@patch("webhook_health_dashboard_test.probe_health")
|
|
def test_updates_last_success_for_healthy(self, mock_probe):
|
|
mock_probe.return_value = (True, 200, 42, "HTTP 200")
|
|
history = {"agents": {}, "runs": []}
|
|
results = check_agents({"allegro": "http://localhost:8651/health"}, history, timeout=1, stale_after=300)
|
|
assert len(results) == 1
|
|
assert results[0].healthy is True
|
|
assert history["agents"]["allegro"]["last_success_ts"] is not None
|
|
|
|
@patch("webhook_health_dashboard_test.probe_health")
|
|
def test_marks_stale_after_threshold(self, mock_probe):
|
|
mock_probe.return_value = (False, None, 12, "URL error: refused")
|
|
history = {
|
|
"agents": {
|
|
"allegro": {
|
|
"last_success_ts": time.time() - 301,
|
|
}
|
|
},
|
|
"runs": [],
|
|
}
|
|
results = check_agents({"allegro": "http://localhost:8651/health"}, history, timeout=1, stale_after=300)
|
|
assert results[0].healthy is False
|
|
assert results[0].stale is True
|
|
|
|
|
|
class TestMarkdown:
|
|
def test_contains_table_and_icons(self):
|
|
now = time.time()
|
|
results = [
|
|
AgentHealth("allegro", "http://localhost:8651/health", 200, True, 31, False, now - 5, now, "HTTP 200 — ok"),
|
|
AgentHealth("ezra", "http://localhost:8652/health", None, False, 14, True, now - 600, now, "URL error: refused"),
|
|
]
|
|
md = to_markdown(results, generated_at=now)
|
|
assert "| Agent | Status | HTTP |" in md
|
|
assert "🟢" in md
|
|
assert "🔴" in md
|
|
assert "Stale agents" in md
|
|
assert "ezra" in md
|
|
|
|
|
|
class TestFileIO:
|
|
def test_save_and_load_history(self, tmp_path):
|
|
path = tmp_path / "history.json"
|
|
payload = {"agents": {"a": {"last_success_ts": 1}}, "runs": []}
|
|
save_history(path, payload)
|
|
loaded = load_history(path)
|
|
assert loaded == payload
|
|
|
|
def test_write_dashboard(self, tmp_path):
|
|
out = tmp_path / "dashboard.md"
|
|
write_dashboard(out, "# Test")
|
|
assert out.read_text() == "# Test\n"
|
|
|
|
|
|
class TestMain:
|
|
@patch("webhook_health_dashboard_test.probe_health")
|
|
def test_main_writes_outputs(self, mock_probe, tmp_path):
|
|
mock_probe.return_value = (True, 200, 10, "HTTP 200")
|
|
output = tmp_path / "dashboard.md"
|
|
history = tmp_path / "history.json"
|
|
rc = main([
|
|
"--targets", "allegro=http://localhost:8651/health",
|
|
"--output", str(output),
|
|
"--history", str(history),
|
|
"--timeout", "1",
|
|
"--stale-after", "300",
|
|
])
|
|
assert rc == 0
|
|
assert output.exists()
|
|
assert history.exists()
|
|
assert "allegro" in output.read_text()
|
|
runs = json.loads(history.read_text())["runs"]
|
|
assert len(runs) == 1
|