import { test, expect, describe, beforeEach } from "bun:test"; import { ClientManager } from "../src/services/ClientManager.ts"; import type { IWebSocketClient } from "../src/interfaces/ISignalingMessage.ts"; class MockWebSocket { sentMessages: string[] = []; closed = false; send(data: string) { this.sentMessages.push(data); } close() { this.closed = true; } } describe("ClientManager", () => { let clientManager: ClientManager; let mockWs: MockWebSocket; beforeEach(() => { clientManager = new ClientManager(); mockWs = new MockWebSocket(); }); test("should add a publisher client", () => { const client: IWebSocketClient = { id: "publisher-1", ws: mockWs, role: "publisher" }; clientManager.addClient(client); expect(clientManager.getClient("publisher-1")).toBe(client); expect(clientManager.getPublisher()).toBe(client); }); test("should add subscriber clients", () => { const subscriber1: IWebSocketClient = { id: "subscriber-1", ws: mockWs, role: "subscriber" }; const subscriber2: IWebSocketClient = { id: "subscriber-2", ws: new MockWebSocket(), role: "subscriber" }; clientManager.addClient(subscriber1); clientManager.addClient(subscriber2); const subscribers = clientManager.getSubscribers(); expect(subscribers).toHaveLength(2); expect(subscribers).toContain(subscriber1); expect(subscribers).toContain(subscriber2); }); test("should notify subscribers when publisher joins", () => { const subscriber1: IWebSocketClient = { id: "subscriber-1", ws: mockWs, role: "subscriber" }; const subscriber2: IWebSocketClient = { id: "subscriber-2", ws: new MockWebSocket(), role: "subscriber" }; clientManager.addClient(subscriber1); clientManager.addClient(subscriber2); const publisher: IWebSocketClient = { id: "publisher-1", ws: new MockWebSocket(), role: "publisher" }; clientManager.addClient(publisher); expect(mockWs.sentMessages).toContain( JSON.stringify({ type: 'publisher-joined' }) ); expect((subscriber2.ws as MockWebSocket).sentMessages).toContain( JSON.stringify({ type: 'publisher-joined' }) ); }); test("should remove client and notify subscribers when publisher leaves", () => { const subscriber: IWebSocketClient = { id: "subscriber-1", ws: mockWs, role: "subscriber" }; const publisher: IWebSocketClient = { id: "publisher-1", ws: new MockWebSocket(), role: "publisher" }; clientManager.addClient(subscriber); clientManager.addClient(publisher); clientManager.removeClient("publisher-1"); expect(clientManager.getClient("publisher-1")).toBeUndefined(); expect(clientManager.getPublisher()).toBeNull(); expect(mockWs.sentMessages).toContain( JSON.stringify({ type: 'publisher-left' }) ); }); test("should handle removing non-existent client", () => { expect(() => { clientManager.removeClient("non-existent"); }).not.toThrow(); }); test("should get all clients", () => { const publisher: IWebSocketClient = { id: "publisher-1", ws: mockWs, role: "publisher" }; const subscriber: IWebSocketClient = { id: "subscriber-1", ws: new MockWebSocket(), role: "subscriber" }; clientManager.addClient(publisher); clientManager.addClient(subscriber); const allClients = clientManager.getAllClients(); expect(allClients).toHaveLength(2); expect(allClients).toContain(publisher); expect(allClients).toContain(subscriber); }); });