Download tests/integration/test_fastapi_server.py from harry1911/sentinel: direct link, hf CLI and curl.
- Browser
- Download file 6.73 kB
-
https://huggingface.co/spaces/harry1911/sentinel/resolve/main/tests/integration/test_fastapi_server.py
- Command line
-
hf download hf://spaces/harry1911/sentinel/tests/integration/test_fastapi_server.py
-
curl -L -o test_fastapi_server.py https://huggingface.co/spaces/harry1911/sentinel/resolve/main/tests/integration/test_fastapi_server.py
6.73 kB
| """Integration tests for the SENTINEL FastAPI server. | |
| Tests full episode flows via TestClient covering: | |
| - Requirements 1.5, 1.6 | |
| """ | |
| import pytest | |
| from fastapi.testclient import TestClient | |
| import sentinel.api.server as server_module | |
| from sentinel.api.server import app | |
| # --------------------------------------------------------------------------- | |
| # Action payloads | |
| # --------------------------------------------------------------------------- | |
| QUERY_LOGS = { | |
| "agent": "holmes", | |
| "category": "investigative", | |
| "name": "QueryLogs", | |
| "params": {"service": "cart-service", "time_range": [0, 60]}, | |
| } | |
| QUERY_METRICS = { | |
| "agent": "argus", | |
| "category": "investigative", | |
| "name": "QueryMetrics", | |
| "params": {"service": "order-service", "metric_name": "cpu", "time_range": [0, 60]}, | |
| } | |
| FORM_HYPOTHESIS = { | |
| "agent": "holmes", | |
| "category": "investigative", | |
| "name": "FormHypothesis", | |
| "params": {"service": "cart-service", "failure_type": "memory_leak", "confidence": 0.7}, | |
| } | |
| RESTART_SERVICE = { | |
| "agent": "forge", | |
| "category": "remediation", | |
| "name": "RestartService", | |
| "params": {"service": "cart-service"}, | |
| } | |
| SCALE_SERVICE = { | |
| "agent": "forge", | |
| "category": "remediation", | |
| "name": "ScaleService", | |
| "params": {"service": "redis-cache", "replicas": 3}, | |
| } | |
| CLOSE_INCIDENT = { | |
| "agent": "hermes", | |
| "category": "meta", | |
| "name": "CloseIncident", | |
| "params": {"resolution_summary": "Incident resolved"}, | |
| } | |
| QUERY_TRACE = { | |
| "agent": "holmes", | |
| "category": "investigative", | |
| "name": "QueryTrace", | |
| "params": {"trace_id": "trace-001"}, | |
| } | |
| CANARY_DEPLOY = { | |
| "agent": "hermes", | |
| "category": "deployment", | |
| "name": "CanaryDeploy", | |
| "params": {"service": "cart-service", "version": "v2", "traffic_percent": 0.1}, | |
| } | |
| # --------------------------------------------------------------------------- | |
| # Observation keys expected in every step response | |
| # --------------------------------------------------------------------------- | |
| OBS_KEYS = { | |
| "metrics_snapshot", | |
| "causal_graph_snapshot", | |
| "active_alerts", | |
| "recent_logs", | |
| "active_traces", | |
| "incident_context", | |
| "sla_state", | |
| } | |
| def _assert_step_response(data: dict) -> None: | |
| """Validate the structure of a /step response body.""" | |
| assert "observation" in data | |
| assert "reward" in data | |
| assert "terminated" in data | |
| assert "truncated" in data | |
| assert "info" in data | |
| assert isinstance(data["observation"], dict) | |
| assert OBS_KEYS.issubset(data["observation"].keys()) | |
| assert isinstance(data["reward"], (int, float)) | |
| assert isinstance(data["terminated"], bool) | |
| assert isinstance(data["truncated"], bool) | |
| assert isinstance(data["info"], dict) | |
| # --------------------------------------------------------------------------- | |
| # Fixture | |
| # --------------------------------------------------------------------------- | |
| def reset_server_env(): | |
| """Reset the server's _env singleton before and after each test.""" | |
| server_module._env = None | |
| yield | |
| if server_module._env is not None: | |
| try: | |
| server_module._env.close() | |
| except Exception: | |
| pass | |
| server_module._env = None | |
| # --------------------------------------------------------------------------- | |
| # Tests | |
| # --------------------------------------------------------------------------- | |
| def test_full_episode_investigative_actions(): | |
| """Full episode: investigative actions + render + close. | |
| Validates: Requirements 1.5, 1.6 | |
| """ | |
| client = TestClient(app, raise_server_exceptions=False) | |
| # Reset | |
| resp = client.post("/reset", json={"seed": 42}) | |
| assert resp.status_code == 200 | |
| reset_data = resp.json() | |
| assert "observation" in reset_data | |
| assert "info" in reset_data | |
| # Step 1: QueryLogs | |
| resp = client.post("/step", json={"action": QUERY_LOGS}) | |
| assert resp.status_code == 200 | |
| _assert_step_response(resp.json()) | |
| # Step 2: QueryMetrics | |
| resp = client.post("/step", json={"action": QUERY_METRICS}) | |
| assert resp.status_code == 200 | |
| _assert_step_response(resp.json()) | |
| # Step 3: FormHypothesis | |
| resp = client.post("/step", json={"action": FORM_HYPOTHESIS}) | |
| assert resp.status_code == 200 | |
| _assert_step_response(resp.json()) | |
| # Render | |
| resp = client.get("/render") | |
| assert resp.status_code == 200 | |
| assert "render" in resp.json() | |
| # Close | |
| resp = client.post("/close") | |
| assert resp.status_code == 200 | |
| assert resp.json()["status"] == "closed" | |
| def test_full_episode_remediation_and_close(): | |
| """Full episode: remediation actions ending with CloseIncident. | |
| Validates: Requirements 1.5, 1.6 | |
| """ | |
| client = TestClient(app, raise_server_exceptions=False) | |
| # Reset | |
| resp = client.post("/reset", json={"seed": 7}) | |
| assert resp.status_code == 200 | |
| # Step 1: RestartService | |
| resp = client.post("/step", json={"action": RESTART_SERVICE}) | |
| assert resp.status_code == 200 | |
| _assert_step_response(resp.json()) | |
| # Step 2: ScaleService | |
| resp = client.post("/step", json={"action": SCALE_SERVICE}) | |
| assert resp.status_code == 200 | |
| _assert_step_response(resp.json()) | |
| # Step 3: CloseIncident — should terminate the episode | |
| resp = client.post("/step", json={"action": CLOSE_INCIDENT}) | |
| assert resp.status_code == 200 | |
| data = resp.json() | |
| _assert_step_response(data) | |
| assert data["terminated"] is True | |
| def test_full_episode_deployment_flow(): | |
| """Full episode: trace query + canary deploy + close, then health check. | |
| Validates: Requirements 1.5, 1.6 | |
| """ | |
| client = TestClient(app, raise_server_exceptions=False) | |
| # Reset | |
| resp = client.post("/reset", json={"seed": 99}) | |
| assert resp.status_code == 200 | |
| # Step 1: QueryTrace | |
| resp = client.post("/step", json={"action": QUERY_TRACE}) | |
| assert resp.status_code == 200 | |
| _assert_step_response(resp.json()) | |
| # Step 2: CanaryDeploy | |
| resp = client.post("/step", json={"action": CANARY_DEPLOY}) | |
| assert resp.status_code == 200 | |
| _assert_step_response(resp.json()) | |
| # Step 3: CloseIncident | |
| resp = client.post("/step", json={"action": CLOSE_INCIDENT}) | |
| assert resp.status_code == 200 | |
| _assert_step_response(resp.json()) | |
| # Health check — env still alive (not closed), step_count >= 3 | |
| resp = client.get("/health") | |
| assert resp.status_code == 200 | |
| health = resp.json() | |
| assert health["initialized"] is True | |
| assert health["step_count"] >= 3 | |