"""End-to-end API smoke test against a running ImmunoOrg server. This test is intentionally written so it skips silently when no server is reachable on ``localhost:7860`` — that way ``pytest tests`` still passes in CI / local dev where you haven't booted ``uvicorn server.main:app`` yourself. To actually exercise it, run:: uvicorn server.main:app --port 7860 & pytest tests/test_api.py -v """ from __future__ import annotations import pytest import requests BASE = "http://localhost:7860" def _server_alive() -> bool: try: r = requests.get(f"{BASE}/health", timeout=1.5) return r.status_code == 200 except requests.RequestException: return False pytestmark = pytest.mark.skipif( not _server_alive(), reason="ImmunoOrg server not running on localhost:7860 (start uvicorn server.main:app)", ) def test_health_reset_step_state(): """One round-trip through /health -> /reset -> /step -> /state.""" # /health r = requests.get(f"{BASE}/health", timeout=5) assert r.status_code == 200, r.text assert r.json()["status"] == "healthy" # /reset r = requests.post( f"{BASE}/reset", json={"task": "level1_single_attack", "difficulty": 1, "seed": 42}, timeout=10, ) assert r.status_code == 200, r.text obs = r.json()["observation"] assert obs["current_phase"] in { "detection", "containment", "rca", "refactor", "validation", } assert len(obs["visible_nodes"]) > 0 assert obs["threat_level"] >= 0.0 # /step (scan logs on first visible node) target_id = obs["visible_nodes"][0]["id"] payload = { "action": { "action_type": "tactical", "tactical_action": "scan_logs", "target": target_id, "reasoning": "Smoke-test scan from pytest.", } } r = requests.post(f"{BASE}/step", json=payload, timeout=10) assert r.status_code == 200, r.text data = r.json() assert "reward" in data assert "observation" in data # /step (diagnostic) r = requests.post( f"{BASE}/step", json={ "action": { "action_type": "diagnostic", "diagnostic_action": "identify_silo", "target": "", "reasoning": "Smoke-test silo lookup.", } }, timeout=10, ) assert r.status_code == 200, r.text # /state r = requests.get(f"{BASE}/state", timeout=5) assert r.status_code == 200, r.text state = r.json() assert state["step_count"] >= 2 assert "cumulative_reward" in state if __name__ == "__main__": if not _server_alive(): print(f"No ImmunoOrg server on {BASE} — start uvicorn server.main:app first.") else: test_health_reset_step_state() print("API smoke test passed.")