feat(web): parité layout AppImage desktop + switch TUI naïf/CLI custom — #208
This commit is contained in:
83
frontend/src/adapters/http/wsLiveClient.chat.test.ts
Normal file
83
frontend/src/adapters/http/wsLiveClient.chat.test.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import type { WebSocketLike } from "./wsLiveClient";
|
||||
import { WsLiveClient } from "./wsLiveClient";
|
||||
|
||||
class FakeSocket implements WebSocketLike {
|
||||
sent: string[] = [];
|
||||
onopen: (() => void) | null = null;
|
||||
onmessage: ((event: { data: string }) => void) | null = null;
|
||||
onerror: ((event: unknown) => void) | null = null;
|
||||
onclose: (() => void) | null = null;
|
||||
|
||||
send(data: string): void {
|
||||
this.sent.push(data);
|
||||
}
|
||||
close(): void {
|
||||
this.onclose?.();
|
||||
}
|
||||
receive(frame: unknown): void {
|
||||
this.onmessage?.({ data: JSON.stringify(frame) });
|
||||
}
|
||||
}
|
||||
|
||||
function connectedClient(): { client: WsLiveClient; socket: FakeSocket } {
|
||||
const socket = new FakeSocket();
|
||||
const client = new WsLiveClient({
|
||||
wsUrl: "wss://host",
|
||||
socketFactory: () => {
|
||||
queueMicrotask(() => socket.onopen?.());
|
||||
return socket;
|
||||
},
|
||||
});
|
||||
return { client, socket };
|
||||
}
|
||||
|
||||
describe("WsLiveClient structured chat routing", () => {
|
||||
it("routes chat.output to the per-session chat sink", async () => {
|
||||
const { client, socket } = connectedClient();
|
||||
await client.ensureConnected();
|
||||
|
||||
const received: unknown[] = [];
|
||||
client.setChatSink("chat-1", (chunk) => received.push(chunk));
|
||||
|
||||
socket.receive({
|
||||
kind: "chat.output",
|
||||
payload: {
|
||||
sessionId: "chat-1",
|
||||
chunk: { kind: "final", content: "done" },
|
||||
},
|
||||
});
|
||||
|
||||
expect(received).toEqual([{ kind: "final", content: "done" }]);
|
||||
|
||||
client.removeChatSink("chat-1");
|
||||
socket.receive({
|
||||
kind: "chat.output",
|
||||
payload: {
|
||||
sessionId: "chat-1",
|
||||
chunk: { kind: "final", content: "ignored" },
|
||||
},
|
||||
});
|
||||
expect(received).toEqual([{ kind: "final", content: "done" }]);
|
||||
});
|
||||
|
||||
it("clears structured chat sinks on disconnect", async () => {
|
||||
const { client, socket } = connectedClient();
|
||||
await client.ensureConnected();
|
||||
|
||||
const received: unknown[] = [];
|
||||
client.setChatSink("chat-1", (chunk) => received.push(chunk));
|
||||
client.disconnect();
|
||||
expect(client.getConnectionState()).toBe("closed");
|
||||
|
||||
socket.receive({
|
||||
kind: "chat.output",
|
||||
payload: {
|
||||
sessionId: "chat-1",
|
||||
chunk: { kind: "final", content: "dropped" },
|
||||
},
|
||||
});
|
||||
expect(received).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user