返回 DeepSeek-Reasonix
history-preparation.test.ts
根目录 / desktop / frontend / src / __tests__ / history-preparation.test.ts
1 import assert from "node:assert/strict";
2 import { HistoryPreparingError, loadPreparedHistory } from "../lib/historyPreparation";
3 import { TranscriptStore } from "../lib/transcriptStore";
4 import type { HistorySlice } from "../lib/types";
5 import { JSDOM } from "jsdom";
6 import { installDesktopHostStub } from "./desktopHostStub";
7 import { canonicalHistorySlice } from "../lib/canonicalTranscriptBackend";
8
9 let calls = 0;
10 let waits = 0;
11 const page = await loadPreparedHistory(async () => {
12 if (++calls < 3) throw new HistoryPreparingError();
13 return "ready";
14 }, () => true, async () => { waits++; });
15 assert.equal(page, "ready");
16 assert.equal(waits, 2);
17
18 let current = true;
19 let cancelledCalls = 0;
20 const cancelled = await loadPreparedHistory(async () => {
21 cancelledCalls++;
22 throw new HistoryPreparingError();
23 }, () => current, async () => { current = false; });
24 assert.equal(cancelled, undefined);
25 assert.equal(cancelledCalls, 1, "session changes stop preparation requests");
26
27 const corruption = new Error("damaged session store");
28 await assert.rejects(loadPreparedHistory(async () => { throw corruption; }, () => true,
29 async () => { assert.fail("real errors must not be retried"); }), error => error === corruption);
30
31 let finish!: (value: string) => void;
32 current = true;
33 const pending = loadPreparedHistory(() => new Promise<string>(resolve => { finish = resolve; }), () => current);
34 current = false;
35 finish("old session");
36 assert.equal(await pending, undefined, "late ready response cannot hydrate another session");
37
38 const readySlice: HistorySlice = { entries: [{ entryId: "m1", turn: 1, order: 1, message: { role: "user", content: "ready history" }, refs: [] }], nextCursor: "", hasOlder: false, totalTurns: 1, startTurn: 1, endTurn: 1, stale: false, revision: 1 };
39 let preparationCalls = 0;
40 const store = new TranscriptStore({
41 HistorySliceForTab: async () => { if (++preparationCalls === 1) throw new HistoryPreparingError(); return readySlice; },
42 HistoryContentForTab: async () => { throw new Error("unused"); },
43 }, { preparationWait: async () => {} });
44 const loaded = await store.loadLatest("tab", "session");
45 assert.equal(loaded?.totalTurns, 1, "store completes startup hydration after preparation");
46 assert.equal(preparationCalls, 2);
47
48 let resume!: () => void;
49 let waitStarted!: () => void;
50 const started = new Promise<void>(resolve => { waitStarted = resolve; });
51 let staleCalls = 0;
52 const changingStore = new TranscriptStore({
53 HistorySliceForTab: async () => { if (++staleCalls === 1) throw new HistoryPreparingError(); return readySlice; },
54 HistoryContentForTab: async () => { throw new Error("unused"); },
55 }, { preparationWait: () => new Promise<void>(resolve => { resume = resolve; waitStarted(); }) });
56 const oldLoad = changingStore.loadLatest("tab", "session");
57 await started;
58 const newLoad = await changingStore.loadLatest("tab", "session");
59 resume();
60 assert.equal(await oldLoad, undefined, "superseded store generation cannot restart its request");
61 assert.equal(newLoad?.totalTurns, 1);
62 assert.equal(staleCalls, 2, "superseded request performs no extra RPC");
63
64 let pagingCalls = 0;
65 current = true;
66 const pagingStore = new TranscriptStore({
67 HistorySliceForTab: async (_tab, req) => {
68 assert.equal("current" in req, false, "lifecycle callback cannot enter the IPC payload");
69 if (++pagingCalls > 1) throw new HistoryPreparingError();
70 return { ...readySlice, hasOlder: true, nextCursor: "older" };
71 },
72 HistoryContentForTab: async () => { throw new Error("unused"); },
73 }, { preparationWait: async () => { current = false; } });
74 await pagingStore.loadLatest("tab", "session");
75 assert.equal(await pagingStore.loadOlder("tab", "session", { current: () => current }), undefined);
76 assert.equal(pagingCalls, 2, "navigation cancels preparation of an older page");
77
78 const dom = new JSDOM("", { url: "http://localhost/" });
79 globalThis.window = dom.window as unknown as Window & typeof globalThis;
80 let remoteReads = 0;
81 const host = installDesktopHostStub({
82 SessionHistoryWindowForTab: async () => ({ status: "preparing", messages: [] }),
83 RemoteSessionHistoryWindowForTab: async () => { remoteReads++; throw new Error("wrong host"); },
84 });
85 await assert.rejects(canonicalHistorySlice("preparing-local", { cursor: "" }), HistoryPreparingError);
86 assert.equal(remoteReads, 0, "preparing local history must not fall back to remote");
87 host.uninstall();
88 dom.window.close();
89 console.log("history preparation: ready, cancellation, late completion and real errors passed");
90
90 lines TYPESCRIPT