Files
MobilityOps/backend/tests/test_mcp_integrations.py
T
NuklearRabbit c5b7e21f81 M6: implement ITWorx MCP Hub publication
Four read-only, service-token-protected MCP provider endpoints (operations summary, attention vehicles, vehicle details, knowledge search facade). Shared-secret auth reusing the M4 callback pattern. Service-request audit trail for every call. Extracted shared operations-summary logic out of the dashboard router to avoid duplicating retrieval logic. 66 backend tests passing, ruff clean. Verified all four endpoints and audit trail directly via curl against the live stack (no live MCP Hub instance available in this environment).
2026-08-01 23:05:04 +02:00

92 lines
3.1 KiB
Python

from app.core.config import get_settings
def _headers(token: str | None = None, client_id: str = "test-mcp-client"):
settings = get_settings()
return {
"X-Service-Token": token if token is not None else settings.mcp_hub_service_token,
"X-Client-Id": client_id,
}
def test_operations_summary_requires_service_token(client):
response = client.get(
"/api/v1/integrations/mcp/operations-summary", headers=_headers(token="wrong")
)
assert response.status_code == 401
def test_operations_summary_returns_metrics(client):
response = client.get("/api/v1/integrations/mcp/operations-summary", headers=_headers())
assert response.status_code == 200
body = response.json()
assert "metrics" in body
assert body["metrics"]["available"] >= 0
def test_attention_vehicles_filters_by_severity(client):
response = client.get(
"/api/v1/integrations/mcp/attention-vehicles",
params={"minimum_severity": "high", "limit": 50},
headers=_headers(),
)
assert response.status_code == 200
rows = response.json()
assert all(r["severity"] == "high" for r in rows)
def test_attention_vehicles_respects_limit(client):
response = client.get(
"/api/v1/integrations/mcp/attention-vehicles",
params={"minimum_severity": "low", "limit": 2},
headers=_headers(),
)
assert response.status_code == 200
assert len(response.json()) <= 2
def test_vehicle_details_known_ref(client):
response = client.get(
"/api/v1/integrations/mcp/vehicles/MO-016", headers=_headers()
)
assert response.status_code == 200
body = response.json()
assert body["public_ref"] == "MO-016"
assert "registration_number" not in body # narrow read-only view, not the full record
def test_vehicle_details_unknown_ref_is_404(client):
response = client.get(
"/api/v1/integrations/mcp/vehicles/MO-999", headers=_headers()
)
assert response.status_code == 404
def test_search_knowledge_grounded_and_respects_max_sources(client):
response = client.post(
"/api/v1/integrations/mcp/search-knowledge",
json={"question": "What must I do when a vehicle returns with damage?", "max_sources": 1},
headers=_headers(),
)
assert response.status_code == 200
body = response.json()
assert body["evidence_state"] == "grounded"
assert len(body["sources"]) == 1
def test_mcp_tool_requests_are_audited(client, ops_client):
client.get("/api/v1/integrations/mcp/operations-summary", headers=_headers(client_id="probe-1"))
events = ops_client.get("/api/v1/audit", params={"action": "mcp_tool_request"}).json()
assert len(events) >= 1
assert events[0]["actor_type"] == "service"
def test_no_write_endpoints_exist_under_mcp_namespace(client):
for method, path in [
("post", "/api/v1/integrations/mcp/vehicles/MO-016"),
("put", "/api/v1/integrations/mcp/vehicles/MO-016"),
("delete", "/api/v1/integrations/mcp/vehicles/MO-016"),
]:
response = getattr(client, method)(path, headers=_headers())
assert response.status_code in (404, 405)