@teneo-protocol/sdk 1.0.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/.github/workflows/publish-npm.yml +8 -6
  2. package/CHANGELOG.md +265 -0
  3. package/README.md +406 -53
  4. package/dist/core/websocket-client.d.ts +12 -0
  5. package/dist/core/websocket-client.d.ts.map +1 -1
  6. package/dist/core/websocket-client.js +22 -2
  7. package/dist/core/websocket-client.js.map +1 -1
  8. package/dist/handlers/message-handlers/agent-room-operation-response-handler.d.ts +76 -0
  9. package/dist/handlers/message-handlers/agent-room-operation-response-handler.d.ts.map +1 -0
  10. package/dist/handlers/message-handlers/agent-room-operation-response-handler.js +70 -0
  11. package/dist/handlers/message-handlers/agent-room-operation-response-handler.js.map +1 -0
  12. package/dist/handlers/message-handlers/agent-selected-handler.d.ts +92 -38
  13. package/dist/handlers/message-handlers/agent-selected-handler.d.ts.map +1 -1
  14. package/dist/handlers/message-handlers/agent-status-update-handler.d.ts +904 -0
  15. package/dist/handlers/message-handlers/agent-status-update-handler.d.ts.map +1 -0
  16. package/dist/handlers/message-handlers/agent-status-update-handler.js +51 -0
  17. package/dist/handlers/message-handlers/agent-status-update-handler.js.map +1 -0
  18. package/dist/handlers/message-handlers/auth-error-handler.d.ts +45 -31
  19. package/dist/handlers/message-handlers/auth-error-handler.d.ts.map +1 -1
  20. package/dist/handlers/message-handlers/auth-message-handler.d.ts +6 -0
  21. package/dist/handlers/message-handlers/auth-message-handler.d.ts.map +1 -1
  22. package/dist/handlers/message-handlers/auth-message-handler.js +65 -5
  23. package/dist/handlers/message-handlers/auth-message-handler.js.map +1 -1
  24. package/dist/handlers/message-handlers/auth-required-handler.d.ts +49 -31
  25. package/dist/handlers/message-handlers/auth-required-handler.d.ts.map +1 -1
  26. package/dist/handlers/message-handlers/auth-success-handler.d.ts +6 -0
  27. package/dist/handlers/message-handlers/auth-success-handler.d.ts.map +1 -1
  28. package/dist/handlers/message-handlers/auth-success-handler.js +46 -4
  29. package/dist/handlers/message-handlers/auth-success-handler.js.map +1 -1
  30. package/dist/handlers/message-handlers/challenge-handler.d.ts +45 -31
  31. package/dist/handlers/message-handlers/challenge-handler.d.ts.map +1 -1
  32. package/dist/handlers/message-handlers/error-message-handler.d.ts +49 -31
  33. package/dist/handlers/message-handlers/error-message-handler.d.ts.map +1 -1
  34. package/dist/handlers/message-handlers/index.d.ts +5 -0
  35. package/dist/handlers/message-handlers/index.d.ts.map +1 -1
  36. package/dist/handlers/message-handlers/index.js +23 -1
  37. package/dist/handlers/message-handlers/index.js.map +1 -1
  38. package/dist/handlers/message-handlers/list-available-agents-handler.d.ts +877 -0
  39. package/dist/handlers/message-handlers/list-available-agents-handler.d.ts.map +1 -0
  40. package/dist/handlers/message-handlers/list-available-agents-handler.js +38 -0
  41. package/dist/handlers/message-handlers/list-available-agents-handler.js.map +1 -0
  42. package/dist/handlers/message-handlers/list-room-agents-handler.d.ts +886 -0
  43. package/dist/handlers/message-handlers/list-room-agents-handler.d.ts.map +1 -0
  44. package/dist/handlers/message-handlers/list-room-agents-handler.js +51 -0
  45. package/dist/handlers/message-handlers/list-room-agents-handler.js.map +1 -0
  46. package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts +178 -89
  47. package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts.map +1 -1
  48. package/dist/handlers/message-handlers/ping-pong-handler.d.ts +62 -58
  49. package/dist/handlers/message-handlers/ping-pong-handler.d.ts.map +1 -1
  50. package/dist/handlers/message-handlers/regular-message-handler.d.ts +31 -29
  51. package/dist/handlers/message-handlers/regular-message-handler.d.ts.map +1 -1
  52. package/dist/handlers/message-handlers/room-operation-response-handler.d.ts +328 -0
  53. package/dist/handlers/message-handlers/room-operation-response-handler.d.ts.map +1 -0
  54. package/dist/handlers/message-handlers/room-operation-response-handler.js +92 -0
  55. package/dist/handlers/message-handlers/room-operation-response-handler.js.map +1 -0
  56. package/dist/handlers/message-handlers/subscribe-response-handler.d.ts +53 -31
  57. package/dist/handlers/message-handlers/subscribe-response-handler.d.ts.map +1 -1
  58. package/dist/handlers/message-handlers/types.d.ts +2 -0
  59. package/dist/handlers/message-handlers/types.d.ts.map +1 -1
  60. package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts +53 -31
  61. package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts.map +1 -1
  62. package/dist/managers/agent-room-manager.d.ts +222 -0
  63. package/dist/managers/agent-room-manager.d.ts.map +1 -0
  64. package/dist/managers/agent-room-manager.js +508 -0
  65. package/dist/managers/agent-room-manager.js.map +1 -0
  66. package/dist/managers/index.d.ts +2 -0
  67. package/dist/managers/index.d.ts.map +1 -1
  68. package/dist/managers/index.js +5 -1
  69. package/dist/managers/index.js.map +1 -1
  70. package/dist/managers/room-management-manager.d.ts +213 -0
  71. package/dist/managers/room-management-manager.d.ts.map +1 -0
  72. package/dist/managers/room-management-manager.js +440 -0
  73. package/dist/managers/room-management-manager.js.map +1 -0
  74. package/dist/managers/room-manager.d.ts +4 -4
  75. package/dist/managers/room-manager.d.ts.map +1 -1
  76. package/dist/managers/room-manager.js.map +1 -1
  77. package/dist/teneo-sdk.d.ts +333 -13
  78. package/dist/teneo-sdk.d.ts.map +1 -1
  79. package/dist/teneo-sdk.js +468 -1
  80. package/dist/teneo-sdk.js.map +1 -1
  81. package/dist/types/config.d.ts +63 -54
  82. package/dist/types/config.d.ts.map +1 -1
  83. package/dist/types/config.js +8 -4
  84. package/dist/types/config.js.map +1 -1
  85. package/dist/types/error-codes.d.ts +2 -0
  86. package/dist/types/error-codes.d.ts.map +1 -1
  87. package/dist/types/error-codes.js +3 -0
  88. package/dist/types/error-codes.js.map +1 -1
  89. package/dist/types/events.d.ts +132 -68
  90. package/dist/types/events.d.ts.map +1 -1
  91. package/dist/types/events.js.map +1 -1
  92. package/dist/types/index.d.ts +1 -1
  93. package/dist/types/index.d.ts.map +1 -1
  94. package/dist/types/index.js +27 -2
  95. package/dist/types/index.js.map +1 -1
  96. package/dist/types/messages.d.ts +11396 -2559
  97. package/dist/types/messages.d.ts.map +1 -1
  98. package/dist/types/messages.js +294 -27
  99. package/dist/types/messages.js.map +1 -1
  100. package/examples/.env.example +1 -1
  101. package/examples/agent-room-management-example.ts +334 -0
  102. package/examples/claude-agent-x-follower/.env.example +2 -2
  103. package/examples/claude-agent-x-follower/QUICKSTART.md +1 -1
  104. package/examples/claude-agent-x-follower/README.md +1 -1
  105. package/examples/n8n-teneo/.env.example +2 -2
  106. package/examples/n8n-teneo/README.md +1 -1
  107. package/examples/openai-teneo/.env.example +2 -2
  108. package/examples/openai-teneo/README.md +1 -1
  109. package/examples/production-dashboard/.env.example +2 -2
  110. package/examples/production-dashboard/README.md +89 -12
  111. package/examples/production-dashboard/public/dashboard.html +1173 -601
  112. package/examples/production-dashboard/server.ts +347 -5
  113. package/examples/room-management-example.ts +285 -0
  114. package/examples/usage/.env.example +1 -1
  115. package/examples/usage/01-connect.ts +1 -1
  116. package/examples/usage/02-list-agents.ts +1 -1
  117. package/examples/usage/03-pick-agent.ts +1 -1
  118. package/examples/usage/04-find-by-capability.ts +1 -1
  119. package/examples/usage/05-webhook-example.ts +1 -1
  120. package/examples/usage/06-simple-api-server.ts +1 -1
  121. package/examples/usage/07-event-listener.ts +1 -1
  122. package/examples/usage/README.md +1 -1
  123. package/package.json +9 -1
  124. package/src/core/websocket-client.ts +26 -2
  125. package/src/handlers/message-handlers/agent-room-operation-response-handler.ts +83 -0
  126. package/src/handlers/message-handlers/agent-status-update-handler.ts +58 -0
  127. package/src/handlers/message-handlers/auth-message-handler.ts +73 -5
  128. package/src/handlers/message-handlers/auth-success-handler.ts +58 -6
  129. package/src/handlers/message-handlers/index.ts +19 -0
  130. package/src/handlers/message-handlers/list-available-agents-handler.ts +41 -0
  131. package/src/handlers/message-handlers/list-room-agents-handler.ts +61 -0
  132. package/src/handlers/message-handlers/room-operation-response-handler.ts +105 -0
  133. package/src/handlers/message-handlers/types.ts +6 -0
  134. package/src/managers/agent-room-manager.ts +609 -0
  135. package/src/managers/index.ts +2 -0
  136. package/src/managers/room-management-manager.ts +523 -0
  137. package/src/managers/room-manager.ts +4 -5
  138. package/src/teneo-sdk.ts +505 -4
  139. package/src/types/config.ts +10 -5
  140. package/src/types/error-codes.ts +4 -0
  141. package/src/types/events.ts +24 -0
  142. package/src/types/index.ts +55 -0
  143. package/src/types/messages.ts +374 -41
  144. package/tests/integration/room-management.test.ts +514 -0
  145. package/tests/integration/websocket.test.ts +1 -1
  146. package/tests/unit/handlers/agent-room-operation-response-handler.test.ts +394 -0
  147. package/tests/unit/handlers/agent-status-update-handler.test.ts +407 -0
  148. package/tests/unit/handlers/auth-success-handler-rooms.test.ts +699 -0
  149. package/tests/unit/handlers/list-available-agents-handler.test.ts +256 -0
  150. package/tests/unit/handlers/list-room-agents-handler.test.ts +294 -0
  151. package/tests/unit/handlers/room-operation-response-handler.test.ts +527 -0
  152. package/tests/unit/managers/agent-room-manager.test.ts +534 -0
  153. package/tests/unit/managers/room-management-manager.test.ts +438 -0
