Initial curated ServiceMap source from 7a0a61232d05ba9097432abef6f7f4102a64d0b8
This commit is contained in:
commit
105251b9c8
96 files changed
+23587
No files matched your search
@@ -0,0 +1,186 @@
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app import main as main_module
|
||||
from app.demo import DEMO_ROOT_CAUSE_NAME, build_demo_records
|
||||
from app.models import HealthStatus, ProbeResult, ServiceCreate, ServiceKind
|
||||
from app.store import Store
|
||||
|
||||
|
||||
def _client(tmp_path: Path) -> tuple[Store, TestClient]:
|
||||
store = Store(str(tmp_path / "servicemap.db"))
|
||||
main_module.store = store
|
||||
return store, TestClient(main_module.app)
|
||||
|
||||
|
||||
def _assert_acyclic(graph: dict[str, set[str]]) -> None:
|
||||
visiting: set[str] = set()
|
||||
visited: set[str] = set()
|
||||
|
||||
def visit(service_id: str) -> None:
|
||||
assert service_id not in visiting
|
||||
if service_id in visited:
|
||||
return
|
||||
visiting.add(service_id)
|
||||
for dependency_id in graph[service_id]:
|
||||
visit(dependency_id)
|
||||
visiting.remove(service_id)
|
||||
visited.add(service_id)
|
||||
|
||||
for service_id in graph:
|
||||
visit(service_id)
|
||||
|
||||
|
||||
def test_demo_seeds_topology_health_history_availability_and_incident(tmp_path: Path) -> None:
|
||||
store, client = _client(tmp_path)
|
||||
|
||||
response = client.post("/api/v1/demo")
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result["active"] is True
|
||||
assert result["synthetic"] is True
|
||||
assert result["scenario_name"] == "Northstar Platform"
|
||||
assert result["total"] == 9
|
||||
|
||||
snapshots = client.get("/api/v1/services").json()
|
||||
assert len(snapshots) == 9
|
||||
by_name = {item["service"]["name"]: item for item in snapshots}
|
||||
assert set(by_name) == {
|
||||
"Public Endpoint",
|
||||
"Reverse Proxy",
|
||||
"Web Frontend",
|
||||
"Auth Service",
|
||||
"API Gateway",
|
||||
"PostgreSQL",
|
||||
"Redis",
|
||||
"Background Worker",
|
||||
"Object Storage",
|
||||
}
|
||||
ids = {item["service"]["id"] for item in snapshots}
|
||||
assert all(set(item["service"]["depends_on"]).issubset(ids) for item in snapshots)
|
||||
assert by_name[DEMO_ROOT_CAUSE_NAME]["latest_probe"]["status"] == HealthStatus.DOWN
|
||||
assert by_name[DEMO_ROOT_CAUSE_NAME]["effective_status"] == HealthStatus.DOWN
|
||||
assert all(item["effective_status"] != HealthStatus.UNKNOWN for item in snapshots)
|
||||
|
||||
semantic = client.get("/api/v1/topology/semantic").json()
|
||||
assert {item["name"] for item in semantic["domains"]} == {"Edge", "Application", "Data"}
|
||||
assert {item["name"] for item in semantic["applications"]} == {
|
||||
"Public Gateway",
|
||||
"Northstar App",
|
||||
"Data Platform",
|
||||
"Worker Platform",
|
||||
}
|
||||
|
||||
incidents = client.get("/api/v1/incidents").json()
|
||||
assert incidents["active"] is True
|
||||
assert incidents["candidates"][0]["service_name"] == DEMO_ROOT_CAUSE_NAME
|
||||
assert set(incidents["candidates"][0]["affected_service_names"]) == {
|
||||
"API Gateway",
|
||||
"Web Frontend",
|
||||
"Reverse Proxy",
|
||||
"Public Endpoint",
|
||||
"Background Worker",
|
||||
}
|
||||
|
||||
postgres_id = by_name[DEMO_ROOT_CAUSE_NAME]["service"]["id"]
|
||||
history = client.get(f"/api/v1/services/{postgres_id}/history?limit=50").json()
|
||||
assert len(history) == 24
|
||||
availability = client.get(f"/api/v1/services/{postgres_id}/availability").json()
|
||||
assert availability["total_checks"] == 24
|
||||
assert availability["uptime_percent"] < 100
|
||||
assert availability["down_checks"] == 2
|
||||
assert store.demo_active() is True
|
||||
|
||||
|
||||
def test_demo_reset_is_deterministic_and_exit_restores_topology_and_probes(tmp_path: Path) -> None:
|
||||
store, client = _client(tmp_path)
|
||||
original = store.create_service(
|
||||
ServiceCreate(name="Existing API", kind=ServiceKind.HTTP, url="https://existing.test")
|
||||
)
|
||||
original_probe = ProbeResult(
|
||||
id="existing-probe",
|
||||
service_id=original.id,
|
||||
status=HealthStatus.HEALTHY,
|
||||
latency_ms=12.5,
|
||||
detail="existing data",
|
||||
checked_at=datetime.now(UTC),
|
||||
)
|
||||
store.save_probe(original_probe)
|
||||
original_service_json = client.get(f"/api/v1/services/{original.id}").json()
|
||||
|
||||
first = client.post("/api/v1/demo").json()
|
||||
first_services = client.get("/api/v1/services").json()
|
||||
first_statuses = {
|
||||
item["service"]["name"]: (item["latest_probe"]["status"], item["effective_status"])
|
||||
for item in first_services
|
||||
}
|
||||
assert first["restored"] is False
|
||||
assert client.get(f"/api/v1/services/{original.id}").status_code == 404
|
||||
|
||||
reset = client.post("/api/v1/demo/reset").json()
|
||||
reset_services = client.get("/api/v1/services").json()
|
||||
reset_statuses = {
|
||||
item["service"]["name"]: (item["latest_probe"]["status"], item["effective_status"])
|
||||
for item in reset_services
|
||||
}
|
||||
assert reset["active"] is True
|
||||
assert reset["root_cause_service_name"] == DEMO_ROOT_CAUSE_NAME
|
||||
assert reset_statuses == first_statuses
|
||||
assert len(reset_services) == 9
|
||||
|
||||
exited = client.post("/api/v1/demo/exit").json()
|
||||
assert exited["active"] is False
|
||||
assert exited["restored"] is True
|
||||
assert client.get("/api/v1/demo").json() == {
|
||||
"active": False,
|
||||
"synthetic": False,
|
||||
"scenario_name": None,
|
||||
"service_count": 0,
|
||||
"root_cause_service_id": None,
|
||||
"root_cause_service_name": None,
|
||||
"affected_service_ids": [],
|
||||
"affected_service_names": [],
|
||||
"affected_count": 0,
|
||||
}
|
||||
assert client.get(f"/api/v1/services/{original.id}").json() == original_service_json
|
||||
assert store.probe_history(original.id)[0].id == original_probe.id
|
||||
|
||||
with store._connect() as db: # regression assertion for FK-clean restoration
|
||||
orphan_count = db.execute(
|
||||
"SELECT COUNT(*) FROM probe_results p LEFT JOIN services s ON s.id = p.service_id WHERE s.id IS NULL"
|
||||
).fetchone()[0]
|
||||
assert orphan_count == 0
|
||||
orphan_evidence_count = db.execute(
|
||||
"SELECT COUNT(*) FROM health_evidence e LEFT JOIN services s ON s.id = e.service_id "
|
||||
"WHERE e.service_id IS NOT NULL AND s.id IS NULL"
|
||||
).fetchone()[0]
|
||||
orphan_link_count = db.execute(
|
||||
"SELECT COUNT(*) FROM health_evidence_service_links link LEFT JOIN health_evidence e "
|
||||
"ON e.id = link.evidence_id WHERE e.id IS NULL"
|
||||
).fetchone()[0]
|
||||
assert orphan_evidence_count == 0
|
||||
assert orphan_link_count == 0
|
||||
|
||||
|
||||
def test_demo_probe_endpoints_remain_external_service_free(tmp_path: Path) -> None:
|
||||
_, client = _client(tmp_path)
|
||||
client.post("/api/v1/demo")
|
||||
snapshots = client.get("/api/v1/services").json()
|
||||
postgres_id = next(item["service"]["id"] for item in snapshots if item["service"]["name"] == DEMO_ROOT_CAUSE_NAME)
|
||||
|
||||
assert client.post(f"/api/v1/services/{postgres_id}/probe").status_code == 409
|
||||
assert client.post("/api/v1/probe-all").json() == {"requested": 0, "completed": 0, "results": []}
|
||||
|
||||
|
||||
def test_demo_records_have_valid_graph_and_stable_ids() -> None:
|
||||
fixed_now = datetime(2026, 9, 12, 10, 0, tzinfo=UTC)
|
||||
services_a, probes_a = build_demo_records(fixed_now)
|
||||
services_b, probes_b = build_demo_records(fixed_now)
|
||||
|
||||
assert [service.model_dump(mode="json") for service in services_a] == [service.model_dump(mode="json") for service in services_b]
|
||||
assert [probe.model_dump(mode="json") for probe in probes_a] == [probe.model_dump(mode="json") for probe in probes_b]
|
||||
assert len(probes_a) == 9 * 24
|
||||
graph = {service.id: set(service.depends_on) for service in services_a}
|
||||
_assert_acyclic(graph)
|
||||
Reference in new issue
Block a user