108 lines
3.6 KiB
Python
108 lines
3.6 KiB
Python
from datetime import UTC, datetime, timedelta
|
|
from pathlib import Path
|
|
|
|
from app.models import HealthStatus, ProbeResult, ServiceCreate, ServiceKind, ServiceUpdate
|
|
from app.store import Store
|
|
|
|
|
|
def test_service_round_trip_and_update(tmp_path: Path) -> None:
|
|
store = Store(str(tmp_path / "servicemap.db"))
|
|
created = store.create_service(
|
|
ServiceCreate(name="Example API", kind=ServiceKind.HTTP, url="https://example.test")
|
|
)
|
|
|
|
loaded = store.get_service(created.id)
|
|
assert loaded is not None
|
|
assert loaded.name == "Example API"
|
|
assert loaded.kind == ServiceKind.HTTP
|
|
assert loaded.url == "https://example.test"
|
|
|
|
updated = store.update_service(
|
|
created.id,
|
|
ServiceUpdate(name="Example TCP", kind=ServiceKind.TCP, host="127.0.0.1", port=443),
|
|
)
|
|
assert updated is not None
|
|
assert updated.kind == ServiceKind.TCP
|
|
assert updated.host == "127.0.0.1"
|
|
assert updated.port == 443
|
|
assert updated.updated_at >= created.updated_at
|
|
|
|
|
|
def test_delete_service_cleans_downstream_dependency(tmp_path: Path) -> None:
|
|
store = Store(str(tmp_path / "servicemap.db"))
|
|
database = store.create_service(
|
|
ServiceCreate(name="Database", kind=ServiceKind.TCP, host="127.0.0.1", port=5432)
|
|
)
|
|
api = store.create_service(
|
|
ServiceCreate(
|
|
name="API",
|
|
kind=ServiceKind.HTTP,
|
|
url="https://example.test",
|
|
depends_on=[database.id],
|
|
)
|
|
)
|
|
|
|
assert store.delete_service(database.id) is True
|
|
loaded_api = store.get_service(api.id)
|
|
assert loaded_api is not None
|
|
assert loaded_api.depends_on == []
|
|
assert store.delete_service(database.id) is False
|
|
|
|
|
|
def test_stale_probe_after_service_delete_is_ignored(tmp_path: Path) -> None:
|
|
store = Store(str(tmp_path / "servicemap.db"))
|
|
service = store.create_service(
|
|
ServiceCreate(name="Temporary", kind=ServiceKind.HTTP, url="https://example.test")
|
|
)
|
|
assert store.delete_service(service.id) is True
|
|
|
|
probe = ProbeResult(
|
|
id="stale-probe",
|
|
service_id=service.id,
|
|
status=HealthStatus.HEALTHY,
|
|
latency_ms=1.0,
|
|
detail="stale",
|
|
checked_at=datetime.now(UTC),
|
|
)
|
|
assert store.save_probe(probe) == probe
|
|
assert store.probe_history(service.id) == []
|
|
|
|
|
|
def test_availability_summary_and_retention(tmp_path: Path) -> None:
|
|
store = Store(str(tmp_path / "servicemap.db"))
|
|
service = store.create_service(
|
|
ServiceCreate(name="API", kind=ServiceKind.HTTP, url="https://example.test")
|
|
)
|
|
now = datetime.now(UTC)
|
|
for index, probe_status in enumerate((HealthStatus.HEALTHY, HealthStatus.DEGRADED, HealthStatus.DOWN)):
|
|
store.save_probe(
|
|
ProbeResult(
|
|
id=f"probe-{index}",
|
|
service_id=service.id,
|
|
status=probe_status,
|
|
latency_ms=float(10 + index),
|
|
detail=probe_status.value,
|
|
checked_at=now - timedelta(minutes=index),
|
|
)
|
|
)
|
|
store.save_probe(
|
|
ProbeResult(
|
|
id="old-probe",
|
|
service_id=service.id,
|
|
status=HealthStatus.DOWN,
|
|
latency_ms=99.0,
|
|
detail="old",
|
|
checked_at=now - timedelta(days=40),
|
|
)
|
|
)
|
|
|
|
summary = store.availability_summary(service.id, hours=24)
|
|
assert summary.total_checks == 3
|
|
assert summary.healthy_checks == 1
|
|
assert summary.degraded_checks == 1
|
|
assert summary.down_checks == 1
|
|
assert summary.uptime_percent == 66.67
|
|
assert summary.average_latency_ms == 11.0
|
|
|
|
assert store.prune_probes(30) == 1
|