"""Endpoint tests for the FastAPI server.""" from fastapi.testclient import TestClient from server.app import app client = TestClient(app) def test_root_landing_page(): response = client.get("/") assert response.status_code == 200 assert "text/html" in response.headers.get("content-type", "") assert "CI/CD" in response.text def test_health_endpoint(): response = client.get("/health") assert response.status_code == 200 data = response.json() assert data["status"] == "healthy" def test_info_returns_all_tasks(): info = client.get("/info") assert info.status_code == 200 data = info.json() assert len(data.get("tasks", [])) >= 6 assert "action_space" in data assert "observation_space" in data def test_tasks_endpoint(): tasks = client.get("/tasks") assert tasks.status_code == 200 data = tasks.json() assert len(data.get("tasks", [])) >= 6 task_ids = [t["id"] for t in data["tasks"]] assert "dockerfile_syntax" in task_ids assert "multi_stage_pipeline_matrix" in task_ids def test_reset_default(): resp = client.post("/reset", json={}) assert resp.status_code == 200 data = resp.json() assert "observation" in data obs = data["observation"] assert obs["total_issues"] >= 1 assert obs["step_number"] == 0 def test_reset_specific_task(): resp = client.post("/reset", json={"task_id": "dockerfile_syntax", "scenario_id": "typo_filename"}) assert resp.status_code == 200 obs = resp.json()["observation"] assert obs["task_id"] == "dockerfile_syntax" def test_reset_with_seed(): resp1 = client.post("/reset", json={"seed": 99}) resp2 = client.post("/reset", json={"seed": 99}) assert resp1.json()["observation"]["task_id"] == resp2.json()["observation"]["task_id"] def test_reset_invalid_task(): resp = client.post("/reset", json={"task_id": "nonexistent_task"}) assert resp.status_code == 400 def test_state_without_reset(): # Force a fresh app state by not resetting — this test relies on prior reset # Just verify the endpoint returns 200 (prior test did a reset) resp = client.get("/state") assert resp.status_code == 200 data = resp.json() assert "observation" in data assert "episode_reward" in data def test_step_edit_file(): client.post("/reset", json={"task_id": "dockerfile_syntax", "scenario_id": "typo_filename"}) resp = client.post("/step", json={ "action": { "action_type": "edit_file", "edits": [{ "file_path": "Dockerfile", "old_content": "COPY requirments.txt .", "new_content": "COPY requirements.txt .", }], } }) assert resp.status_code == 200 data = resp.json() assert data["reward"] > 0 assert data["info"]["issues_fixed"] >= 1 def test_step_submit(): client.post("/reset", json={"task_id": "dockerfile_syntax"}) resp = client.post("/step", json={"action": {"action_type": "submit"}}) assert resp.status_code == 200 assert resp.json()["done"] is True def test_step_request_hint(): client.post("/reset", json={"task_id": "dockerfile_syntax"}) resp = client.post("/step", json={"action": {"action_type": "request_hint"}}) assert resp.status_code == 200 obs = resp.json()["observation"] assert obs["hints_used"] == 1 assert "Hint" in (obs.get("last_action_feedback") or "") def test_grader_endpoint(): trajectory = [ {"step": 1, "action": {"action_type": "edit_file", "edits": [{"file_path": "Dockerfile"}]}, "reward": 0.3, "done": True, "info": {"issues_fixed": 1, "issues_total": 1}}, ] resp = client.post("/grader", json={"task_id": "dockerfile_syntax", "trajectory": trajectory}) assert resp.status_code == 200 result = resp.json()["result"] assert result["score"] == 1.0 def test_grader_empty_trajectory(): resp = client.post("/grader", json={"task_id": "dockerfile_syntax", "trajectory": []}) assert resp.status_code == 200 assert resp.json()["result"]["score"] == 0.0 def test_full_episode_via_api(): """Full episode: reset -> edit -> submit -> verify score.""" client.post("/reset", json={"task_id": "dockerfile_syntax", "scenario_id": "typo_filename"}) client.post("/step", json={ "action": { "action_type": "edit_file", "edits": [{ "file_path": "Dockerfile", "old_content": "COPY requirments.txt .", "new_content": "COPY requirements.txt .", }], } }) resp = client.post("/step", json={"action": {"action_type": "submit"}}) assert resp.json()["done"] is True state = client.get("/state") assert state.json()["done"] is True assert state.json()["episode_reward"] > 0