Files
Genarrative/src/services/image-editor/editorAgentClient.test.ts
T
kdletters 8dad52f040 实现画布Agent侧边栏聊天
新增画布Agent会话契约、领域规则、SpacetimeDB元数据表和生成绑定。
新增api-server画布Agent会话CRUD、OSS消息文档读写、SSE消息流和生成工具编排。
新增前端右侧Agent对话面板、会话状态hook、SSE客户端、BFF客户端和相关测试。
调整画布侧栏、任务栏、小地图默认状态和面板互斥交互。
补齐画布Agent、OSS消息存储、SSE收口、后端契约和项目记忆文档。
2026-07-04 12:03:58 +08:00

170 lines
5.0 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from 'vitest';
import {
createEditorAgentConversation,
deleteEditorAgentConversation,
getEditorAgentConversation,
listEditorAgentConversations,
streamEditorAgentMessage,
} from './editorAgentClient';
const requestJsonMock = vi.hoisted(() => vi.fn());
const fetchWithApiAuthMock = vi.hoisted(() => vi.fn());
vi.mock('../apiClient', () => ({
requestJson: requestJsonMock,
fetchWithApiAuth: fetchWithApiAuthMock,
}));
function createSseResponse(payload: string) {
const encoder = new TextEncoder();
return new Response(
new ReadableStream<Uint8Array>({
start(controller) {
controller.enqueue(encoder.encode(payload));
controller.close();
},
}),
{
status: 200,
headers: { 'Content-Type': 'text/event-stream; charset=utf-8' },
},
);
}
describe('editorAgentClient', () => {
afterEach(() => {
requestJsonMock.mockReset();
fetchWithApiAuthMock.mockReset();
});
it('uses the planned editor agent conversation CRUD routes', async () => {
requestJsonMock
.mockResolvedValueOnce({
conversations: [
{
conversationId: 'conversation-1',
projectId: 'project-1',
title: '角色参考',
createdAt: '2026-07-03T00:00:00.000Z',
updatedAt: '2026-07-03T00:00:00.000Z',
},
],
})
.mockResolvedValueOnce({
conversation: {
conversationId: 'conversation-2',
projectId: 'project-1',
title: '新对话',
messages: [],
createdAt: '2026-07-03T00:00:00.000Z',
updatedAt: '2026-07-03T00:01:00.000Z',
},
})
.mockResolvedValueOnce({
conversation: {
conversationId: 'conversation-1',
projectId: 'project-1',
title: '角色参考',
messages: [],
createdAt: '2026-07-03T00:00:00.000Z',
updatedAt: '2026-07-03T00:02:00.000Z',
},
})
.mockResolvedValueOnce({
conversation: {
conversationId: 'conversation-1',
projectId: 'project-1',
title: '角色参考',
messages: [],
createdAt: '2026-07-03T00:00:00.000Z',
updatedAt: '2026-07-03T00:02:00.000Z',
},
});
await listEditorAgentConversations('project-1');
await createEditorAgentConversation('project-1', { title: '新对话' });
await getEditorAgentConversation('conversation-1');
await deleteEditorAgentConversation('conversation-1');
expect(requestJsonMock).toHaveBeenNthCalledWith(
1,
'/api/editor/projects/project-1/agent-conversations',
{ method: 'GET' },
'读取画布 Agent 会话列表失败',
);
expect(requestJsonMock).toHaveBeenNthCalledWith(
2,
'/api/editor/projects/project-1/agent-conversations',
expect.objectContaining({
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: '新对话' }),
}),
'创建画布 Agent 会话失败',
);
expect(requestJsonMock).toHaveBeenNthCalledWith(
3,
'/api/editor/agent-conversations/conversation-1',
{ method: 'GET' },
'读取画布 Agent 会话失败',
);
expect(requestJsonMock).toHaveBeenNthCalledWith(
4,
'/api/editor/agent-conversations/conversation-1',
{ method: 'DELETE' },
'删除画布 Agent 会话失败',
);
});
it('posts editor agent message streams and emits typed SSE events', async () => {
fetchWithApiAuthMock.mockResolvedValueOnce(
createSseResponse(
[
'event: stage',
'data: {"conversationId":"conversation-1","stage":"thinking"}',
'',
'event: message_delta',
'data: {"conversationId":"conversation-1","messageId":"assistant-1","role":"assistant","kind":"chat","textDelta":"我先看一下"}',
'',
'event: done',
'data: {"conversationId":"conversation-1","title":"生成角色"}',
'',
'',
].join('\n'),
),
);
const events: string[] = [];
await streamEditorAgentMessage(
'conversation-1',
{
clientMessageId: 'client-message-1',
text: '帮我把角色改成像素风',
attachments: [],
},
{
onEvent: (event) => events.push(event.event),
},
);
expect(events).toEqual(['stage', 'message_delta', 'done']);
expect(fetchWithApiAuthMock).toHaveBeenCalledWith(
'/api/editor/agent-conversations/conversation-1/messages/stream',
expect.objectContaining({
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
clientMessageId: 'client-message-1',
text: '帮我把角色改成像素风',
attachments: [],
}),
}),
expect.objectContaining({
timeoutMs: 1_200_000,
authImpact: 'local',
}),
);
});
});