feat(ui): implement premium beige design system and ux refinements
This commit is contained in:
201
backend/tests/e2e/test_openapi_import.py
Normal file
201
backend/tests/e2e/test_openapi_import.py
Normal file
@@ -0,0 +1,201 @@
|
||||
"""E2E tests for OpenAPI import flow (flow 5).
|
||||
|
||||
Flow 5: paste OpenAPI spec URL -> import job -> classify endpoints ->
|
||||
review classifications -> approve -> tool generation.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from app.openapi.models import ClassificationResult, EndpointInfo
|
||||
from app.openapi.review_api import _job_store
|
||||
from tests.e2e.conftest import create_e2e_app
|
||||
|
||||
pytestmark = pytest.mark.e2e
|
||||
|
||||
|
||||
def _fake_endpoint(
|
||||
path: str = "/orders/{id}",
|
||||
method: str = "GET",
|
||||
operation_id: str = "getOrder",
|
||||
summary: str = "Get order details",
|
||||
) -> EndpointInfo:
|
||||
return EndpointInfo(
|
||||
path=path,
|
||||
method=method,
|
||||
operation_id=operation_id,
|
||||
summary=summary,
|
||||
description="",
|
||||
parameters=(),
|
||||
request_body_schema=None,
|
||||
response_schema=None,
|
||||
)
|
||||
|
||||
|
||||
def _fake_classification(
|
||||
endpoint: EndpointInfo | None = None,
|
||||
access_type: str = "read",
|
||||
needs_interrupt: bool = False,
|
||||
agent_group: str = "order_lookup",
|
||||
) -> ClassificationResult:
|
||||
return ClassificationResult(
|
||||
endpoint=endpoint or _fake_endpoint(),
|
||||
access_type=access_type,
|
||||
customer_params=["order_id"],
|
||||
agent_group=agent_group,
|
||||
confidence=0.95,
|
||||
needs_interrupt=needs_interrupt,
|
||||
)
|
||||
|
||||
|
||||
class TestFlow5OpenAPIImport:
|
||||
"""Flow 5: full OpenAPI import lifecycle."""
|
||||
|
||||
def test_import_job_lifecycle(self) -> None:
|
||||
"""Start import -> check status -> review classifications -> approve."""
|
||||
app = create_e2e_app()
|
||||
|
||||
with TestClient(app) as client:
|
||||
# Step 1: Start import job
|
||||
resp = client.post(
|
||||
"/api/openapi/import",
|
||||
json={"url": "https://api.example.com/openapi.json"},
|
||||
)
|
||||
assert resp.status_code == 202
|
||||
body = resp.json()
|
||||
assert body["status"] == "pending"
|
||||
job_id = body["job_id"]
|
||||
|
||||
# Step 2: Check job status (still pending since background task hasn't run)
|
||||
resp = client.get(f"/api/openapi/jobs/{job_id}")
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["job_id"] == job_id
|
||||
|
||||
def test_import_job_with_classifications(self) -> None:
|
||||
"""Simulate completed import and review classified endpoints."""
|
||||
app = create_e2e_app()
|
||||
|
||||
# Seed a completed job directly
|
||||
ep_read = _fake_endpoint("/orders/{id}", "GET", "getOrder", "Get order")
|
||||
ep_write = _fake_endpoint("/orders/{id}/cancel", "POST", "cancelOrder", "Cancel order")
|
||||
|
||||
clf_read = _fake_classification(ep_read, "read", False, "order_lookup")
|
||||
clf_write = _fake_classification(ep_write, "write", True, "order_actions")
|
||||
|
||||
job_id = "test-job-001"
|
||||
_job_store[job_id] = {
|
||||
"job_id": job_id,
|
||||
"status": "done",
|
||||
"spec_url": "https://api.example.com/openapi.json",
|
||||
"total_endpoints": 2,
|
||||
"classified_count": 2,
|
||||
"error_message": None,
|
||||
"classifications": [clf_read, clf_write],
|
||||
}
|
||||
|
||||
with TestClient(app) as client:
|
||||
# Step 1: Get classifications
|
||||
resp = client.get(f"/api/openapi/jobs/{job_id}/classifications")
|
||||
assert resp.status_code == 200
|
||||
classifications = resp.json()
|
||||
assert len(classifications) == 2
|
||||
|
||||
# Verify read endpoint
|
||||
read_clf = classifications[0]
|
||||
assert read_clf["access_type"] == "read"
|
||||
assert read_clf["needs_interrupt"] is False
|
||||
assert read_clf["endpoint"]["path"] == "/orders/{id}"
|
||||
|
||||
# Verify write endpoint
|
||||
write_clf = classifications[1]
|
||||
assert write_clf["access_type"] == "write"
|
||||
assert write_clf["needs_interrupt"] is True
|
||||
assert write_clf["endpoint"]["path"] == "/orders/{id}/cancel"
|
||||
|
||||
# Step 2: Update a classification
|
||||
resp = client.put(
|
||||
f"/api/openapi/jobs/{job_id}/classifications/0",
|
||||
json={
|
||||
"access_type": "write",
|
||||
"needs_interrupt": True,
|
||||
"agent_group": "order_actions",
|
||||
},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
updated = resp.json()
|
||||
assert updated["access_type"] == "write"
|
||||
assert updated["needs_interrupt"] is True
|
||||
assert updated["agent_group"] == "order_actions"
|
||||
|
||||
# Step 3: Approve the job
|
||||
resp = client.post(f"/api/openapi/jobs/{job_id}/approve")
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["status"] == "approved"
|
||||
|
||||
def test_import_nonexistent_job_returns_404(self) -> None:
|
||||
app = create_e2e_app()
|
||||
|
||||
with TestClient(app) as client:
|
||||
resp = client.get("/api/openapi/jobs/nonexistent")
|
||||
assert resp.status_code == 404
|
||||
|
||||
def test_import_invalid_url_returns_422(self) -> None:
|
||||
app = create_e2e_app()
|
||||
|
||||
with TestClient(app) as client:
|
||||
resp = client.post("/api/openapi/import", json={"url": "not-a-url"})
|
||||
assert resp.status_code == 422
|
||||
|
||||
def test_classification_index_out_of_range(self) -> None:
|
||||
app = create_e2e_app()
|
||||
|
||||
job_id = "test-job-range"
|
||||
_job_store[job_id] = {
|
||||
"job_id": job_id,
|
||||
"status": "done",
|
||||
"spec_url": "https://example.com/spec.json",
|
||||
"total_endpoints": 1,
|
||||
"classified_count": 1,
|
||||
"error_message": None,
|
||||
"classifications": [_fake_classification()],
|
||||
}
|
||||
|
||||
with TestClient(app) as client:
|
||||
resp = client.put(
|
||||
f"/api/openapi/jobs/{job_id}/classifications/99",
|
||||
json={
|
||||
"access_type": "read",
|
||||
"needs_interrupt": False,
|
||||
"agent_group": "order_lookup",
|
||||
},
|
||||
)
|
||||
assert resp.status_code == 404
|
||||
|
||||
def test_update_classification_invalid_agent_group(self) -> None:
|
||||
app = create_e2e_app()
|
||||
|
||||
job_id = "test-job-invalid"
|
||||
_job_store[job_id] = {
|
||||
"job_id": job_id,
|
||||
"status": "done",
|
||||
"spec_url": "https://example.com/spec.json",
|
||||
"total_endpoints": 1,
|
||||
"classified_count": 1,
|
||||
"error_message": None,
|
||||
"classifications": [_fake_classification()],
|
||||
}
|
||||
|
||||
with TestClient(app) as client:
|
||||
resp = client.put(
|
||||
f"/api/openapi/jobs/{job_id}/classifications/0",
|
||||
json={
|
||||
"access_type": "read",
|
||||
"needs_interrupt": False,
|
||||
"agent_group": "invalid group!", # spaces and special chars
|
||||
},
|
||||
)
|
||||
assert resp.status_code == 422
|
||||
Reference in New Issue
Block a user