Files
next-tjwater-frontend/tests/browser/support/mock-agent-api.ts
T
jiang 2d8506fea0
Generic Container CI/CD / test-build-publish (push) Successful in 2m9s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 2m39s
feat: add project-aware agent workspace
2026-10-22 18:52:18 +08:00

181 lines
6.0 KiB
TypeScript

import { responsesFixture } from "./responses-fixture";
import type { Page } from "@playwright/test";
type MockAgentApiOptions = {
model?: {
id: string;
label: string;
description: string;
icon: "bolt" | "sparkle";
};
sessions?: Array<Record<string, unknown>>;
};
export async function mockAgentApi(page: Page, options: MockAgentApiOptions = {}) {
const corsHeaders = { "Access-Control-Allow-Origin": "*" };
const model = options.model ?? {
id: "test/model",
label: "测试模型",
description: "Playwright 浏览器测试",
icon: "bolt" as const
};
const triggeredSessions = new Map<string, { sessionId: string; triggerId: string }>();
await page.route("http://127.0.0.1:8787/api/v1/**", async (route) => {
if (route.request().method() === "OPTIONS") {
await route.fulfill({
status: 204,
headers: {
"Access-Control-Allow-Headers": "Authorization, Content-Type, X-Project-Id",
"Access-Control-Allow-Methods": "DELETE, GET, PATCH, POST, OPTIONS",
"Access-Control-Allow-Origin": "*"
}
});
return;
}
const pathname = new URL(route.request().url()).pathname;
if (pathname.endsWith("/topics") && route.request().method() === "GET") {
await route.fulfill({ headers: corsHeaders, json: { topics: [] } }); return;
}
if (pathname.endsWith("/suggestions") && route.request().method() === "POST") {
await route.fulfill({ headers: corsHeaders, json: {
suggestions: [
{ question: "分析当前管网需要优先核对的压力监测点。", activity_id: null },
{ question: "检查近期供水任务的证据与待确认事项。", activity_id: null },
{ question: "解读近期分析成果并列出进一步验证的问题。", activity_id: null },
],
generated_at: "2026-10-09T07:00:00Z",
} });
return;
}
const collaborationMatch = pathname.match(/\/task\/([^/]+)\/collaboration\/session$/);
if (collaborationMatch && route.request().method() === "POST") {
await route.fulfill({
status: 201,
headers: corsHeaders,
json: {
id: `collaboration-${collaborationMatch[1]}`,
session_id: `collaboration-${collaborationMatch[1]}`,
title: "任务协作问答",
purpose: "task_collaboration",
task_id: collaborationMatch[1],
status: "active",
created_at: "2026-09-23T01:11:00Z",
updated_at: "2026-09-23T01:11:00Z"
}
});
return;
}
if (pathname.endsWith("/session") && route.request().method() === "POST") {
const body = (route.request().postDataJSON() ?? {}) as {
trigger_context?: { trigger_id?: string };
};
const triggerId = body.trigger_context?.trigger_id;
const existing = triggerId ? triggeredSessions.get(triggerId) : undefined;
const sessionId = existing?.sessionId ?? `browser-session-${triggeredSessions.size + 1}`;
if (triggerId && !existing) triggeredSessions.set(triggerId, { sessionId, triggerId });
await route.fulfill({
status: existing ? 200 : 201,
headers: corsHeaders,
json: {
session_id: sessionId,
title: "水质投诉事件调查",
status: "active",
created_at: "2026-09-22T02:00:00Z",
updated_at: "2026-09-22T02:00:00Z",
created: !existing,
event_duplicate: Boolean(existing),
trigger_id: triggerId
}
});
return;
}
const sessionMatch = pathname.match(/\/session\/([^/]+)$/);
if (sessionMatch && route.request().method() === "GET") {
const collaboration = sessionMatch[1].startsWith("collaboration-");
const stored = options.sessions?.find((session) => (session.session_id ?? session.id) === sessionMatch[1]);
await route.fulfill({
headers: corsHeaders,
json: {
id: sessionMatch[1],
session_id: sessionMatch[1],
title: stored?.title ?? (collaboration ? "任务协作问答" : "水质投诉事件调查"),
purpose: stored?.purpose ?? (collaboration ? "task_collaboration" : "ordinary_chat"),
task_id: collaboration ? sessionMatch[1].slice("collaboration-".length) : undefined,
status: "active",
created_at: "2026-09-22T02:00:00Z",
updated_at: "2026-09-22T02:00:00Z",
messages: [],
is_streaming: false
}
});
return;
}
if (pathname.endsWith("/responses")) {
await route.fulfill({
status: 200,
headers: {
...corsHeaders,
"Cache-Control": "no-cache",
"Content-Type": "text/event-stream"
},
body: responsesFixture([
{ type: "start", messageId: "assistant-browser-test" },
{ type: "text-start", id: "answer" },
{ type: "text-delta", id: "answer", delta: "已收到工况汇总请求。" },
{ type: "text-end", id: "answer" },
{ type: "finish", finishReason: "stop" }
])
});
return;
}
if (pathname.endsWith("/sessions")) {
await route.fulfill({ headers: corsHeaders, json: { sessions: options.sessions ?? [] } });
return;
}
if (pathname.endsWith("/models")) {
await route.fulfill({
headers: corsHeaders,
json: {
default_model: model.id,
models: [model]
}
});
return;
}
if (pathname.endsWith("/ui-registry")) {
await route.fulfill({
headers: corsHeaders,
json: {
schema_version: "agent-ui-registry@1",
chart_grammars: [],
components: [],
actions: []
}
});
return;
}
if (pathname.endsWith("/frontend-action-registry")) {
await route.fulfill({
headers: corsHeaders,
json: {
schema_version: "frontend-action-registry@1",
actions: []
}
});
return;
}
await route.fallback();
});
}