返回 DeepSeek-Reasonix
runtime-status-freshness.test.ts
根目录 / desktop / frontend / src / __tests__ / runtime-status-freshness.test.ts
1 import assert from "node:assert/strict";
2 import { initialState, reducer, promptEventClock } from "../lib/useController";
3 import { findTabAfterSubmitFailure } from "../lib/turnSubmissionFailure";
4 import type { TabMeta } from "../lib/types";
5
6 const idle = { type: "backend_status", running: false, pendingPrompt: false, backgroundJobs: 0,
7 cancelRequested: false, cancellable: false, runtimeEpoch: "epoch-a", turnEventSeq: 7 } as const;
8 const now = promptEventClock();
9 const baseline = reducer(initialState, { ...idle, snapshotAt: now });
10 const optimistic = reducer(baseline, { type: "user", text: "hello", seq: 0, submissionId: "send" });
11 const rejected = reducer(optimistic, { type: "turn_submit_rejected", submissionId: "send", error: "not ready" });
12 assert.equal(reducer(rejected, { ...idle, snapshotAt: now }), rejected, "pre-submit snapshot cannot undo a later lifecycle event");
13 const fresh = promptEventClock() + 1;
14 const settled = reducer(rejected, { ...idle, snapshotAt: fresh });
15 assert.equal(settled.running, false, "same-sequence fresh idle clears a rejected optimistic send");
16 assert.equal(settled.cancellable, false);
17 assert.equal(reducer(settled, { ...idle, running: true, cancellable: true, snapshotAt: now }), settled, "out-of-order equal-sequence response cannot restore running");
18 assert.equal(reducer(settled, { ...idle, running: true, cancellable: true, snapshotAt: fresh }), settled, "duplicate snapshot is ignored");
19 assert.equal(reducer(settled, { ...idle, running: true, cancellable: true }), settled, "equal sequence without freshness cannot override state");
20 assert.equal(reducer(settled, { ...idle, running: true, cancellable: true, turnEventSeq: 6, snapshotAt: fresh + 1 }), settled, "older backend sequence is always stale");
21 const active = reducer(settled, { ...idle, running: true, cancellable: true, turnEventSeq: 8, snapshotAt: fresh + 2 });
22 assert.equal(active.running, true, "newer backend event still advances state");
23 const replaced = reducer(active, { ...idle, runtimeEpoch: "epoch-b", turnEventSeq: 1, snapshotAt: fresh + 3 });
24 assert.equal(replaced.running, false, "new runtime may restart its event sequence");
25
26 let clock = 100;
27 let finishRead: (tabs: TabMeta[]) => void = () => {};
28 const read = findTabAfterSubmitFailure({ ListTabs: () => new Promise((resolve) => { finishRead = resolve; }) }, "tab", [0], () => clock);
29 // A newer lifecycle event arrives while the older status read is in flight.
30 clock = 200;
31 finishRead([{ id: "tab", running: false }] as TabMeta[]);
32 const snapshot = await read;
33 assert.ok(snapshot && snapshot[1] < 150, "reconciliation carries read-start time, not response time");
34 const newerTurn = { ...rejected, turnLifecycleObservedAt: 150 };
35 assert.equal(reducer(newerTurn, { ...idle, snapshotAt: snapshot[1] }), newerTurn, "delayed failed-send reconciliation cannot clear a newer turn");
36 console.log(" PASS runtime snapshots use event order and read freshness without trapping optimistic state");
37
37 lines TYPESCRIPT