@@ -0,0 +1,438 @@
1
+ /**
2
+ * Unit tests for RoomManagementManager
3
+ * Tests room CRUD operations, validation, caching, and error handling
4
+ */
5
+
6
+ import { describe, it, expect, beforeEach, vi } from "vitest";
7
+ import { RoomManagementManager } from "../../../src/managers/room-management-manager";
8
+ import { WebSocketClient } from "../../../src/core/websocket-client";
9
+ import { Logger, RoomInfo } from "../../../src/types";
10
+ import { ErrorCode } from "../../../src/types/error-codes";
11
+
12
+ describe("RoomManagementManager", () => {
13
+ let manager: RoomManagementManager;
14
+ let mockWsClient: WebSocketClient;
15
+ let mockLogger: Logger;
16
+
17
+ beforeEach(() => {
18
+ // Create mock logger
19
+ mockLogger = {
20
+ debug: vi.fn(),
21
+ info: vi.fn(),
22
+ warn: vi.fn(),
23
+ error: vi.fn()
24
+ };
25
+
26
+ // Create mock WebSocket client
27
+ mockWsClient = {
28
+ isConnected: true,
29
+ sendMessage: vi.fn().mockResolvedValue(undefined)
30
+ } as any;
31
+
32
+ // Create manager instance
33
+ manager = new RoomManagementManager(mockWsClient, mockLogger);
34
+ });
35
+
36
+ describe("createRoom", () => {
37
+ it("should create a room successfully", async () => {
38
+ const roomOptions = {
39
+ name: "Test Room",
40
+ description: "Test Description"
41
+ };
42
+
43
+ const createdRoom: RoomInfo = {
44
+ id: "room-123",
45
+ name: roomOptions.name,
46
+ description: roomOptions.description,
47
+ is_public: false,
48
+ created_by: "user-123",
49
+ created_at: new Date().toISOString(),
50
+ updated_at: new Date().toISOString(),
51
+ is_owner: true
52
+ };
53
+
54
+ // Start create operation
55
+ const createPromise = manager.createRoom(roomOptions);
56
+
57
+ // Simulate server response
58
+ setTimeout(() => {
59
+ manager.emit("room:created", createdRoom);
60
+ }, 10);
61
+
62
+ const result = await createPromise;
63
+
64
+ expect(result).toEqual(createdRoom);
65
+ expect(mockWsClient.sendMessage).toHaveBeenCalledWith({
66
+ type: "create_room",
67
+ data: {
68
+ name: roomOptions.name,
69
+ description: roomOptions.description
70
+ }
71
+ });
72
+ });
73
+
74
+ it("should reject if not connected", async () => {
75
+ mockWsClient.isConnected = false;
76
+
77
+ await expect(
78
+ manager.createRoom({ name: "Test Room" })
79
+ ).rejects.toThrow("Not connected to Teneo network");
80
+ });
81
+
82
+ it("should validate room name", async () => {
83
+ await expect(manager.createRoom({ name: "" })).rejects.toThrow("Room name cannot be empty");
84
+
85
+ await expect(
86
+ manager.createRoom({ name: "a".repeat(101) })
87
+ ).rejects.toThrow("Room name too long");
88
+ });
89
+
90
+ it("should validate room description", async () => {
91
+ await expect(
92
+ manager.createRoom({
93
+ name: "Test",
94
+ description: "a".repeat(501)
95
+ })
96
+ ).rejects.toThrow("Room description too long");
97
+ });
98
+
99
+ it("should check room limit before creating private room", async () => {
100
+ manager.setRoomLimit(1);
101
+ manager.setOwnedRooms([
102
+ {
103
+ id: "room-1",
104
+ name: "Existing Room",
105
+ is_owner: true
106
+ } as RoomInfo
107
+ ]);
108
+
109
+ await expect(
110
+ manager.createRoom({ name: "New Room" })
111
+ ).rejects.toThrow("Room limit reached");
112
+ });
113
+
114
+ it("should timeout if no response", async () => {
115
+ vi.useFakeTimers();
116
+
117
+ const createPromise = manager.createRoom({ name: "Test Room" });
118
+
119
+ vi.advanceTimersByTime(30001);
120
+
121
+ await expect(createPromise).rejects.toThrow("timeout");
122
+
123
+ vi.useRealTimers();
124
+ });
125
+
126
+ it("should handle create errors", async () => {
127
+ const createPromise = manager.createRoom({ name: "Test Room" });
128
+
129
+ setTimeout(() => {
130
+ manager.emit("room:create_error", new Error("Server error"));
131
+ }, 10);
132
+
133
+ await expect(createPromise).rejects.toThrow("Server error");
134
+ });
135
+ });
136
+
137
+ describe("updateRoom", () => {
138
+ beforeEach(() => {
139
+ manager.setOwnedRooms([
140
+ {
141
+ id: "room-123",
142
+ name: "Original Room",
143
+ is_owner: true
144
+ } as RoomInfo
145
+ ]);
146
+ });
147
+
148
+ it("should update a room successfully", async () => {
149
+ const updates = {
150
+ name: "Updated Room",
151
+ description: "Updated Description"
152
+ };
153
+
154
+ const updatedRoom: RoomInfo = {
155
+ id: "room-123",
156
+ name: updates.name,
157
+ description: updates.description,
158
+ is_owner: true
159
+ } as RoomInfo;
160
+
161
+ const updatePromise = manager.updateRoom("room-123", updates);
162
+
163
+ setTimeout(() => {
164
+ manager.emit("room:updated", updatedRoom);
165
+ }, 10);
166
+
167
+ const result = await updatePromise;
168
+
169
+ expect(result).toEqual(updatedRoom);
170
+ expect(mockWsClient.sendMessage).toHaveBeenCalledWith({
171
+ type: "update_room",
172
+ data: {
173
+ room_id: "room-123",
174
+ name: updates.name,
175
+ description: updates.description
176
+ }
177
+ });
178
+ });
179
+
180
+ it("should reject if user doesn't own room", async () => {
181
+ await expect(
182
+ manager.updateRoom("room-999", { name: "New Name" })
183
+ ).rejects.toThrow("don't own this room");
184
+ });
185
+
186
+ it("should require at least one field", async () => {
187
+ await expect(manager.updateRoom("room-123", {})).rejects.toThrow(
188
+ "At least one field"
189
+ );
190
+ });
191
+
192
+ it("should validate updated name", async () => {
193
+ await expect(
194
+ manager.updateRoom("room-123", { name: "" })
195
+ ).rejects.toThrow("Room name cannot be empty");
196
+ });
197
+
198
+ it("should validate updated description", async () => {
199
+ await expect(
200
+ manager.updateRoom("room-123", { description: "a".repeat(501) })
201
+ ).rejects.toThrow("Room description too long");
202
+ });
203
+ });
204
+
205
+ describe("deleteRoom", () => {
206
+ beforeEach(() => {
207
+ manager.setOwnedRooms([
208
+ {
209
+ id: "room-123",
210
+ name: "Room to Delete",
211
+ is_owner: true
212
+ } as RoomInfo
213
+ ]);
214
+ });
215
+
216
+ it("should delete a room successfully", async () => {
217
+ const deletePromise = manager.deleteRoom("room-123");
218
+
219
+ setTimeout(() => {
220
+ manager.emit("room:deleted", "room-123");
221
+ }, 10);
222
+
223
+ await expect(deletePromise).resolves.toBeUndefined();
224
+ expect(mockWsClient.sendMessage).toHaveBeenCalledWith({
225
+ type: "delete_room",
226
+ data: {
227
+ room_id: "room-123"
228
+ }
229
+ });
230
+ });
231
+
232
+ it("should reject if user doesn't own room", async () => {
233
+ await expect(manager.deleteRoom("room-999")).rejects.toThrow("don't own this room");
234
+ });
235
+
236
+ it("should handle delete errors", async () => {
237
+ const deletePromise = manager.deleteRoom("room-123");
238
+
239
+ setTimeout(() => {
240
+ manager.emit("room:delete_error", new Error("Cannot delete"), "room-123");
241
+ }, 10);
242
+
243
+ await expect(deletePromise).rejects.toThrow("Cannot delete");
244
+ });
245
+ });
246
+
247
+ describe("Query Methods", () => {
248
+ it("should return owned rooms", () => {
249
+ const ownedRooms: RoomInfo[] = [
250
+ { id: "room-1", name: "Room 1", is_owner: true } as RoomInfo,
251
+ { id: "room-2", name: "Room 2", is_owner: true } as RoomInfo
252
+ ];
253
+
254
+ manager.setOwnedRooms(ownedRooms);
255
+
256
+ const result = manager.getOwnedRooms();
257
+ expect(result).toHaveLength(2);
258
+ expect(result[0].id).toBe("room-1");
259
+ expect(result[1].id).toBe("room-2");
260
+ });
261
+
262
+ it("should return shared rooms", () => {
263
+ const sharedRooms: RoomInfo[] = [
264
+ { id: "room-3", name: "Shared 1", is_owner: false } as RoomInfo,
265
+ { id: "room-4", name: "Shared 2", is_owner: false } as RoomInfo
266
+ ];
267
+
268
+ manager.setSharedRooms(sharedRooms);
269
+
270
+ const result = manager.getSharedRooms();
271
+ expect(result).toHaveLength(2);
272
+ expect(result[0].id).toBe("room-3");
273
+ });
274
+
275
+ it("should get room by ID from owned rooms", () => {
276
+ manager.setOwnedRooms([
277
+ { id: "room-1", name: "Owned Room", is_owner: true } as RoomInfo
278
+ ]);
279
+
280
+ const result = manager.getRoomById("room-1");
281
+ expect(result).toBeDefined();
282
+ expect(result!.name).toBe("Owned Room");
283
+ });
284
+
285
+ it("should get room by ID from shared rooms", () => {
286
+ manager.setSharedRooms([
287
+ { id: "room-2", name: "Shared Room", is_owner: false } as RoomInfo
288
+ ]);
289
+
290
+ const result = manager.getRoomById("room-2");
291
+ expect(result).toBeDefined();
292
+ expect(result!.name).toBe("Shared Room");
293
+ });
294
+
295
+ it("should return undefined for non-existent room", () => {
296
+ const result = manager.getRoomById("room-999");
297
+ expect(result).toBeUndefined();
298
+ });
299
+
300
+ it("should get room limit", () => {
301
+ manager.setRoomLimit(5);
302
+ expect(manager.getRoomLimit()).toBe(5);
303
+ });
304
+
305
+ it("should get owned room count", () => {
306
+ manager.setOwnedRooms([
307
+ { id: "room-1", name: "Room 1", is_owner: true } as RoomInfo,
308
+ { id: "room-2", name: "Room 2", is_owner: true } as RoomInfo,
309
+ { id: "room-3", name: "Room 3", is_owner: true } as RoomInfo
310
+ ]);
311
+
312
+ expect(manager.getOwnedRoomCount()).toBe(3);
313
+ });
314
+
315
+ it("should check if can create room", () => {
316
+ manager.setRoomLimit(3);
317
+ manager.setOwnedRooms([
318
+ { id: "room-1", name: "Room 1", is_owner: true } as RoomInfo,
319
+ { id: "room-2", name: "Room 2", is_owner: true } as RoomInfo
320
+ ]);
321
+
322
+ expect(manager.canCreateRoom()).toBe(true);
323
+
324
+ manager.setOwnedRooms([
325
+ { id: "room-1", name: "Room 1", is_owner: true } as RoomInfo,
326
+ { id: "room-2", name: "Room 2", is_owner: true } as RoomInfo,
327
+ { id: "room-3", name: "Room 3", is_owner: true } as RoomInfo
328
+ ]);
329
+
330
+ expect(manager.canCreateRoom()).toBe(false);
331
+ });
332
+ });
333
+
334
+ describe("Cache Management", () => {
335
+ it("should upsert owned room", () => {
336
+ const room: RoomInfo = {
337
+ id: "room-1",
338
+ name: "New Room",
339
+ is_owner: true
340
+ } as RoomInfo;
341
+
342
+ manager.upsertRoom(room);
343
+
344
+ const result = manager.getRoomById("room-1");
345
+ expect(result).toBeDefined();
346
+ expect(result!.name).toBe("New Room");
347
+ expect(manager.getOwnedRoomCount()).toBe(1);
348
+ });
349
+
350
+ it("should upsert shared room", () => {
351
+ const room: RoomInfo = {
352
+ id: "room-2",
353
+ name: "Shared Room",
354
+ is_owner: false
355
+ } as RoomInfo;
356
+
357
+ manager.upsertRoom(room);
358
+
359
+ const result = manager.getRoomById("room-2");
360
+ expect(result).toBeDefined();
361
+ expect(result!.name).toBe("Shared Room");
362
+ expect(manager.getSharedRooms()).toHaveLength(1);
363
+ expect(manager.getOwnedRoomCount()).toBe(0);
364
+ });
365
+
366
+ it("should move room from shared to owned when ownership changes", () => {
367
+ const room: RoomInfo = {
368
+ id: "room-1",
369
+ name: "Room",
370
+ is_owner: false
371
+ } as RoomInfo;
372
+
373
+ manager.upsertRoom(room);
374
+ expect(manager.getSharedRooms()).toHaveLength(1);
375
+ expect(manager.getOwnedRoomCount()).toBe(0);
376
+
377
+ // Update to owned
378
+ room.is_owner = true;
379
+ manager.upsertRoom(room);
380
+ expect(manager.getSharedRooms()).toHaveLength(0);
381
+ expect(manager.getOwnedRoomCount()).toBe(1);
382
+ });
383
+
384
+ it("should remove room from cache", () => {
385
+ manager.setOwnedRooms([
386
+ { id: "room-1", name: "Room 1", is_owner: true } as RoomInfo
387
+ ]);
388
+
389
+ expect(manager.getRoomById("room-1")).toBeDefined();
390
+
391
+ manager.removeRoom("room-1");
392
+
393
+ expect(manager.getRoomById("room-1")).toBeUndefined();
394
+ expect(manager.getOwnedRoomCount()).toBe(0);
395
+ });
396
+
397
+ it("should clear all caches", () => {
398
+ manager.setRoomLimit(5);
399
+ manager.setOwnedRooms([
400
+ { id: "room-1", name: "Room 1", is_owner: true } as RoomInfo
401
+ ]);
402
+ manager.setSharedRooms([
403
+ { id: "room-2", name: "Room 2", is_owner: false } as RoomInfo
404
+ ]);
405
+
406
+ manager.clearCaches();
407
+
408
+ expect(manager.getOwnedRoomCount()).toBe(0);
409
+ expect(manager.getSharedRooms()).toHaveLength(0);
410
+ expect(manager.getRoomLimit()).toBe(1); // Reset to default
411
+ });
412
+ });
413
+
414
+ describe("Return Value Immutability", () => {
415
+ it("should return defensive copies from getOwnedRooms", () => {
416
+ manager.setOwnedRooms([
417
+ { id: "room-1", name: "Room 1", is_owner: true } as RoomInfo
418
+ ]);
419
+
420
+ const rooms1 = manager.getOwnedRooms();
421
+ const rooms2 = manager.getOwnedRooms();
422
+
423
+ expect(rooms1).not.toBe(rooms2);
424
+ expect(rooms1[0]).not.toBe(rooms2[0]);
425
+ });
426
+
427
+ it("should return defensive copy from getRoomById", () => {
428
+ manager.setOwnedRooms([
429
+ { id: "room-1", name: "Room 1", is_owner: true } as RoomInfo
430
+ ]);
431
+
432
+ const room1 = manager.getRoomById("room-1");
433
+ const room2 = manager.getRoomById("room-1");
434
+
435
+ expect(room1).not.toBe(room2);
436
+ });
437
+ });
438
+ });