Files
Genarrative/src/services/creative-agent/creativeAgentSse.test.ts
2026-05-08 11:44:42 +08:00

142 lines
3.8 KiB
TypeScript

import { expect, test, vi } from 'vitest';
import {
readCreativeAgentResultFromSse,
readCreativeAgentSessionFromSse,
} from './creativeAgentSse';
function createChunkedStreamResponse(chunks: Uint8Array[]) {
const stream = new ReadableStream<Uint8Array>({
start(controller) {
for (const chunk of chunks) {
controller.enqueue(chunk);
}
controller.close();
},
});
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream; charset=utf-8',
},
});
}
test('readCreativeAgentSessionFromSse parses typed creative agent events', async () => {
const encoder = new TextEncoder();
const onEvent = vi.fn();
const session = {
sessionId: 'creative-session-1',
stage: 'waiting_template_confirmation',
inputSummary: {
text: '做一套生日拼图',
entryContext: 'creation_home',
images: [],
materialSummary: null,
unsupportedCapabilities: [],
},
messages: [],
puzzleTemplateCatalog: [],
puzzleTemplateSelection: null,
puzzleImageGenerationPlan: null,
targetBinding: null,
updatedAt: '2026-05-05T10:00:00.000Z',
};
const response = createChunkedStreamResponse([
encoder.encode(
'event: stage\r\ndata: {"sessionId":"creative-session-1","stage":"perceiving"}\r\n\r\n',
),
encoder.encode(
'event: thought_summary_delta\r\ndata: {"sessionId":"creative-session-1","thoughtId":"thought-1","textDelta":"正在理解素材"}\r\n\r\n',
),
encoder.encode(
'event: puzzle_template_catalog\r\ndata: {"sessionId":"creative-session-1","templates":[]}\r\n\r\n',
),
encoder.encode(
`event: session\r\ndata: ${JSON.stringify({ session })}\r\n\r\n`,
),
]);
await expect(
readCreativeAgentSessionFromSse(response, {
fallbackMessage: '发送失败',
incompleteMessage: '结果不完整',
onEvent,
}),
).resolves.toEqual(session);
expect(onEvent).toHaveBeenCalledWith({
event: 'stage',
data: {
sessionId: 'creative-session-1',
stage: 'perceiving',
},
});
expect(onEvent).toHaveBeenCalledWith({
event: 'thought_summary_delta',
data: {
sessionId: 'creative-session-1',
thoughtId: 'thought-1',
textDelta: '正在理解素材',
},
});
expect(onEvent).toHaveBeenCalledWith({
event: 'puzzle_template_catalog',
data: {
sessionId: 'creative-session-1',
templates: [],
},
});
});
test('readCreativeAgentResultFromSse keeps draft edit payload when present', async () => {
const encoder = new TextEncoder();
const session = {
sessionId: 'creative-session-1',
stage: 'target_ready',
inputSummary: {
text: null,
entryContext: 'creation_home',
images: [],
materialSummary: null,
unsupportedCapabilities: [],
},
messages: [],
puzzleTemplateCatalog: [],
puzzleTemplateSelection: null,
puzzleImageGenerationPlan: null,
targetBinding: null,
updatedAt: '2026-05-05T10:00:00.000Z',
};
const puzzleSession = {
sessionId: 'puzzle-session-1',
currentTurn: 1,
progressPercent: 100,
stage: 'ready_to_publish',
anchorPack: {},
draft: null,
messages: [],
lastAssistantReply: null,
publishedProfileId: null,
suggestedActions: [],
resultPreview: null,
updatedAt: '2026-05-05T10:00:00.000Z',
};
const response = createChunkedStreamResponse([
encoder.encode(
`event: draft_edit_result\ndata: ${JSON.stringify({
editInstructions: [],
session,
puzzleSession,
})}\n\n`,
),
]);
const result = await readCreativeAgentResultFromSse(response, {
fallbackMessage: '修改失败',
incompleteMessage: '结果不完整',
});
expect(result.session).toEqual(session);
expect(result.draftEditResult?.puzzleSession).toEqual(puzzleSession);
});