fix: harden resume and SSE replay
This commit is contained in:
@@ -1,16 +1,93 @@
|
||||
import { test, expect } from "vitest";
|
||||
import { SseHub } from "../src/sse/sse-hub.js";
|
||||
|
||||
test("re-emette il widget pendente alla sottoscrizione", () => {
|
||||
const hub = new SseHub(); const sent: any[] = [];
|
||||
hub.subscribe("s1", (ev, data) => sent.push({ ev, data }), { id: "u1", widget: "select" });
|
||||
expect(sent[0]).toEqual({ ev: "ui_request", data: { type: "ui_request", ui_request: { id: "u1", widget: "select" } } });
|
||||
test("publish assigns monotonic ids and a subscriber replays only ids newer than its cursor", () => {
|
||||
const hub = new SseHub();
|
||||
const firstId = hub.publish("s1", "text_delta", { text: "one" });
|
||||
const secondId = hub.publish("s1", "text_delta", { text: "two" });
|
||||
const sent: any[] = [];
|
||||
|
||||
hub.subscribe(
|
||||
"s1",
|
||||
(event, data, id) => sent.push({ event, data, id }),
|
||||
{ afterId: firstId },
|
||||
);
|
||||
const thirdId = hub.publish("s1", "text_delta", { text: "three" });
|
||||
|
||||
expect([firstId, secondId, thirdId]).toEqual([1, 2, 3]);
|
||||
expect(sent).toEqual([
|
||||
{ event: "text_delta", data: { text: "two" }, id: 2 },
|
||||
{ event: "text_delta", data: { text: "three" }, id: 3 },
|
||||
]);
|
||||
});
|
||||
|
||||
test("publish raggiunge i subscriber e unsubscribe li stacca", () => {
|
||||
const hub = new SseHub(); const sent: any[] = [];
|
||||
const off = hub.subscribe("s1", (ev, data) => sent.push({ ev, data }));
|
||||
hub.publish("s1", "text_delta", { text: "x" });
|
||||
off(); hub.publish("s1", "text_delta", { text: "y" });
|
||||
expect(sent).toEqual([{ ev: "text_delta", data: { text: "x" } }]);
|
||||
test("clear drops stale subscribers and buffers but preserves the per-session id sequence", () => {
|
||||
const hub = new SseHub();
|
||||
const stale: any[] = [];
|
||||
hub.subscribe("s1", (event, data, id) => stale.push({ event, data, id }));
|
||||
expect(hub.publish("s1", "info", { text: "before" })).toBe(1);
|
||||
|
||||
hub.clear("s1");
|
||||
expect(hub.publish("s1", "info", { text: "after" })).toBe(2);
|
||||
const resumed: any[] = [];
|
||||
hub.subscribe(
|
||||
"s1",
|
||||
(event, data, id) => resumed.push({ event, data, id }),
|
||||
{ afterId: 1 },
|
||||
);
|
||||
|
||||
expect(stale).toEqual([{ event: "info", data: { text: "before" }, id: 1 }]);
|
||||
expect(resumed).toEqual([{ event: "info", data: { text: "after" }, id: 2 }]);
|
||||
});
|
||||
|
||||
test("a buffered pending gate is emitted exactly once and matched by descriptor id", () => {
|
||||
const hub = new SseHub();
|
||||
const descriptor = { id: "gate-1", widget: "select", title: "Original" };
|
||||
hub.publish("s1", "ui_request", { type: "ui_request", ui_request: descriptor });
|
||||
const replayed: any[] = [];
|
||||
|
||||
hub.subscribe(
|
||||
"s1",
|
||||
(event, data, id) => replayed.push({ event, data, id }),
|
||||
{ afterId: 0, pending: { ...descriptor, title: "Runtime copy" } },
|
||||
);
|
||||
|
||||
expect(replayed).toEqual([{
|
||||
event: "ui_request",
|
||||
data: { type: "ui_request", ui_request: descriptor },
|
||||
id: 1,
|
||||
}]);
|
||||
|
||||
const alreadySeen: any[] = [];
|
||||
hub.subscribe(
|
||||
"s1",
|
||||
(event, data, id) => alreadySeen.push({ event, data, id }),
|
||||
{ afterId: 1, pending: descriptor },
|
||||
);
|
||||
expect(alreadySeen).toEqual([]);
|
||||
});
|
||||
|
||||
test("an unbuffered pending gate receives one fresh buffered id", () => {
|
||||
const hub = new SseHub();
|
||||
hub.publish("s1", "info", { text: "ready" });
|
||||
const first: any[] = [];
|
||||
|
||||
hub.subscribe(
|
||||
"s1",
|
||||
(event, data, id) => first.push({ event, data, id }),
|
||||
{ afterId: 1, pending: { id: "gate-1", widget: "select" } },
|
||||
);
|
||||
expect(first).toEqual([{
|
||||
event: "ui_request",
|
||||
data: { type: "ui_request", ui_request: { id: "gate-1", widget: "select" } },
|
||||
id: 2,
|
||||
}]);
|
||||
|
||||
const reconnect: any[] = [];
|
||||
hub.subscribe(
|
||||
"s1",
|
||||
(event, data, id) => reconnect.push({ event, data, id }),
|
||||
{ afterId: 1, pending: { id: "gate-1", widget: "select" } },
|
||||
);
|
||||
expect(reconnect).toEqual(first);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user