8dad52f040
新增画布Agent会话契约、领域规则、SpacetimeDB元数据表和生成绑定。 新增api-server画布Agent会话CRUD、OSS消息文档读写、SSE消息流和生成工具编排。 新增前端右侧Agent对话面板、会话状态hook、SSE客户端、BFF客户端和相关测试。 调整画布侧栏、任务栏、小地图默认状态和面板互斥交互。 补齐画布Agent、OSS消息存储、SSE收口、后端契约和项目记忆文档。
89 lines
2.5 KiB
TypeScript
89 lines
2.5 KiB
TypeScript
import { expect, test, vi } from 'vitest';
|
|
|
|
import { readEditorAgentSseEvents } from './editorAgentSse';
|
|
|
|
function createSseResponse(chunks: string[]) {
|
|
const encoder = new TextEncoder();
|
|
return new Response(
|
|
new ReadableStream<Uint8Array>({
|
|
start(controller) {
|
|
for (const chunk of chunks) {
|
|
controller.enqueue(encoder.encode(chunk));
|
|
}
|
|
controller.close();
|
|
},
|
|
}),
|
|
{
|
|
headers: {
|
|
'Content-Type': 'text/event-stream; charset=utf-8',
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
test('readEditorAgentSseEvents parses stage, generation result and done events', async () => {
|
|
const onEvent = vi.fn();
|
|
await readEditorAgentSseEvents(
|
|
createSseResponse([
|
|
'event: stage\r\ndata: {"conversationId":"conversation-1","stage":"generating"}\r\n\r\n',
|
|
'event: generation_result\r\ndata: {"conversationId":"conversation-1","messageId":"assistant-1","toolCallId":"tool-1","toolName":"generate_image","model":"gpt-image-2","images":[{"resourceId":"resource-1","imageSrc":"/generated/1.png","thumbnailSrc":null,"width":512,"height":512}]}\r\n\r\n',
|
|
'event: done\r\ndata: {"conversationId":"conversation-1","title":"像素角色"}\r\n\r\n',
|
|
]),
|
|
onEvent,
|
|
);
|
|
|
|
expect(onEvent).toHaveBeenNthCalledWith(1, {
|
|
event: 'stage',
|
|
data: {
|
|
conversationId: 'conversation-1',
|
|
stage: 'generating',
|
|
},
|
|
});
|
|
expect(onEvent).toHaveBeenNthCalledWith(
|
|
2,
|
|
expect.objectContaining({
|
|
event: 'generation_result',
|
|
data: expect.objectContaining({
|
|
toolCallId: 'tool-1',
|
|
images: [
|
|
{
|
|
resourceId: 'resource-1',
|
|
imageSrc: '/generated/1.png',
|
|
thumbnailSrc: null,
|
|
width: 512,
|
|
height: 512,
|
|
},
|
|
],
|
|
}),
|
|
}),
|
|
);
|
|
expect(onEvent).toHaveBeenNthCalledWith(3, {
|
|
event: 'done',
|
|
data: {
|
|
conversationId: 'conversation-1',
|
|
title: '像素角色',
|
|
},
|
|
});
|
|
});
|
|
|
|
test('readEditorAgentSseEvents accepts fallback event name from message payload', async () => {
|
|
const onEvent = vi.fn();
|
|
|
|
await readEditorAgentSseEvents(
|
|
createSseResponse([
|
|
'data: {"event":"error","data":{"conversationId":"conversation-1","code":"INSUFFICIENT_BALANCE","message":"泥点不足","recoverable":true}}\n\n',
|
|
]),
|
|
onEvent,
|
|
);
|
|
|
|
expect(onEvent).toHaveBeenCalledWith({
|
|
event: 'error',
|
|
data: {
|
|
conversationId: 'conversation-1',
|
|
code: 'INSUFFICIENT_BALANCE',
|
|
message: '泥点不足',
|
|
recoverable: true,
|
|
},
|
|
});
|
|
});
|