Files
WebRTC-Broadcast/tests/sfu/SFUClientManager.test.ts
2025-09-05 00:36:54 -04:00

344 lines
9.4 KiB
TypeScript

import { test, expect, describe, beforeEach } from "bun:test";
import { SFUClientManager } from "../../src/services/SFUClientManager.ts";
import type { ISFUClient } from "../../src/interfaces/ISFUTypes.ts";
// Mock WebRTC Transport
class MockWebRtcTransport {
id: string;
closed = false;
constructor(id: string) {
this.id = id;
}
close() {
this.closed = true;
}
}
// Mock Producer
class MockProducer {
id: string;
closed = false;
constructor(id: string) {
this.id = id;
}
close() {
this.closed = true;
}
}
// Mock Consumer
class MockConsumer {
id: string;
closed = false;
constructor(id: string) {
this.id = id;
}
close() {
this.closed = true;
}
}
// Mock WebSocket
class MockWebSocket {
sentMessages: string[] = [];
send(data: string) {
this.sentMessages.push(data);
}
}
describe("SFUClientManager", () => {
let clientManager: SFUClientManager;
beforeEach(() => {
clientManager = new SFUClientManager();
});
test("should add and retrieve clients", () => {
const mockWs = new MockWebSocket();
const client: ISFUClient = {
id: "client-1",
role: "publisher",
ws: mockWs,
consumers: new Map()
};
clientManager.addClient(client);
const retrievedClient = clientManager.getClient("client-1");
expect(retrievedClient).toBe(client);
});
test("should set and manage client transport", () => {
const mockWs = new MockWebSocket();
const client: ISFUClient = {
id: "client-1",
role: "publisher",
ws: mockWs,
consumers: new Map()
};
clientManager.addClient(client);
const transport = new MockWebRtcTransport("transport-1") as any;
clientManager.setClientTransport("client-1", transport);
const retrievedClient = clientManager.getClient("client-1");
expect(retrievedClient?.transport).toBe(transport);
});
test("should manage client producer", () => {
const mockWs = new MockWebSocket();
const client: ISFUClient = {
id: "publisher-1",
role: "publisher",
ws: mockWs,
consumers: new Map()
};
clientManager.addClient(client);
const producer = new MockProducer("producer-1") as any;
clientManager.setClientProducer("publisher-1", producer);
const retrievedClient = clientManager.getClient("publisher-1");
expect(retrievedClient?.producer).toBe(producer);
const retrievedProducer = clientManager.getProducer("producer-1");
expect(retrievedProducer).toBe(producer);
});
test("should manage client consumers", () => {
const mockWs = new MockWebSocket();
const client: ISFUClient = {
id: "subscriber-1",
role: "subscriber",
ws: mockWs,
consumers: new Map()
};
clientManager.addClient(client);
const consumer = new MockConsumer("consumer-1") as any;
clientManager.addClientConsumer("subscriber-1", consumer);
const retrievedClient = clientManager.getClient("subscriber-1");
expect(retrievedClient?.consumers.has("consumer-1")).toBe(true);
expect(retrievedClient?.consumers.get("consumer-1")).toBe(consumer);
const retrievedConsumer = clientManager.getConsumer("consumer-1");
expect(retrievedConsumer).toBe(consumer);
});
test("should remove client consumer", () => {
const mockWs = new MockWebSocket();
const client: ISFUClient = {
id: "subscriber-1",
role: "subscriber",
ws: mockWs,
consumers: new Map()
};
clientManager.addClient(client);
const consumer = new MockConsumer("consumer-1") as any;
clientManager.addClientConsumer("subscriber-1", consumer);
// Verify consumer is added
expect(clientManager.getConsumer("consumer-1")).toBe(consumer);
// Remove consumer
clientManager.removeClientConsumer("subscriber-1", "consumer-1");
// Verify consumer is removed and closed
expect(clientManager.getConsumer("consumer-1")).toBeUndefined();
expect(consumer.closed).toBe(true);
const retrievedClient = clientManager.getClient("subscriber-1");
expect(retrievedClient?.consumers.has("consumer-1")).toBe(false);
});
test("should get publishers and subscribers separately", () => {
const publisherWs = new MockWebSocket();
const subscriberWs1 = new MockWebSocket();
const subscriberWs2 = new MockWebSocket();
const publisher: ISFUClient = {
id: "publisher-1",
role: "publisher",
ws: publisherWs,
consumers: new Map(),
producer: new MockProducer("producer-1") as any
};
const subscriber1: ISFUClient = {
id: "subscriber-1",
role: "subscriber",
ws: subscriberWs1,
consumers: new Map()
};
const subscriber2: ISFUClient = {
id: "subscriber-2",
role: "subscriber",
ws: subscriberWs2,
consumers: new Map()
};
clientManager.addClient(publisher);
clientManager.addClient(subscriber1);
clientManager.addClient(subscriber2);
const publishers = clientManager.getPublishers();
const subscribers = clientManager.getSubscribers();
expect(publishers).toHaveLength(1);
expect(publishers[0]).toBe(publisher);
expect(subscribers).toHaveLength(2);
expect(subscribers).toContain(subscriber1);
expect(subscribers).toContain(subscriber2);
});
test("should get all producers", () => {
const publisherWs1 = new MockWebSocket();
const publisherWs2 = new MockWebSocket();
const publisher1: ISFUClient = {
id: "publisher-1",
role: "publisher",
ws: publisherWs1,
consumers: new Map()
};
const publisher2: ISFUClient = {
id: "publisher-2",
role: "publisher",
ws: publisherWs2,
consumers: new Map()
};
clientManager.addClient(publisher1);
clientManager.addClient(publisher2);
const producer1 = new MockProducer("producer-1") as any;
const producer2 = new MockProducer("producer-2") as any;
clientManager.setClientProducer("publisher-1", producer1);
clientManager.setClientProducer("publisher-2", producer2);
const allProducers = clientManager.getAllProducers();
expect(allProducers).toHaveLength(2);
expect(allProducers).toContain(producer1);
expect(allProducers).toContain(producer2);
});
test("should remove client and cleanup resources", () => {
const mockWs = new MockWebSocket();
const client: ISFUClient = {
id: "client-1",
role: "publisher",
ws: mockWs,
consumers: new Map()
};
clientManager.addClient(client);
// Add transport, producer, and consumer
const transport = new MockWebRtcTransport("transport-1") as any;
const producer = new MockProducer("producer-1") as any;
const consumer = new MockConsumer("consumer-1") as any;
clientManager.setClientTransport("client-1", transport);
clientManager.setClientProducer("client-1", producer);
clientManager.addClientConsumer("client-1", consumer);
// Remove client
clientManager.removeClient("client-1");
// Verify client is removed
expect(clientManager.getClient("client-1")).toBeUndefined();
// Verify resources are cleaned up
expect(transport.closed).toBe(true);
expect(producer.closed).toBe(true);
expect(consumer.closed).toBe(true);
// Verify global maps are cleaned up
expect(clientManager.getProducer("producer-1")).toBeUndefined();
expect(clientManager.getConsumer("consumer-1")).toBeUndefined();
});
test("should provide correct statistics", () => {
const publisherWs = new MockWebSocket();
const subscriberWs1 = new MockWebSocket();
const subscriberWs2 = new MockWebSocket();
const publisher: ISFUClient = {
id: "publisher-1",
role: "publisher",
ws: publisherWs,
consumers: new Map(),
producer: new MockProducer("producer-1") as any
};
const subscriber1: ISFUClient = {
id: "subscriber-1",
role: "subscriber",
ws: subscriberWs1,
consumers: new Map()
};
const subscriber2: ISFUClient = {
id: "subscriber-2",
role: "subscriber",
ws: subscriberWs2,
consumers: new Map()
};
clientManager.addClient(publisher);
clientManager.addClient(subscriber1);
clientManager.addClient(subscriber2);
const producer = new MockProducer("producer-1") as any;
const consumer1 = new MockConsumer("consumer-1") as any;
const consumer2 = new MockConsumer("consumer-2") as any;
clientManager.setClientProducer("publisher-1", producer);
clientManager.addClientConsumer("subscriber-1", consumer1);
clientManager.addClientConsumer("subscriber-2", consumer2);
const stats = clientManager.getStats();
expect(stats.clients).toBe(3);
expect(stats.publishers).toBe(1);
expect(stats.subscribers).toBe(2);
expect(stats.producers).toBe(1);
expect(stats.consumers).toBe(2);
});
test("should handle removing non-existent client gracefully", () => {
expect(() => {
clientManager.removeClient("non-existent");
}).not.toThrow();
});
test("should handle operations on non-existent clients gracefully", () => {
const transport = new MockWebRtcTransport("transport-1") as any;
const producer = new MockProducer("producer-1") as any;
const consumer = new MockConsumer("consumer-1") as any;
expect(() => {
clientManager.setClientTransport("non-existent", transport);
clientManager.setClientProducer("non-existent", producer);
clientManager.addClientConsumer("non-existent", consumer);
clientManager.removeClientConsumer("non-existent", "consumer-1");
}).not.toThrow();
});
});