import { afterEach, beforeEach, expect, test, vi } from 'vitest'; const requestJsonMock = vi.hoisted(() => vi.fn()); const { createCreationAgentClientMock } = vi.hoisted(() => ({ createCreationAgentClientMock: vi.fn(), })); vi.mock('../creation-agent', () => ({ createCreationAgentClient: createCreationAgentClientMock, })); vi.mock('../apiClient', () => ({ requestJson: requestJsonMock, })); beforeEach(() => { vi.resetModules(); createCreationAgentClientMock.mockReset(); createCreationAgentClientMock.mockReturnValue({ createSession: vi.fn(), getSession: vi.fn(), sendMessage: vi.fn(), streamMessage: vi.fn(), executeAction: vi.fn(), }); requestJsonMock.mockReset(); }); afterEach(() => { vi.restoreAllMocks(); }); test('wooden fish creation keeps image2 generation requests alive long enough', async () => { await import('./woodenFishClient'); expect(createCreationAgentClientMock).toHaveBeenCalledWith( expect.objectContaining({ createSessionTimeoutMs: 20 * 60 * 1000, executeActionTimeoutMs: 20 * 60 * 1000, }), ); }); test('wooden fish list works uses creation works endpoint', async () => { const { woodenFishClient } = await import('./woodenFishClient'); requestJsonMock.mockResolvedValueOnce({ items: [] }); await woodenFishClient.listWorks(); expect(requestJsonMock).toHaveBeenCalledWith( '/api/creation/wooden-fish/works', { method: 'GET' }, '读取敲木鱼作品列表失败', ); }); test('wooden fish start run uses runtime guest json skeleton', async () => { const { woodenFishClient } = await import('./woodenFishClient'); requestJsonMock.mockResolvedValueOnce({ run: { runId: 'run-1' } }); await woodenFishClient.startRun('profile/1', { runtimeGuestToken: 'runtime-guest-token', }); expect(requestJsonMock).toHaveBeenCalledWith( '/api/runtime/wooden-fish/runs', expect.objectContaining({ method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: 'Bearer runtime-guest-token', }, body: JSON.stringify({ profileId: 'profile/1' }), }), '启动敲木鱼运行态失败', expect.objectContaining({ retry: expect.objectContaining({ retryUnsafeMethods: true }), skipAuth: true, skipRefresh: true, }), ); }); test('wooden fish checkpoint run keeps client event id local to the client', async () => { const { woodenFishClient } = await import('./woodenFishClient'); vi.spyOn(Date, 'now').mockReturnValue(1780000000000); requestJsonMock.mockResolvedValueOnce({ run: { runId: 'run-1' } }); await woodenFishClient.checkpointRun( 'run/1', { totalTapCount: 12, wordCounters: [{ text: '功德', count: 3 }], }, { runtimeGuestToken: 'runtime-guest-token' }, ); const [, init] = requestJsonMock.mock.calls[0]; const body = JSON.parse(init.body); expect(requestJsonMock.mock.calls[0][0]).toBe( '/api/runtime/wooden-fish/runs/run%2F1/checkpoint', ); expect(body).toEqual({ totalTapCount: 12, wordCounters: [{ text: '功德', count: 3 }], clientEventId: 'checkpoint-run/1-1780000000000', }); expect(body).not.toHaveProperty('runId'); expect(body).not.toHaveProperty('checkpointAtMs'); }); test('wooden fish finish run keeps finish event id local to the client', async () => { const { woodenFishClient } = await import('./woodenFishClient'); vi.spyOn(Date, 'now').mockReturnValue(1780000000001); requestJsonMock.mockResolvedValueOnce({ run: { runId: 'run-1' } }); await woodenFishClient.finishRun( 'run/1', { totalTapCount: 18, wordCounters: [{ text: '清净', count: 2 }], }, { runtimeGuestToken: 'runtime-guest-token' }, ); const [, init] = requestJsonMock.mock.calls[0]; const body = JSON.parse(init.body); expect(requestJsonMock.mock.calls[0][0]).toBe( '/api/runtime/wooden-fish/runs/run%2F1/finish', ); expect(body).toEqual({ totalTapCount: 18, wordCounters: [{ text: '清净', count: 2 }], clientEventId: 'finish-run/1-1780000000001', }); expect(body).not.toHaveProperty('runId'); expect(body).not.toHaveProperty('finishedAtMs'); });