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