包含本地/远程部署脚本、AI Agent 服务、Dashboard 与 QA 验证。 Co-authored-by: Cursor <cursoragent@cursor.com>
112 lines
3.3 KiB
Python
112 lines
3.3 KiB
Python
import httpx
|
|
import pytest
|
|
import respx
|
|
|
|
|
|
ISSUES_RESPONSE = [
|
|
{
|
|
"id": "issue-1",
|
|
"name": "修复登录 Bug",
|
|
"state": "state-uuid-1",
|
|
"priority": "urgent",
|
|
"assignees": ["mem-1"],
|
|
"start_date": "2026-04-01",
|
|
"due_date": "2026-04-10",
|
|
},
|
|
{
|
|
"id": "issue-2",
|
|
"name": "优化首页性能",
|
|
"state": "state-uuid-2",
|
|
"priority": "medium",
|
|
"assignees": ["mem-2"],
|
|
"start_date": None,
|
|
"due_date": None,
|
|
},
|
|
]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@respx.mock
|
|
async def test_issues_returns_list(client):
|
|
respx.get(
|
|
"http://api:8000/api/v1/workspaces/ws-demo/projects/proj-123/issues/"
|
|
).mock(return_value=httpx.Response(200, json={"results": ISSUES_RESPONSE}))
|
|
resp = await client.get(
|
|
"/data/issues",
|
|
params={"workspace_slug": "ws-demo", "project_id": "proj-123"},
|
|
)
|
|
assert resp.status_code == 200
|
|
body = resp.json()
|
|
assert isinstance(body, list)
|
|
assert len(body) == 2
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@respx.mock
|
|
async def test_issues_fields(client):
|
|
respx.get(
|
|
"http://api:8000/api/v1/workspaces/ws-demo/projects/proj-123/issues/"
|
|
).mock(return_value=httpx.Response(200, json={"results": ISSUES_RESPONSE}))
|
|
resp = await client.get(
|
|
"/data/issues",
|
|
params={"workspace_slug": "ws-demo", "project_id": "proj-123"},
|
|
)
|
|
issue = resp.json()[0]
|
|
for field in ("id", "name", "state", "priority", "assignees"):
|
|
assert field in issue
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@respx.mock
|
|
async def test_issues_forwards_assignee_filter(client):
|
|
route = respx.get(
|
|
"http://api:8000/api/v1/workspaces/ws-demo/projects/proj-123/issues/"
|
|
).mock(return_value=httpx.Response(200, json={"results": [ISSUES_RESPONSE[0]]}))
|
|
resp = await client.get(
|
|
"/data/issues",
|
|
params={
|
|
"workspace_slug": "ws-demo",
|
|
"project_id": "proj-123",
|
|
"assignee_id": "mem-1",
|
|
},
|
|
)
|
|
assert resp.status_code == 200
|
|
assert route.called
|
|
qs = str(route.calls[0].request.url)
|
|
assert "assignee" in qs or "mem-1" in qs
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@respx.mock
|
|
async def test_issues_sends_api_key(client):
|
|
route = respx.get(
|
|
"http://api:8000/api/v1/workspaces/ws-demo/projects/proj-123/issues/"
|
|
).mock(return_value=httpx.Response(200, json={"results": ISSUES_RESPONSE}))
|
|
await client.get(
|
|
"/data/issues",
|
|
params={"workspace_slug": "ws-demo", "project_id": "proj-123"},
|
|
)
|
|
assert route.calls[0].request.headers.get("x-api-key") == "test-plane-token"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@respx.mock
|
|
async def test_issues_plane_error_returns_502(client):
|
|
respx.get(
|
|
"http://api:8000/api/v1/workspaces/ws-demo/projects/proj-123/issues/"
|
|
).mock(return_value=httpx.Response(404, json={"detail": "not found"}))
|
|
resp = await client.get(
|
|
"/data/issues",
|
|
params={"workspace_slug": "ws-demo", "project_id": "proj-123"},
|
|
)
|
|
assert resp.status_code == 502
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_issues_missing_params_returns_422(client):
|
|
resp = await client.get("/data/issues", params={"workspace_slug": "ws-demo"})
|
|
assert resp.status_code == 422
|
|
|
|
resp2 = await client.get("/data/issues", params={"project_id": "proj-123"})
|
|
assert resp2.status_code == 422
|