fix: serialize session lifecycle transitions

This commit is contained in:
User
2026-07-15 01:37:42 +02:00
parent 08b1f4909e
commit 6747f6f8a0
10 changed files with 1402 additions and 101 deletions
+54 -9
View File
@@ -21,14 +21,34 @@ test("publish assigns monotonic ids and a subscriber replays only ids newer than
]);
});
test("clear drops stale subscribers and buffers but preserves the per-session id sequence", () => {
test("clear closes every stale subscriber once and replays post-clear events from the cursor", () => {
const hub = new SseHub();
const stale: any[] = [];
hub.subscribe("s1", (event, data, id) => stale.push({ event, data, id }));
const firstStale: any[] = [];
const secondStale: any[] = [];
const closeCalls = [0, 0];
let offFirst: () => void = () => undefined;
let offSecond: () => void = () => undefined;
offFirst = hub.subscribe(
"s1",
(event, data, id) => firstStale.push({ event, data, id }),
{ close: () => { closeCalls[0] += 1; offFirst(); } },
);
offSecond = hub.subscribe(
"s1",
(event, data, id) => secondStale.push({ event, data, id }),
{ close: () => { closeCalls[1] += 1; offSecond(); } },
);
expect(hub.publish("s1", "info", { text: "before" })).toBe(1);
hub.clear("s1");
offFirst();
offSecond();
expect(closeCalls).toEqual([1, 1]);
expect(hub.publish("s1", "info", { text: "after" })).toBe(2);
expect(hub.publish("s1", "ui_request", {
type: "ui_request",
ui_request: { id: "gate-1", widget: "select" },
})).toBe(3);
const resumed: any[] = [];
hub.subscribe(
"s1",
@@ -36,22 +56,47 @@ test("clear drops stale subscribers and buffers but preserves the per-session id
{ afterId: 1 },
);
expect(stale).toEqual([{ event: "info", data: { text: "before" }, id: 1 }]);
expect(resumed).toEqual([{ event: "info", data: { text: "after" }, id: 2 }]);
const before = [{ event: "info", data: { text: "before" }, id: 1 }];
expect(firstStale).toEqual(before);
expect(secondStale).toEqual(before);
expect(resumed).toEqual([
{ event: "info", data: { text: "after" }, id: 2 },
{
event: "ui_request",
data: { type: "ui_request", ui_request: { id: "gate-1", widget: "select" } },
id: 3,
},
]);
});
test("forget drops subscribers, buffers, and the per-session id sequence", () => {
test("forget closes every subscriber once and resets the per-session id sequence", () => {
const hub = new SseHub();
const stale: any[] = [];
hub.subscribe("s1", (event, data, id) => stale.push({ event, data, id }));
const firstStale: any[] = [];
const secondStale: any[] = [];
const closeCalls = [0, 0];
let offFirst: () => void = () => undefined;
let offSecond: () => void = () => undefined;
offFirst = hub.subscribe(
"s1",
(event, data, id) => firstStale.push({ event, data, id }),
{ close: () => { closeCalls[0] += 1; offFirst(); } },
);
offSecond = hub.subscribe(
"s1",
(event, data, id) => secondStale.push({ event, data, id }),
{ close: () => { closeCalls[1] += 1; offSecond(); } },
);
expect(hub.publish("s1", "info", { text: "before" })).toBe(1);
hub.forget("s1");
expect(closeCalls).toEqual([1, 1]);
expect(hub.publish("s1", "info", { text: "after" })).toBe(1);
const fresh: any[] = [];
hub.subscribe("s1", (event, data, id) => fresh.push({ event, data, id }));
expect(stale).toEqual([{ event: "info", data: { text: "before" }, id: 1 }]);
const before = [{ event: "info", data: { text: "before" }, id: 1 }];
expect(firstStale).toEqual(before);
expect(secondStale).toEqual(before);
expect(fresh).toEqual([{ event: "info", data: { text: "after" }, id: 1 }]);
});