neuralcad / tests /test_api_routes.py
CallMeDaniel's picture
test: verify plan/approve preserves notes field
c2479cb
"""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"