"""Tests for server/routes.py — FastAPI chat API endpoints.""" from fastapi.testclient import TestClient from server.web import app client = TestClient(app) class TestChatEndpoint: def test_basic_chat(self): resp = client.post("/api/chat", json={ "message": "I need a bracket", "history": [], "backend": "mock", }) assert resp.status_code == 200 data = resp.json() assert "responses" in data assert len(data["responses"]) > 0 def test_chat_with_mentions(self): resp = client.post("/api/chat", json={ "message": "What do you think?", "history": [], "mentions": ["cnc"], "backend": "mock", }) assert resp.status_code == 200 data = resp.json() agent_ids = [r["agent_id"] for r in data["responses"]] assert "cnc" in agent_ids def test_chat_with_history(self): resp = client.post("/api/chat", json={ "message": "Make it wider", "history": [ {"role": "user", "content": "I need a bracket"}, {"role": "agent", "agent_id": "design", "content": "L-bracket suggestion."}, ], "backend": "mock", }) assert resp.status_code == 200 data = resp.json() assert "responses" in data def test_chat_empty_message_rejected(self): resp = client.post("/api/chat", json={ "message": "", "history": [], "backend": "mock", }) assert resp.status_code == 422 def test_chat_returns_design_state(self): resp = client.post("/api/chat", json={ "message": "60mm wide aluminum bracket", "history": [], "backend": "mock", }) assert resp.status_code == 200 data = resp.json() assert "design_state" in data def test_chat_at_mention_in_message(self): resp = client.post("/api/chat", json={ "message": "@engineering what thickness?", "history": [], "backend": "mock", }) assert resp.status_code == 200 data = resp.json() agent_ids = [r["agent_id"] for r in data["responses"]] assert "engineering" in agent_ids class TestReportEndpoint: def test_basic_report(self): resp = client.post("/api/report", json={ "part_name": "test_bracket", "history": [ {"role": "agent", "agent_id": "design", "content": "L-bracket design."}, {"role": "agent", "agent_id": "engineering", "content": "3mm aluminum."}, {"role": "agent", "agent_id": "cnc", "content": "3-axis OK."}, ], "backend": "mock", }) assert resp.status_code == 200 data = resp.json() assert "report" in data assert "test_bracket" in data["report"] assert "Design Decisions" in data["report"] assert "Engineering Specifications" in data["report"] assert "Manufacturing Notes" in data["report"] def test_empty_history(self): resp = client.post("/api/report", json={ "part_name": "empty_part", "history": [], "backend": "mock", }) assert resp.status_code == 200 data = resp.json() assert "report" in data class TestAgentsEndpoint: def test_list_agents(self): resp = client.get("/api/agents") assert resp.status_code == 200 data = resp.json() assert "agents" in data agent_ids = [a["id"] for a in data["agents"]] assert "design" in agent_ids assert "engineering" in agent_ids assert "cnc" in agent_ids assert "cad" in agent_ids def test_agent_has_metadata(self): resp = client.get("/api/agents") data = resp.json() agent = data["agents"][0] assert "id" in agent assert "name" in agent assert "role" in agent assert "color" in agent assert "avatar" in agent class TestPlanApproveEndpoint: def test_approve_sets_phase(self): resp = client.post("/api/plan/approve", json={ "plan": { "part_name": "bracket", "description": "test", "material": "aluminum 6061", "dimensions": {"width": 60}, "features": ["4x M6 holes"], "constraints": ["min wall 3mm"], "axis_recommendation": "3-axis", "machining_notes": [], "confidence_score": 9.0, }, "design_state": { "part_name": "bracket", "material": "steel", "dimensions": {"width": 50}, "phase": "planning", }, }) assert resp.status_code == 200 data = resp.json() assert data["design_state"]["phase"] == "approved" assert data["design_state"]["material"] == "aluminum 6061" assert data["design_state"]["dimensions"]["width"] == 60 def test_approve_merges_plan_into_state(self): resp = client.post("/api/plan/approve", json={ "plan": { "part_name": "gear", "description": "spur gear", "material": "brass", "dimensions": {"diameter": 40}, "features": [], "constraints": [], "axis_recommendation": "3-axis", "machining_notes": ["No undercuts"], "confidence_score": 8.0, }, "design_state": {"phase": "planning"}, }) data = resp.json() assert data["design_state"]["part_name"] == "gear" assert data["design_state"]["plan"]["material"] == "brass" def test_approve_preserves_notes(self): resp = client.post("/api/plan/approve", json={ "plan": { "part_name": "bracket", "description": "test", "material": "aluminum 6061", "dimensions": {"width": 60}, "features": [], "constraints": [], "axis_recommendation": "3-axis", "machining_notes": [], "confidence_score": 9.0, "notes": "Check material compatibility", }, "design_state": {"phase": "planning"}, }) assert resp.status_code == 200 data = resp.json() assert data["design_state"]["plan"]["notes"] == "Check material compatibility" class TestPlanRejectEndpoint: def test_reject_resets_phase(self): resp = client.post("/api/plan/reject", json={ "design_state": { "phase": "planning", "material": "aluminum", "plan": {"part_name": "x", "description": "", "material": "aluminum", "dimensions": {}, "features": [], "constraints": [], "axis_recommendation": "", "machining_notes": [], "confidence_score": 5.0}, }, }) assert resp.status_code == 200 data = resp.json() assert data["design_state"]["phase"] == "exploring" assert data["design_state"]["plan"] is None assert data["design_state"]["material"] == "aluminum"