import { expect, test, vi } from 'vitest'; import { readCreativeAgentResultFromSse, readCreativeAgentSessionFromSse, } from './creativeAgentSse'; function createChunkedStreamResponse(chunks: Uint8Array[]) { const stream = new ReadableStream({ 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); });