| """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(): |
| |
| |
| 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 |
|
|