import { test, expect, describe, beforeEach } from "bun:test"; import { SignalingService } from "../src/services/SignalingService.ts"; import { ClientManager } from "../src/services/ClientManager.ts"; import type { ISignalingMessage } from "../src/interfaces/ISignalingMessage.ts"; class MockWebSocket { sentMessages: string[] = []; closed = false; send(data: string) { this.sentMessages.push(data); } close() { this.closed = true; } } describe("SignalingService", () => { let signalingService: SignalingService; let clientManager: ClientManager; let mockWs: MockWebSocket; beforeEach(() => { clientManager = new ClientManager(); signalingService = new SignalingService(clientManager); mockWs = new MockWebSocket(); }); test("should handle publisher connection", () => { const clientId = signalingService.handleConnection(mockWs, "publisher"); expect(typeof clientId).toBe("string"); expect(clientId.length).toBeGreaterThan(0); expect(mockWs.sentMessages[0]).toBe( JSON.stringify({ type: 'join', data: { clientId, role: 'publisher' } }) ); expect(clientManager.getPublisher()?.id).toBe(clientId); }); test("should handle subscriber connection", () => { const clientId = signalingService.handleConnection(mockWs, "subscriber"); expect(typeof clientId).toBe("string"); expect(clientId.length).toBeGreaterThan(0); expect(mockWs.sentMessages[0]).toBe( JSON.stringify({ type: 'join', data: { clientId, role: 'subscriber' } }) ); const subscribers = clientManager.getSubscribers(); expect(subscribers).toHaveLength(1); expect(subscribers[0].id).toBe(clientId); }); test("should handle disconnection", () => { const clientId = signalingService.handleConnection(mockWs, "publisher"); expect(clientManager.getPublisher()).not.toBeNull(); signalingService.handleDisconnection(clientId); expect(clientManager.getPublisher()).toBeNull(); }); test("should handle offer from publisher to subscribers", () => { // Add publisher const publisherId = signalingService.handleConnection(mockWs, "publisher"); // Add subscribers const subscriber1Ws = new MockWebSocket(); const subscriber2Ws = new MockWebSocket(); signalingService.handleConnection(subscriber1Ws, "subscriber"); signalingService.handleConnection(subscriber2Ws, "subscriber"); const offerMessage: ISignalingMessage = { type: "offer", data: { sdp: "fake-offer-sdp", type: "offer" } }; signalingService.handleMessage(publisherId, offerMessage); expect(subscriber1Ws.sentMessages).toContain( JSON.stringify({ ...offerMessage, senderId: publisherId }) ); expect(subscriber2Ws.sentMessages).toContain( JSON.stringify({ ...offerMessage, senderId: publisherId }) ); }); test("should handle answer from subscriber to publisher", () => { // Add publisher const publisherWs = new MockWebSocket(); const publisherId = signalingService.handleConnection(publisherWs, "publisher"); // Add subscriber const subscriberWs = new MockWebSocket(); const subscriberId = signalingService.handleConnection(subscriberWs, "subscriber"); const answerMessage: ISignalingMessage = { type: "answer", data: { sdp: "fake-answer-sdp", type: "answer" } }; signalingService.handleMessage(subscriberId, answerMessage); expect(publisherWs.sentMessages).toContain( JSON.stringify({ ...answerMessage, senderId: subscriberId }) ); }); test("should handle ice candidates from publisher to subscribers", () => { const publisherId = signalingService.handleConnection(mockWs, "publisher"); const subscriberWs = new MockWebSocket(); signalingService.handleConnection(subscriberWs, "subscriber"); const iceCandidateMessage: ISignalingMessage = { type: "ice-candidate", data: { candidate: "fake-ice-candidate" } }; signalingService.handleMessage(publisherId, iceCandidateMessage); expect(subscriberWs.sentMessages).toContain( JSON.stringify({ ...iceCandidateMessage, senderId: publisherId }) ); }); test("should handle ice candidates from subscriber to publisher", () => { const publisherWs = new MockWebSocket(); const publisherId = signalingService.handleConnection(publisherWs, "publisher"); const subscriberId = signalingService.handleConnection(mockWs, "subscriber"); const iceCandidateMessage: ISignalingMessage = { type: "ice-candidate", data: { candidate: "fake-ice-candidate" } }; signalingService.handleMessage(subscriberId, iceCandidateMessage); expect(publisherWs.sentMessages).toContain( JSON.stringify({ ...iceCandidateMessage, senderId: subscriberId }) ); }); test("should ignore messages from non-existent clients", () => { const message: ISignalingMessage = { type: "offer", data: { sdp: "fake-sdp" } }; expect(() => { signalingService.handleMessage("non-existent-id", message); }).not.toThrow(); }); });