RobotHub-TransportServer / client /js /tests /video-client.test.ts
blanchon's picture
Update
1e7b565
/**
* Tests for Video Client - equivalent to Python's test_video_client.py
*/
import { test, expect, describe, beforeEach, afterEach } from "bun:test";
import { video } from "../src/index";
import { TEST_SERVER_URL, TestRoomManager, mockFrameSource } from "./setup";
const { VideoProducer, VideoConsumer, createProducerClient, createConsumerClient } = video;
describe("Video Types", () => {
test("resolution creation", () => {
const resolution = { width: 1920, height: 1080 };
expect(resolution.width).toBe(1920);
expect(resolution.height).toBe(1080);
});
test("video config creation", () => {
const config = {
encoding: "vp8" as video.VideoEncoding,
resolution: { width: 640, height: 480 },
framerate: 30,
bitrate: 1000000
};
expect(config.encoding).toBe("vp8");
expect(config.resolution?.width).toBe(640);
expect(config.framerate).toBe(30);
});
test("participant role enum", () => {
const producerRole = "producer";
const consumerRole = "consumer";
expect(producerRole).toBe("producer");
expect(consumerRole).toBe("consumer");
});
});
describe("Video Core", () => {
let roomManager: TestRoomManager;
beforeEach(() => {
roomManager = new TestRoomManager();
});
afterEach(async () => {
await roomManager.cleanup(new VideoProducer(TEST_SERVER_URL));
});
test("video producer creation", () => {
const producer = new VideoProducer(TEST_SERVER_URL);
expect(producer.getConnectionInfo().base_url).toBe(TEST_SERVER_URL);
expect(producer.isConnected()).toBe(false);
expect(producer.getConnectionInfo().room_id).toBeNull();
});
test("video consumer creation", () => {
const consumer = new VideoConsumer(TEST_SERVER_URL);
expect(consumer.getConnectionInfo().base_url).toBe(TEST_SERVER_URL);
expect(consumer.isConnected()).toBe(false);
expect(consumer.getConnectionInfo().room_id).toBeNull();
});
test("producer room creation", async () => {
try {
const producer = new VideoProducer(TEST_SERVER_URL);
const { workspaceId, roomId } = await producer.createRoom();
roomManager.addRoom(workspaceId, roomId);
expect(typeof roomId).toBe("string");
expect(roomId.length).toBeGreaterThan(0);
console.log(`βœ… Created room: ${roomId}`);
} catch (error) {
console.warn(`⚠️ Server not available: ${error}`);
// Skip test if server not available
}
}, { timeout: 5000 });
test("consumer list rooms", async () => {
try {
const consumer = new VideoConsumer(TEST_SERVER_URL);
const workspaceId = "test-workspace-id";
const rooms = await consumer.listRooms(workspaceId);
expect(Array.isArray(rooms)).toBe(true);
console.log(`βœ… Listed ${rooms.length} rooms`);
} catch (error) {
console.warn(`⚠️ Server not available: ${error}`);
// Skip test if server not available
}
}, { timeout: 5000 });
});
describe("Video Client Integration", () => {
let roomManager: TestRoomManager;
beforeEach(() => {
roomManager = new TestRoomManager();
});
afterEach(async () => {
await roomManager.cleanup(new VideoProducer(TEST_SERVER_URL));
});
test("producer consumer setup", () => {
// Test producer setup
const producer = new VideoProducer(TEST_SERVER_URL);
expect(producer.getLocalStream()).toBeNull();
// Test consumer setup
const consumer = new VideoConsumer(TEST_SERVER_URL);
expect(consumer.getRemoteStream()).toBeNull();
console.log("βœ… Producer/Consumer setup test passed");
});
test("custom stream setup validation", async () => {
const producer = new VideoProducer(TEST_SERVER_URL);
// This will fail because we're not connected, but it tests the setup
try {
// Simulate starting a custom stream when not connected
await producer.startCamera();
expect(true).toBe(false); // Should not reach here
} catch (error: any) {
expect(error.message).toContain("Must be connected");
console.log("βœ… Custom stream setup validation passed");
}
});
test("factory functions", async () => {
// Test that factory functions create the right types
// (We can't actually connect without a server)
try {
await createProducerClient(TEST_SERVER_URL);
} catch (error) {
// Expected to fail without server
console.log("βœ… Producer factory function exists");
}
try {
await createConsumerClient("test-workspace", "test-room", TEST_SERVER_URL);
} catch (error) {
// Expected to fail without server
console.log("βœ… Consumer factory function exists");
}
console.log("βœ… Factory functions test passed");
});
test("mock frame source", async () => {
const frameData = await mockFrameSource();
expect(frameData).not.toBeNull();
expect(frameData).toBeInstanceOf(ArrayBuffer);
if (frameData) {
expect(frameData.byteLength).toBe(320 * 240 * 4); // RGBA
console.log("βœ… Mock frame source test passed");
}
});
test("video config types", () => {
const config: video.VideoConfig = {
encoding: "h264",
resolution: { width: 1280, height: 720 },
framerate: 60,
bitrate: 2000000,
quality: 90
};
expect(config.encoding).toBe("h264");
expect(config.resolution?.width).toBe(1280);
expect(config.resolution?.height).toBe(720);
expect(config.framerate).toBe(60);
expect(config.bitrate).toBe(2000000);
expect(config.quality).toBe(90);
console.log("βœ… Video config types test passed");
});
test("recovery config types", () => {
const recoveryConfig: video.RecoveryConfig = {
frame_timeout_ms: 200,
max_frame_reuse_count: 5,
recovery_policy: "freeze_last_frame",
fallback_policy: "connection_info",
show_hold_indicators: true,
fade_intensity: 0.8
};
expect(recoveryConfig.frame_timeout_ms).toBe(200);
expect(recoveryConfig.recovery_policy).toBe("freeze_last_frame");
expect(recoveryConfig.fallback_policy).toBe("connection_info");
console.log("βœ… Recovery config types test passed");
});
test("participant info types", () => {
const participantInfo: video.ParticipantInfo = {
producer: "producer-123",
consumers: ["consumer-1", "consumer-2"],
total: 3
};
expect(participantInfo.producer).toBe("producer-123");
expect(participantInfo.consumers).toHaveLength(2);
expect(participantInfo.total).toBe(3);
console.log("βœ… Participant info types test passed");
});
test("room info types", () => {
const roomInfo: video.RoomInfo = {
id: "room-123",
workspace_id: "workspace-456",
participants: {
producer: "producer-1",
consumers: ["consumer-1"],
total: 2
},
frame_count: 1000,
config: {
encoding: "vp8",
resolution: { width: 640, height: 480 },
framerate: 30
},
has_producer: true,
active_consumers: 1
};
expect(roomInfo.id).toBe("room-123");
expect(roomInfo.workspace_id).toBe("workspace-456");
expect(roomInfo.has_producer).toBe(true);
expect(roomInfo.active_consumers).toBe(1);
console.log("βœ… Room info types test passed");
});
test("stream stats types", () => {
const streamStats: video.StreamStats = {
stream_id: "stream-123",
duration_seconds: 120.5,
frame_count: 3615,
total_bytes: 1048576,
average_fps: 30.0,
average_bitrate: 1000000
};
expect(streamStats.stream_id).toBe("stream-123");
expect(streamStats.duration_seconds).toBe(120.5);
expect(streamStats.frame_count).toBe(3615);
expect(streamStats.average_fps).toBe(30.0);
console.log("βœ… Stream stats types test passed");
});
});