File size: 8,487 Bytes
1e7b565
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116a27a
1e7b565
 
 
 
 
 
116a27a
1e7b565
 
 
 
 
 
116a27a
1e7b565
 
 
116a27a
1e7b565
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116a27a
1e7b565
 
 
 
 
 
 
 
 
116a27a
1e7b565
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/**
 * Tests for Factory Functions - equivalent to Python's test_factory_functions.py
 */
import { test, expect, describe, beforeEach, afterEach } from "bun:test";
import { robotics } from "../src/index";
import { TEST_SERVER_URL, TestRoomManager, MessageCollector, sleep } from "./setup";

const { RoboticsProducer, RoboticsConsumer, createClient, createProducerClient, createConsumerClient } = robotics;

describe("Factory Functions", () => {
    let roomManager: TestRoomManager;

    beforeEach(() => {
        roomManager = new TestRoomManager();
    });

    afterEach(async () => {
        // Cleanup will be handled by individual tests
    });

    test("create client producer", () => {
        const client = createClient("producer", TEST_SERVER_URL);
        expect(client).toBeInstanceOf(RoboticsProducer);
        expect(client.isConnected()).toBe(false);
        expect(client.getConnectionInfo().base_url).toBe("http://localhost:8000");
    });

    test("create client consumer", () => {
        const client = createClient("consumer", TEST_SERVER_URL);
        expect(client).toBeInstanceOf(RoboticsConsumer);
        expect(client.isConnected()).toBe(false);
        expect(client.getConnectionInfo().base_url).toBe("http://localhost:8000");
    });

    test("create client invalid role", () => {
        expect(() => createClient("invalid_role" as any, TEST_SERVER_URL)).toThrow("Invalid role");
    });

    test("create client default url", () => {
        const client = createClient("producer", TEST_SERVER_URL);
        expect(client.getConnectionInfo().base_url).toBe("http://localhost:8000");
    });

    test("create producer client auto room", async () => {
        const producer = await createProducerClient(TEST_SERVER_URL);
        
        try {
            expect(producer).toBeInstanceOf(RoboticsProducer);
            expect(producer.isConnected()).toBe(true);
            const info = producer.getConnectionInfo();
            expect(info.room_id).toBeTruthy();
            expect(info.workspace_id).toBeTruthy();
            expect(info.role).toBe("producer");

            // Should be able to send commands immediately
            await producer.sendStateSync({ test: 123.0 });

            // Track for cleanup
            roomManager.addRoom(info.workspace_id!, info.room_id!);
        } finally {
            await producer.disconnect();
            await roomManager.cleanup(producer);
        }
    });

    test("create producer client specific room", async () => {
        // First create a room
        const tempProducer = new RoboticsProducer(TEST_SERVER_URL);
        const { workspaceId, roomId } = await tempProducer.createRoom();
        roomManager.addRoom(workspaceId, roomId);

        try {
            const producer = await createProducerClient(TEST_SERVER_URL, workspaceId, roomId);

            expect(producer).toBeInstanceOf(RoboticsProducer);
            expect(producer.isConnected()).toBe(true);
            const info = producer.getConnectionInfo();
            expect(info.room_id).toBe(roomId);
            expect(info.workspace_id).toBe(workspaceId);
            expect(info.role).toBe("producer");

            await producer.disconnect();
        } finally {
            await roomManager.cleanup(tempProducer);
        }
    });

    test("create consumer client", async () => {
        // First create a room
        const tempProducer = new RoboticsProducer(TEST_SERVER_URL);
        const { workspaceId, roomId } = await tempProducer.createRoom();
        roomManager.addRoom(workspaceId, roomId);

        try {
            const consumer = await createConsumerClient(workspaceId, roomId, TEST_SERVER_URL);

            expect(consumer).toBeInstanceOf(RoboticsConsumer);
            expect(consumer.isConnected()).toBe(true);
            const info = consumer.getConnectionInfo();
            expect(info.room_id).toBe(roomId);
            expect(info.workspace_id).toBe(workspaceId);
            expect(info.role).toBe("consumer");

            // Should be able to get state immediately
            const state = await consumer.getStateSyncAsync();
            expect(typeof state).toBe("object");

            await consumer.disconnect();
        } finally {
            await roomManager.cleanup(tempProducer);
        }
    });

    test("create producer consumer pair", async () => {
        const producer = await createProducerClient(TEST_SERVER_URL);
        const producerInfo = producer.getConnectionInfo();
        const workspaceId = producerInfo.workspace_id!;
        const roomId = producerInfo.room_id!;
        roomManager.addRoom(workspaceId, roomId);

        try {
            const consumer = await createConsumerClient(workspaceId, roomId, TEST_SERVER_URL);

            // Both should be connected to same room
            const consumerInfo = consumer.getConnectionInfo();
            expect(producerInfo.room_id).toBe(consumerInfo.room_id);
            expect(producerInfo.workspace_id).toBe(consumerInfo.workspace_id);
            expect(producer.isConnected()).toBe(true);
            expect(consumer.isConnected()).toBe(true);

            // Test communication
            const updateCollector = new MessageCollector(1);
            consumer.onJointUpdate(updateCollector.collect);

            // Give some time for connection to stabilize
            await sleep(100);

            // Send update from producer
            await producer.sendStateSync({ test_joint: 42.0 });

            // Wait for message
            const receivedUpdates = await updateCollector.waitForMessages(2000);

            // Consumer should have received update
            expect(receivedUpdates.length).toBeGreaterThanOrEqual(1);

            await consumer.disconnect();
        } finally {
            await producer.disconnect();
            await roomManager.cleanup(producer);
        }
    });

    test("convenience functions with default url", async () => {
        const producer = await createProducerClient(TEST_SERVER_URL);
        const producerInfo = producer.getConnectionInfo();
        const workspaceId = producerInfo.workspace_id!;
        const roomId = producerInfo.room_id!;
        roomManager.addRoom(workspaceId, roomId);

        try {
            expect(producerInfo.base_url).toBe("http://localhost:8000");
            expect(producer.isConnected()).toBe(true);

            const consumer = await createConsumerClient(workspaceId, roomId, TEST_SERVER_URL);

            try {
                const consumerInfo = consumer.getConnectionInfo();
                expect(consumerInfo.base_url).toBe("http://localhost:8000");
                expect(consumer.isConnected()).toBe(true);
            } finally {
                await consumer.disconnect();
            }
        } finally {
            await producer.disconnect();
            await roomManager.cleanup(producer);
        }
    });

    test("multiple convenience producers", async () => {
        const producer1 = await createProducerClient(TEST_SERVER_URL);
        const producer2 = await createProducerClient(TEST_SERVER_URL);

        try {
            const info1 = producer1.getConnectionInfo();
            const info2 = producer2.getConnectionInfo();

            // Both should be connected to different rooms
            expect(info1.room_id).not.toBe(info2.room_id);
            expect(producer1.isConnected()).toBe(true);
            expect(producer2.isConnected()).toBe(true);

            // Track for cleanup
            roomManager.addRoom(info1.workspace_id!, info1.room_id!);
            roomManager.addRoom(info2.workspace_id!, info2.room_id!);

            // Both should work independently
            await producer1.sendStateSync({ joint1: 10.0 });
            await producer2.sendStateSync({ joint2: 20.0 });
        } finally {
            await producer1.disconnect();
            await producer2.disconnect();
            await roomManager.cleanup(producer1);
        }
    });

    test("create consumer nonexistent room", async () => {
        const fakeWorkspaceId = "00000000-0000-0000-0000-000000000000";
        const fakeRoomId = "00000000-0000-0000-0000-000000000000";

        const consumer = new RoboticsConsumer(TEST_SERVER_URL);
        const success = await consumer.connect(fakeWorkspaceId, fakeRoomId);

        // Should fail to connect to non-existent room
        expect(success).toBe(false);
        expect(consumer.isConnected()).toBe(false);
    });
});