"""Integration tests: real HTTP layer with real sandbox, stores, and validation.""" import asyncio import httpx import pytest from arena.api import app, service from arena.codebase_archive import LocalCodebaseArchiveStore from arena.run_models import RunRequest from arena.run_store import InMemoryRunStore from arena.swarm_runtime import SwarmRuntime from arena.validator_graph import DeterministicValidatorExecutor, ValidatorGraph from conftest import make_local_session def request(method: str, path: str, **kwargs) -> httpx.Response: async def _request() -> httpx.Response: transport = httpx.ASGITransport(app=app) async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client: return await client.request(method, path, **kwargs) return asyncio.run(_request()) @pytest.fixture(autouse=True) def _reset_service() -> None: from arena.event_bus import EventBus store = LocalCodebaseArchiveStore() event_bus = EventBus() service._run_store = InMemoryRunStore() service._archive_store = store service._swarm_runtime = SwarmRuntime( session_factory=make_local_session, archive_store=store, event_bus=event_bus, ) service._validator_graph = ValidatorGraph( executor=DeterministicValidatorExecutor(), rubric_enabled=False, ) from arena.run_flow import RunFlow service._run_flow = RunFlow( run_store=service._run_store, swarm_runtime=service._swarm_runtime, validator_graph=service._validator_graph, archive_store=service._archive_store, event_bus=event_bus, ) def test_health_endpoint_returns_ok() -> None: response = request("GET", "/health") assert response.status_code == 200 assert response.json() == {"status": "ok"} def test_skills_endpoint_returns_list() -> None: response = request("GET", "/skills") assert response.status_code == 200 skills = response.json() assert isinstance(skills, list) def test_create_run_and_get_status() -> None: response = request( "POST", "/runs", json={"prompt": "Build a CLI", "user_tests": ["echo ok"]}, ) assert response.status_code == 200 data = response.json() assert "token" not in data run_id = data["id"] status_resp = request("GET", f"/runs/{run_id}") assert status_resp.status_code == 200 run = status_resp.json() assert run["id"] == run_id assert run["prompt"] == "Build a CLI" assert run["status"] == "completed" assert len(run["tasks"]) >= 1 assert run["codebase_archive"] is not None assert run["validation_report"] is not None def test_rejoin_run() -> None: response = request( "POST", "/runs", json={"prompt": "Build a CLI", "user_tests": []}, ) run_id = response.json()["id"] access = service._run_store.create_run(RunRequest(prompt="Private rejoin")) rejoin_resp = request("POST", "/runs/rejoin", json={"token": access.token}) assert rejoin_resp.status_code == 200 assert "token" not in rejoin_resp.json() assert rejoin_resp.json()["id"] == access.run.id assert rejoin_resp.json()["id"] != run_id def test_validate_run() -> None: response = request( "POST", "/runs", json={"prompt": "Build a CLI", "user_tests": ["echo ok"]}, ) run_id = response.json()["id"] validate_resp = request("POST", f"/runs/{run_id}/validate") assert validate_resp.status_code == 200 report = validate_resp.json() assert report["passed"] is True assert report["run_id"] == run_id final = request("GET", f"/runs/{run_id}") assert final.json()["status"] == "completed" assert final.json()["validation_report"] is not None def test_download_codebase_zip() -> None: response = request( "POST", "/runs", json={"prompt": "Build a CLI", "user_tests": []}, ) run_id = response.json()["id"] zip_resp = request("GET", f"/runs/{run_id}/codebase.zip") assert zip_resp.status_code == 200 assert zip_resp.headers["content-type"] == "application/zip" assert len(zip_resp.content) > 0 assert zip_resp.content[:2] == b"PK" def test_start_run_nonblocking() -> None: import time response = request( "POST", "/runs/start", json={"prompt": "Build a CLI", "user_tests": ["echo ok"]}, ) assert response.status_code == 200 run_id = response.json()["id"] assert response.json()["status"] in ("queued", "planning", "working", "validating", "completed") time.sleep(0.2) final = request("GET", f"/runs/{run_id}") assert final.json()["status"] in ("working", "validating", "completed") def test_cancel_run() -> None: response = request( "POST", "/runs", json={"prompt": "Build a CLI", "user_tests": []}, ) run_id = response.json()["id"] cancel_resp = request("POST", f"/runs/{run_id}/cancel") assert cancel_resp.status_code == 200 assert cancel_resp.json()["status"] == "completed" def test_add_run_message() -> None: response = request( "POST", "/runs", json={"prompt": "Build a CLI", "user_tests": []}, ) run_id = response.json()["id"] message_resp = request( "POST", f"/runs/{run_id}/messages", json={"content": "Please keep this active"}, ) assert message_resp.status_code == 200 assert "token" not in message_resp.json() assert message_resp.json()["events"][-1]["message"] == "User: Please keep this active" def test_run_not_found_returns_404() -> None: response = request("GET", "/runs/nonexistent") assert response.status_code == 404 def test_rejoin_bad_token_returns_404() -> None: response = request("POST", "/runs/rejoin", json={"token": "not-a-real-token"}) assert response.status_code == 404