Download src/utils/transcript-export/load-complete-events.test.ts from SaylorTwift/openhands: direct link, hf CLI and curl.
- Browser
- Download file 4.75 kB
-
https://huggingface.co/SaylorTwift/openhands/resolve/main/src/utils/transcript-export/load-complete-events.test.ts
- Command line
-
hf download hf://SaylorTwift/openhands/src/utils/transcript-export/load-complete-events.test.ts
-
curl -L -o load-complete-events.test.ts https://huggingface.co/SaylorTwift/openhands/resolve/main/src/utils/transcript-export/load-complete-events.test.ts
4.75 kB
| import { describe, expect, it, vi } from "vitest"; | |
| import type { MessageEvent } from "#/types/agent-server/core"; | |
| import { | |
| loadCompleteTranscriptEvents, | |
| TRANSCRIPT_HISTORY_PAGE_SIZE, | |
| } from "./load-complete-events"; | |
| const timestamp = "2026-07-10T12:34:56.000Z"; | |
| const makeMessage = (index: number): MessageEvent => ({ | |
| id: `event-${index.toString().padStart(3, "0")}`, | |
| timestamp: new Date(Date.UTC(2026, 6, 10, 0, 0, index)).toISOString(), | |
| source: "user", | |
| llm_message: { | |
| role: "user", | |
| content: [{ type: "text", text: `Message ${index}` }], | |
| }, | |
| activated_skills: [], | |
| extended_content: [], | |
| }); | |
| describe("loadCompleteTranscriptEvents", () => { | |
| it("paginates beyond the 50 events initially loaded by the chat", async () => { | |
| const allEvents = Array.from({ length: 225 }, (_, index) => | |
| makeMessage(index), | |
| ); | |
| const loadedEvents = allEvents.slice(-50); | |
| const descendingEvents = allEvents.slice().reverse(); | |
| const searchEvents = vi.fn( | |
| async ({ limit, pageId }: { limit: number; pageId?: string }) => { | |
| const offset = Number(pageId ?? 0); | |
| const items = descendingEvents.slice(offset, offset + limit); | |
| const nextOffset = offset + items.length; | |
| return { | |
| items, | |
| next_page_id: | |
| nextOffset < descendingEvents.length ? String(nextOffset) : null, | |
| }; | |
| }, | |
| ); | |
| const result = await loadCompleteTranscriptEvents( | |
| loadedEvents, | |
| searchEvents, | |
| ); | |
| expect(result).toEqual(allEvents); | |
| expect(searchEvents).toHaveBeenCalledTimes(3); | |
| expect(searchEvents).toHaveBeenNthCalledWith(1, { | |
| limit: TRANSCRIPT_HISTORY_PAGE_SIZE, | |
| sortOrder: "TIMESTAMP_DESC", | |
| strictPagination: true, | |
| }); | |
| }); | |
| it("rejects a repeated full page when completeness cannot be proven", async () => { | |
| const page = Array.from( | |
| { length: TRANSCRIPT_HISTORY_PAGE_SIZE }, | |
| (_, index) => makeMessage(index), | |
| ).reverse(); | |
| const searchEvents = vi.fn().mockResolvedValue({ items: page }); | |
| await expect( | |
| loadCompleteTranscriptEvents(page, searchEvents), | |
| ).rejects.toThrow("cannot prove that all events were loaded"); | |
| expect(searchEvents).toHaveBeenCalledTimes(1); | |
| }); | |
| it("uses cursors without dropping events at a shared timestamp boundary", async () => { | |
| const allEvents = Array.from({ length: 125 }, (_, index) => ({ | |
| ...makeMessage(index), | |
| timestamp, | |
| })); | |
| const descendingEvents = allEvents.slice().reverse(); | |
| const searchEvents = vi.fn( | |
| async ({ limit, pageId }: { limit: number; pageId?: string }) => { | |
| const offset = Number(pageId ?? 0); | |
| const items = descendingEvents.slice(offset, offset + limit); | |
| return { | |
| items, | |
| next_page_id: | |
| offset + items.length < descendingEvents.length | |
| ? String(offset + items.length) | |
| : null, | |
| }; | |
| }, | |
| ); | |
| const result = await loadCompleteTranscriptEvents([], searchEvents); | |
| expect(result).toEqual(allEvents); | |
| expect(searchEvents).toHaveBeenNthCalledWith(2, { | |
| limit: TRANSCRIPT_HISTORY_PAGE_SIZE, | |
| pageId: "100", | |
| sortOrder: "TIMESTAMP_DESC", | |
| strictPagination: true, | |
| }); | |
| }); | |
| it("does not let live-only store events mask unfetched persisted history", async () => { | |
| const persistedEvents = Array.from({ length: 150 }, (_, index) => | |
| makeMessage(index), | |
| ); | |
| const liveEvents = Array.from({ length: 50 }, (_, index) => | |
| makeMessage(index + 150), | |
| ); | |
| const searchEvents = vi.fn( | |
| async ({ | |
| limit, | |
| timestampLt, | |
| }: { | |
| limit: number; | |
| timestampLt?: string; | |
| }) => ({ | |
| items: persistedEvents | |
| .filter((event) => | |
| timestampLt ? event.timestamp < timestampLt : true, | |
| ) | |
| .slice() | |
| .reverse() | |
| .slice(0, limit), | |
| next_page_id: null, | |
| }), | |
| ); | |
| const result = await loadCompleteTranscriptEvents( | |
| liveEvents, | |
| searchEvents, | |
| persistedEvents.length, | |
| ); | |
| expect(result).toEqual([...persistedEvents, ...liveEvents]); | |
| expect(searchEvents).toHaveBeenCalledTimes(2); | |
| }); | |
| it("rejects malformed history pages", async () => { | |
| await expect( | |
| loadCompleteTranscriptEvents([], async () => ({ items: null as never })), | |
| ).rejects.toThrow("expected page.items to be an array"); | |
| }); | |
| it("rejects a partial export when the server reports more events", async () => { | |
| await expect( | |
| loadCompleteTranscriptEvents( | |
| [], | |
| async () => ({ items: [makeMessage(1)], next_page_id: null }), | |
| 2, | |
| ), | |
| ).rejects.toThrow("Transcript history is incomplete"); | |
| }); | |
| }); | |