Files
fusion/packages/dashboard/src/__tests__/sse-buffer.test.ts
gsxdsm b6fbadec4d feat(FN-1145): harden dashboard SSE streams with replay and reconnect
- Add a shared SSE event buffer utility and wire it into planning, subtask, mission interview, and task stream routes
- Support Last-Event-ID replay semantics and robust event serialization so clients can recover missed stream events
- Add a resilient client-side reconnect wrapper and surface reconnecting state in planning, subtask breakdown, and mission interview modals
- Expand test coverage across API routes, SSE buffering behavior, reconnect handling, and mission/planning stream flows
2026-04-08 06:02:28 -07:00

70 lines
2.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { SessionEventBuffer } from "../sse-buffer.js";
describe("SessionEventBuffer", () => {
it("push assigns monotonically increasing ids", () => {
const buffer = new SessionEventBuffer(10);
const id1 = buffer.push("thinking", JSON.stringify("a"));
const id2 = buffer.push("question", JSON.stringify({ id: "q-1" }));
expect(id1).toBe(1);
expect(id2).toBe(2);
});
it("getEventsSince returns only events newer than lastEventId", () => {
const buffer = new SessionEventBuffer(10);
buffer.push("thinking", JSON.stringify("a"));
buffer.push("thinking", JSON.stringify("b"));
buffer.push("question", JSON.stringify({ id: "q-1" }));
const events = buffer.getEventsSince(1);
expect(events.map((event) => event.id)).toEqual([2, 3]);
});
it("drops oldest events when capacity overflows", () => {
const buffer = new SessionEventBuffer(2);
buffer.push("thinking", JSON.stringify("a"));
buffer.push("thinking", JSON.stringify("b"));
buffer.push("question", JSON.stringify({ id: "q-1" }));
const events = buffer.getEventsSince(0);
expect(events).toHaveLength(2);
expect(events[0]?.id).toBe(2);
expect(events[1]?.id).toBe(3);
});
it("returns empty array for an empty buffer", () => {
const buffer = new SessionEventBuffer(10);
expect(buffer.getEventsSince(0)).toEqual([]);
});
it("returns all buffered events for non-finite lastEventId", () => {
const buffer = new SessionEventBuffer(10);
buffer.push("thinking", JSON.stringify("a"));
buffer.push("complete", JSON.stringify({}));
expect(buffer.getEventsSince(Number.NaN)).toHaveLength(2);
});
it("supports interleaved push/read access without duplicate ids", async () => {
const buffer = new SessionEventBuffer(20);
await Promise.all(
Array.from({ length: 10 }, (_, index) =>
Promise.resolve().then(() => {
const eventId = buffer.push("thinking", JSON.stringify(`event-${index + 1}`));
const snapshot = buffer.getEventsSince(Math.max(0, eventId - 1));
expect(snapshot[snapshot.length - 1]?.id).toBe(eventId);
}),
),
);
const events = buffer.getEventsSince(0);
expect(events).toHaveLength(10);
expect(events.map((event) => event.id)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
});
});