From 11f66aee9dcc9afec775da0e20a43e5dbb033f17 Mon Sep 17 00:00:00 2001 From: weichen Date: Thu, 15 Oct 2026 07:45:31 +0000 Subject: [PATCH] Verify complete saved records beyond one event page Why: Full execution history must retain outputs after the 500-event API page boundary. Evidence: Ordered 502-event replay and nonadvancing-cursor rejection tests pass. Impact: Add regression coverage only; production source is unchanged. Refs: feature/workbench-flow-completion review testing gap --- test/run-events.test.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 test/run-events.test.ts diff --git a/test/run-events.test.ts b/test/run-events.test.ts new file mode 100644 index 0000000..bbcfde4 --- /dev/null +++ b/test/run-events.test.ts @@ -0,0 +1,26 @@ +import { afterEach, expect, it, vi } from 'vitest' +import { api } from '../src/api' +import { loadRunEvents } from '../src/runEvents' +import type { StreamEvent } from '../src/contracts' + +afterEach(() => vi.restoreAllMocks()) +function event(seq: number): StreamEvent { + return { seq, type: 'message.delta', runId: 'run', conversationId: 'conversation', ts: '2026-10-15T07:00:00Z', v: 1, payload: { index: 0, text: `已保存输出${seq}` } } +} + +it('reads a complete saved record across the 500-event boundary in order', async () => { + const query = vi.spyOn(api, 'runEventsPage') + .mockResolvedValueOnce({ events: Array.from({ length: 500 }, (_, i) => event(i + 1)) }) + .mockResolvedValueOnce({ events: [event(501), event(502)] }) + const controller = new AbortController() + const result = await loadRunEvents('conversation', 'run', controller.signal) + expect(result.map(item => item.seq)).toEqual(Array.from({ length: 502 }, (_, i) => i + 1)) + expect(query).toHaveBeenNthCalledWith(1, 'conversation', 'run', 0, controller.signal) + expect(query).toHaveBeenNthCalledWith(2, 'conversation', 'run', 500, controller.signal) +}) + +it('stops an invalid nonadvancing record cursor instead of fetching forever', async () => { + const query = vi.spyOn(api, 'runEventsPage').mockResolvedValue({ events: Array.from({ length: 500 }, (_, i) => event(i + 1)) }) + await expect(loadRunEvents('conversation', 'run', new AbortController().signal)).rejects.toThrow('cursor did not advance') + expect(query).toHaveBeenCalledTimes(2) +})