返回 DeepSeek-Reasonix
eventStream.test.ts
根目录 / desktop / electron / src / shared / eventStream.test.ts
1 import assert from "node:assert/strict";
2 import { test } from "node:test";
3 import { DesktopEventStream, MissedEventSubscriptions, type EventRecovery } from "./eventStream.js";
4 import type { EventFrame } from "./ipc.js";
5
6 test("missed dynamic subscriptions have bounded retention and overflow still requests recovery", () => {
7 const missed = new MissedEventSubscriptions();
8 missed.add("agent:event");
9 assert.equal(missed.consume("agent:event"), true);
10 assert.equal(missed.consume("agent:event"), false, "normal recovery consumes the pending name");
11 for (let index = 0; index < 10000; index++) {
12 missed.add(`remote-tab:closed-${index}:event`);
13 assert.ok(missed.size <= 64);
14 }
15 assert.equal(missed.size, 0, "overflow retains no closed-tab event names");
16 assert.equal(missed.consume("remote-tab:closed-0:event"), true, "an evicted name still triggers read-side repair");
17 assert.equal(missed.consume("remote-tab:closed-9999:event"), true);
18 missed.clear();
19 assert.equal(missed.consume("remote-tab:closed-0:event"), false, "a new generation drops old pending state");
20 missed.add("agent:event");
21 assert.equal(missed.size, 1);
22 });
23
24 test("transport rejects stale generations and duplicate/out-of-order frames and reports sequence gaps", () => {
25 const accepted: EventFrame[] = [], repairs: EventRecovery[] = [];
26 const stream = new DesktopEventStream(frame => accepted.push(frame), event => repairs.push(event));
27 const frame = (seq: number, generation = "g1", name = "agent:event") => ({ seq, generation, name, args: [seq] });
28 assert.equal(stream.accept(frame(1)), false, "unnegotiated generations cannot enter the renderer");
29 stream.observeState({ phase: "ready", generation: "g1" });
30 assert.equal(stream.accept(frame(1)), true);
31 assert.equal(stream.accept(frame(1)), false);
32 assert.equal(stream.accept(frame(3)), true);
33 assert.equal(stream.accept(frame(2)), false);
34 assert.equal(stream.accept(frame(4, "old")), false);
35 assert.equal(stream.accept(frame(Number.NaN)), false);
36 assert.equal(stream.accept(frame(0)), false);
37 assert.equal(stream.accept(frame(0, "g1", "app:open-settings")), true, "shell events have their own sequence-free namespace");
38 assert.deepEqual(repairs.map(({ reason, expectedSeq, actualSeq }) => ({ reason, expectedSeq, actualSeq })), [
39 { reason: "generation", expectedSeq: 1, actualSeq: 0 },
40 { reason: "gap", expectedSeq: 2, actualSeq: 3 },
41 ]);
42 stream.observeState({ phase: "restarting", generation: "" });
43 assert.equal(stream.accept(frame(4)), false);
44 stream.observeState({ phase: "ready", generation: "g2" });
45 assert.equal(stream.accept(frame(5)), false, "queued frames from the previous service stay rejected");
46 assert.equal(stream.accept(frame(1, "g2")), true);
47 assert.deepEqual(accepted.map(value => `${value.generation}:${value.seq}`), ["g1:1", "g1:3", "g1:0", "g2:1"]);
48 stream.requestRecovery("subscription");
49 assert.equal(stream.recovery?.reason, "subscription");
50 assert.equal(stream.recovery?.generation, "g2");
51 });
52
52 lines TYPESCRIPT