Spaces:
Sleeping
Sleeping
| /** | |
| * Tests for RoboticsProducer - equivalent to Python's test_producer.py | |
| */ | |
| import { test, expect, describe, beforeEach, afterEach } from "bun:test"; | |
| import { robotics } from "../src/index"; | |
| import { TEST_SERVER_URL, TestRoomManager, MessageCollector, sleep, assertIsConnected, assertIsDisconnected } from "./setup"; | |
| const { RoboticsProducer } = robotics; | |
| describe("RoboticsProducer", () => { | |
| let producer: robotics.RoboticsProducer; | |
| let roomManager: TestRoomManager; | |
| beforeEach(() => { | |
| producer = new RoboticsProducer(TEST_SERVER_URL); | |
| roomManager = new TestRoomManager(); | |
| }); | |
| afterEach(async () => { | |
| if (producer.isConnected()) { | |
| await producer.disconnect(); | |
| } | |
| await roomManager.cleanup(producer); | |
| }); | |
| test("producer connection", async () => { | |
| // Create room first | |
| const { workspaceId, roomId } = await producer.createRoom(); | |
| roomManager.addRoom(workspaceId, roomId); | |
| expect(producer.isConnected()).toBe(false); | |
| const success = await producer.connect(workspaceId, roomId); | |
| expect(success).toBe(true); | |
| assertIsConnected(producer, workspaceId, roomId); | |
| const info = producer.getConnectionInfo(); | |
| expect(info.role).toBe("producer"); | |
| await producer.disconnect(); | |
| assertIsDisconnected(producer); | |
| }); | |
| test("producer connection info", async () => { | |
| const { workspaceId, roomId } = await producer.createRoom(); | |
| roomManager.addRoom(workspaceId, roomId); | |
| await producer.connect(workspaceId, roomId); | |
| const info = producer.getConnectionInfo(); | |
| expect(info.connected).toBe(true); | |
| expect(info.room_id).toBe(roomId); | |
| expect(info.workspace_id).toBe(workspaceId); | |
| expect(info.role).toBe("producer"); | |
| expect(info.participant_id).toBeTruthy(); | |
| expect(info.base_url).toBe(TEST_SERVER_URL); | |
| }); | |
| test("send joint update", async () => { | |
| const { workspaceId, roomId } = await producer.createRoom(); | |
| roomManager.addRoom(workspaceId, roomId); | |
| await producer.connect(workspaceId, roomId); | |
| const joints = [ | |
| { name: "shoulder", value: 45.0 }, | |
| { name: "elbow", value: -20.0 }, | |
| { name: "wrist", value: 10.0 } | |
| ]; | |
| // Should not throw an exception | |
| await producer.sendJointUpdate(joints); | |
| }); | |
| test("send state sync", async () => { | |
| const { workspaceId, roomId } = await producer.createRoom(); | |
| roomManager.addRoom(workspaceId, roomId); | |
| await producer.connect(workspaceId, roomId); | |
| const state = { shoulder: 45.0, elbow: -20.0, wrist: 10.0 }; | |
| // Should not throw an exception | |
| await producer.sendStateSync(state); | |
| }); | |
| test("send emergency stop", async () => { | |
| const { workspaceId, roomId } = await producer.createRoom(); | |
| roomManager.addRoom(workspaceId, roomId); | |
| await producer.connect(workspaceId, roomId); | |
| // Should not throw an exception | |
| await producer.sendEmergencyStop("Test emergency stop"); | |
| await producer.sendEmergencyStop(); // Default reason | |
| }); | |
| test("producer callbacks", async () => { | |
| const { workspaceId, roomId } = await producer.createRoom(); | |
| roomManager.addRoom(workspaceId, roomId); | |
| let connectedCalled = false; | |
| let disconnectedCalled = false; | |
| let errorCalled = false; | |
| let errorMessage: string | null = null; | |
| producer.onConnected(() => { | |
| connectedCalled = true; | |
| }); | |
| producer.onDisconnected(() => { | |
| disconnectedCalled = true; | |
| }); | |
| producer.onError((error) => { | |
| errorCalled = true; | |
| errorMessage = error; | |
| }); | |
| // Connect and disconnect | |
| await producer.connect(workspaceId, roomId); | |
| await sleep(100); // Give callbacks time to execute | |
| expect(connectedCalled).toBe(true); | |
| await producer.disconnect(); | |
| await sleep(100); // Give callbacks time to execute | |
| expect(disconnectedCalled).toBe(true); | |
| }); | |
| test("send without connection", async () => { | |
| expect(producer.isConnected()).toBe(false); | |
| await expect(producer.sendJointUpdate([{ name: "test", value: 0 }])) | |
| .rejects.toThrow("Must be connected"); | |
| await expect(producer.sendStateSync({ test: 0 })) | |
| .rejects.toThrow("Must be connected"); | |
| await expect(producer.sendEmergencyStop()) | |
| .rejects.toThrow("Must be connected"); | |
| }); | |
| test("multiple connections", async () => { | |
| // Connect to first room | |
| const { workspaceId: workspaceId1, roomId: roomId1 } = await producer.createRoom(); | |
| roomManager.addRoom(workspaceId1, roomId1); | |
| await producer.connect(workspaceId1, roomId1); | |
| expect(producer.getConnectionInfo().room_id).toBe(roomId1); | |
| expect(producer.getConnectionInfo().workspace_id).toBe(workspaceId1); | |
| // Create second room | |
| const { workspaceId: workspaceId2, roomId: roomId2 } = await producer.createRoom(); | |
| roomManager.addRoom(workspaceId2, roomId2); | |
| // Connect to second room (should disconnect from first) | |
| await producer.connect(workspaceId2, roomId2); | |
| expect(producer.getConnectionInfo().room_id).toBe(roomId2); | |
| expect(producer.getConnectionInfo().workspace_id).toBe(workspaceId2); | |
| expect(producer.isConnected()).toBe(true); | |
| }); | |
| test("duplicate producer connection", async () => { | |
| const { workspaceId, roomId } = await producer.createRoom(); | |
| roomManager.addRoom(workspaceId, roomId); | |
| const producer2 = new RoboticsProducer(TEST_SERVER_URL); | |
| try { | |
| // First producer connects successfully | |
| const success1 = await producer.connect(workspaceId, roomId); | |
| expect(success1).toBe(true); | |
| // Second producer should fail to connect as producer | |
| const success2 = await producer2.connect(workspaceId, roomId); | |
| expect(success2).toBe(false); // Should fail since room already has producer | |
| } finally { | |
| if (producer2.isConnected()) { | |
| await producer2.disconnect(); | |
| } | |
| } | |
| }); | |
| test("custom participant id", async () => { | |
| const { workspaceId, roomId } = await producer.createRoom(); | |
| roomManager.addRoom(workspaceId, roomId); | |
| const customId = "custom-producer-123"; | |
| await producer.connect(workspaceId, roomId, customId); | |
| const info = producer.getConnectionInfo(); | |
| expect(info.participant_id).toBe(customId); | |
| }); | |
| test("large joint update", async () => { | |
| const { workspaceId, roomId } = await producer.createRoom(); | |
| roomManager.addRoom(workspaceId, roomId); | |
| await producer.connect(workspaceId, roomId); | |
| // Create a large joint update | |
| const joints = Array.from({ length: 100 }, (_, i) => ({ | |
| name: `joint_${i}`, | |
| value: i | |
| })); | |
| // Should handle large updates without issue | |
| await producer.sendJointUpdate(joints); | |
| }); | |
| test("rapid updates", async () => { | |
| const { workspaceId, roomId } = await producer.createRoom(); | |
| roomManager.addRoom(workspaceId, roomId); | |
| await producer.connect(workspaceId, roomId); | |
| // Send multiple rapid updates | |
| for (let i = 0; i < 10; i++) { | |
| await producer.sendStateSync({ joint1: i, joint2: i * 2 }); | |
| await sleep(10); // Small delay | |
| } | |
| }); | |
| }); |