fix(realtime): isolate notification failures

This commit is contained in:
Federico Jaramillo Martinez
2026-07-17 23:58:56 +02:00
parent 45f068ef05
commit 1f13bab58a
7 changed files with 187 additions and 24 deletions
@@ -54,6 +54,28 @@ describe("SessionEventHub", () => {
expect(removed.send).not.toHaveBeenCalled();
});
it("continues publishing session events when one socket send fails", () => {
const hub = new SessionEventHub();
const failed = new FakeSocket();
const healthy = new FakeSocket();
failed.send.mockImplementation(() => { throw new Error("socket closed"); });
hub.add("s1", failed);
hub.add("s1", healthy);
hub.publish("s1", { type: "assistant.delta", text: "hello" });
expect(failed.send).toHaveBeenCalledOnce();
expect(healthy.send).toHaveBeenCalledWith(JSON.stringify({ type: "assistant.delta", text: "hello", seq: 1 }));
expect(hub.currentSeq("s1")).toBe(1);
failed.send.mockClear();
hub.publish("s1", { type: "assistant.delta", text: "again" });
expect(failed.send).not.toHaveBeenCalled();
expect(healthy.send).toHaveBeenLastCalledWith(JSON.stringify({ type: "assistant.delta", text: "again", seq: 2 }));
expect(hub.currentSeq("s1")).toBe(2);
});
it("publishes global events only to global sockets", () => {
const hub = new SessionEventHub();
const globalSocket = new FakeSocket();
@@ -78,6 +100,26 @@ describe("SessionEventHub", () => {
expect(sessionSocket.send).not.toHaveBeenCalled();
});
it("continues publishing unstamped global events when one socket send fails", () => {
const hub = new SessionEventHub();
const failed = new FakeSocket();
const healthy = new FakeSocket();
failed.send.mockImplementation(() => { throw new Error("socket closed"); });
hub.addGlobal(failed);
hub.addGlobal(healthy);
hub.publishGlobal({ type: "session.name", sessionId: "s1", name: "Renamed" });
expect(failed.send).toHaveBeenCalledOnce();
expect(healthy.send).toHaveBeenCalledWith(JSON.stringify({ type: "session.name", sessionId: "s1", name: "Renamed" }));
failed.send.mockClear();
hub.publishGlobal({ type: "session.name", sessionId: "s1", name: "Renamed again" });
expect(failed.send).not.toHaveBeenCalled();
expect(healthy.send).toHaveBeenLastCalledWith(JSON.stringify({ type: "session.name", sessionId: "s1", name: "Renamed again" }));
});
it("stamps a monotonically increasing per-session seq on published events", () => {
const hub = new SessionEventHub();
const socket = new FakeSocket();