@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,699 @@
1
+ /**
2
+ * Unit tests for AuthSuccessHandler - Room Management Initialization
3
+ * Tests room categorization and room management manager initialization
4
+ */
5
+
6
+ import { describe, it, expect, beforeEach, vi } from "vitest";
7
+ import { AuthSuccessHandler } from "../../../src/handlers/message-handlers/auth-success-handler";
8
+ import { HandlerContext } from "../../../src/handlers/message-handlers/types";
9
+ import { AuthSuccessMessage, RoomInfo, Logger } from "../../../src/types";
10
+
11
+ describe("AuthSuccessHandler - Room Management", () => {
12
+ let handler: AuthSuccessHandler;
13
+ let mockContext: HandlerContext;
14
+ let mockLogger: Logger;
15
+ let mockRoomManagementManager: any;
16
+ let emitSpy: ReturnType<typeof vi.fn>;
17
+ let updateAuthStateSpy: ReturnType<typeof vi.fn>;
18
+ let updateConnectionStateSpy: ReturnType<typeof vi.fn>;
19
+ let getAuthStateSpy: ReturnType<typeof vi.fn>;
20
+
21
+ beforeEach(() => {
22
+ // Create mock logger
23
+ mockLogger = {
24
+ debug: vi.fn(),
25
+ info: vi.fn(),
26
+ warn: vi.fn(),
27
+ error: vi.fn()
28
+ };
29
+
30
+ // Create mock room management manager
31
+ mockRoomManagementManager = {
32
+ setRoomLimit: vi.fn(),
33
+ setOwnedRooms: vi.fn(),
34
+ setSharedRooms: vi.fn()
35
+ };
36
+
37
+ // Create spies
38
+ emitSpy = vi.fn();
39
+ updateAuthStateSpy = vi.fn();
40
+ updateConnectionStateSpy = vi.fn();
41
+ getAuthStateSpy = vi.fn().mockReturnValue({
42
+ authenticated: true,
43
+ clientId: "client-123",
44
+ walletAddress: "0xabc..."
45
+ });
46
+
47
+ // Create mock context
48
+ mockContext = {
49
+ emit: emitSpy,
50
+ sendWebhook: vi.fn().mockResolvedValue(undefined),
51
+ logger: mockLogger,
52
+ getConnectionState: vi.fn(),
53
+ getAuthState: getAuthStateSpy,
54
+ updateConnectionState: updateConnectionStateSpy,
55
+ updateAuthState: updateAuthStateSpy,
56
+ sendMessage: vi.fn(),
57
+ roomManagementManager: mockRoomManagementManager
58
+ };
59
+
60
+ // Create handler instance
61
+ handler = new AuthSuccessHandler();
62
+ });
63
+
64
+ describe("Room Extraction", () => {
65
+ it("should extract rooms from auth success message", async () => {
66
+ const rooms: RoomInfo[] = [
67
+ {
68
+ id: "room-1",
69
+ name: "My Room",
70
+ is_owner: true
71
+ } as RoomInfo,
72
+ {
73
+ id: "room-2",
74
+ name: "Shared Room",
75
+ is_owner: false
76
+ } as RoomInfo
77
+ ];
78
+
79
+ const message: AuthSuccessMessage = {
80
+ type: "auth_success",
81
+ data: {
82
+ id: "client-123",
83
+ type: "user",
84
+ address: "0xabc...",
85
+ rooms
86
+ }
87
+ } as AuthSuccessMessage;
88
+
89
+ await handler.handle(message, mockContext);
90
+
91
+ // Should update auth state with room data
92
+ expect(updateAuthStateSpy).toHaveBeenCalledWith(
93
+ expect.objectContaining({
94
+ roomObjects: rooms
95
+ })
96
+ );
97
+ });
98
+
99
+ it("should handle missing rooms array", async () => {
100
+ const message: AuthSuccessMessage = {
101
+ type: "auth_success",
102
+ data: {
103
+ id: "client-123",
104
+ type: "user",
105
+ address: "0xabc..."
106
+ // rooms field is undefined
107
+ }
108
+ } as AuthSuccessMessage;
109
+
110
+ await handler.handle(message, mockContext);
111
+
112
+ // Should still work with empty array
113
+ expect(updateAuthStateSpy).toHaveBeenCalledWith(
114
+ expect.objectContaining({
115
+ roomObjects: [],
116
+ privateRoomIds: [],
117
+ sharedRoomIds: []
118
+ })
119
+ );
120
+ });
121
+
122
+ it("should handle null rooms", async () => {
123
+ const message: AuthSuccessMessage = {
124
+ type: "auth_success",
125
+ data: {
126
+ id: "client-123",
127
+ type: "user",
128
+ address: "0xabc...",
129
+ rooms: null as any
130
+ }
131
+ } as AuthSuccessMessage;
132
+
133
+ await handler.handle(message, mockContext);
134
+
135
+ expect(updateAuthStateSpy).toHaveBeenCalledWith(
136
+ expect.objectContaining({
137
+ roomObjects: []
138
+ })
139
+ );
140
+ });
141
+
142
+ it("should handle empty rooms array", async () => {
143
+ const message: AuthSuccessMessage = {
144
+ type: "auth_success",
145
+ data: {
146
+ id: "client-123",
147
+ type: "user",
148
+ address: "0xabc...",
149
+ rooms: []
150
+ }
151
+ } as AuthSuccessMessage;
152
+
153
+ await handler.handle(message, mockContext);
154
+
155
+ expect(updateAuthStateSpy).toHaveBeenCalledWith(
156
+ expect.objectContaining({
157
+ roomObjects: [],
158
+ privateRoomIds: [],
159
+ sharedRoomIds: []
160
+ })
161
+ );
162
+ });
163
+ });
164
+
165
+ describe("Room Categorization", () => {
166
+ it("should categorize owned rooms (is_owner: true)", async () => {
167
+ const rooms: RoomInfo[] = [
168
+ {
169
+ id: "room-1",
170
+ name: "My Room 1",
171
+ is_owner: true
172
+ } as RoomInfo,
173
+ {
174
+ id: "room-2",
175
+ name: "My Room 2",
176
+ is_owner: true
177
+ } as RoomInfo
178
+ ];
179
+
180
+ const message: AuthSuccessMessage = {
181
+ type: "auth_success",
182
+ data: {
183
+ id: "client-123",
184
+ type: "user",
185
+ address: "0xabc...",
186
+ rooms
187
+ }
188
+ } as AuthSuccessMessage;
189
+
190
+ await handler.handle(message, mockContext);
191
+
192
+ expect(updateAuthStateSpy).toHaveBeenCalledWith(
193
+ expect.objectContaining({
194
+ privateRoomIds: ["room-1", "room-2"],
195
+ sharedRoomIds: []
196
+ })
197
+ );
198
+ });
199
+
200
+ it("should categorize shared rooms (is_owner: false)", async () => {
201
+ const rooms: RoomInfo[] = [
202
+ {
203
+ id: "room-3",
204
+ name: "Shared Room 1",
205
+ is_owner: false
206
+ } as RoomInfo,
207
+ {
208
+ id: "room-4",
209
+ name: "Shared Room 2",
210
+ is_owner: false
211
+ } as RoomInfo
212
+ ];
213
+
214
+ const message: AuthSuccessMessage = {
215
+ type: "auth_success",
216
+ data: {
217
+ id: "client-123",
218
+ type: "user",
219
+ address: "0xabc...",
220
+ rooms
221
+ }
222
+ } as AuthSuccessMessage;
223
+
224
+ await handler.handle(message, mockContext);
225
+
226
+ expect(updateAuthStateSpy).toHaveBeenCalledWith(
227
+ expect.objectContaining({
228
+ privateRoomIds: [],
229
+ sharedRoomIds: ["room-3", "room-4"]
230
+ })
231
+ );
232
+ });
233
+
234
+ it("should categorize mixed owned and shared rooms", async () => {
235
+ const rooms: RoomInfo[] = [
236
+ {
237
+ id: "room-1",
238
+ name: "My Room",
239
+ is_owner: true
240
+ } as RoomInfo,
241
+ {
242
+ id: "room-2",
243
+ name: "Shared Room",
244
+ is_owner: false
245
+ } as RoomInfo,
246
+ {
247
+ id: "room-3",
248
+ name: "Another Owned",
249
+ is_owner: true
250
+ } as RoomInfo,
251
+ {
252
+ id: "room-4",
253
+ name: "Another Shared",
254
+ is_owner: false
255
+ } as RoomInfo
256
+ ];
257
+
258
+ const message: AuthSuccessMessage = {
259
+ type: "auth_success",
260
+ data: {
261
+ id: "client-123",
262
+ type: "user",
263
+ address: "0xabc...",
264
+ rooms
265
+ }
266
+ } as AuthSuccessMessage;
267
+
268
+ await handler.handle(message, mockContext);
269
+
270
+ expect(updateAuthStateSpy).toHaveBeenCalledWith(
271
+ expect.objectContaining({
272
+ privateRoomIds: ["room-1", "room-3"],
273
+ sharedRoomIds: ["room-2", "room-4"]
274
+ })
275
+ );
276
+ });
277
+
278
+ it("should handle rooms with missing is_owner flag", async () => {
279
+ const rooms: RoomInfo[] = [
280
+ {
281
+ id: "room-1",
282
+ name: "Room without flag"
283
+ // is_owner is undefined
284
+ } as RoomInfo
285
+ ];
286
+
287
+ const message: AuthSuccessMessage = {
288
+ type: "auth_success",
289
+ data: {
290
+ id: "client-123",
291
+ type: "user",
292
+ address: "0xabc...",
293
+ rooms
294
+ }
295
+ } as AuthSuccessMessage;
296
+
297
+ await handler.handle(message, mockContext);
298
+
299
+ // Undefined is_owner should be treated as falsy (shared room)
300
+ expect(updateAuthStateSpy).toHaveBeenCalledWith(
301
+ expect.objectContaining({
302
+ privateRoomIds: [],
303
+ sharedRoomIds: ["room-1"]
304
+ })
305
+ );
306
+ });
307
+ });
308
+
309
+ describe("Room Management Manager Initialization", () => {
310
+ it("should initialize room management manager with room limit", async () => {
311
+ const message: AuthSuccessMessage = {
312
+ type: "auth_success",
313
+ data: {
314
+ id: "client-123",
315
+ type: "user",
316
+ address: "0xabc...",
317
+ rooms: [],
318
+ max_private_rooms: 5
319
+ }
320
+ } as AuthSuccessMessage;
321
+
322
+ await handler.handle(message, mockContext);
323
+
324
+ expect(mockRoomManagementManager.setRoomLimit).toHaveBeenCalledWith(5);
325
+ });
326
+
327
+ it("should initialize with owned rooms", async () => {
328
+ const ownedRooms: RoomInfo[] = [
329
+ {
330
+ id: "room-1",
331
+ name: "My Room 1",
332
+ is_owner: true
333
+ } as RoomInfo,
334
+ {
335
+ id: "room-2",
336
+ name: "My Room 2",
337
+ is_owner: true
338
+ } as RoomInfo
339
+ ];
340
+
341
+ const message: AuthSuccessMessage = {
342
+ type: "auth_success",
343
+ data: {
344
+ id: "client-123",
345
+ type: "user",
346
+ address: "0xabc...",
347
+ rooms: ownedRooms
348
+ }
349
+ } as AuthSuccessMessage;
350
+
351
+ await handler.handle(message, mockContext);
352
+
353
+ expect(mockRoomManagementManager.setOwnedRooms).toHaveBeenCalledWith(ownedRooms);
354
+ });
355
+
356
+ it("should initialize with shared rooms", async () => {
357
+ const sharedRooms: RoomInfo[] = [
358
+ {
359
+ id: "room-3",
360
+ name: "Shared Room 1",
361
+ is_owner: false
362
+ } as RoomInfo,
363
+ {
364
+ id: "room-4",
365
+ name: "Shared Room 2",
366
+ is_owner: false
367
+ } as RoomInfo
368
+ ];
369
+
370
+ const message: AuthSuccessMessage = {
371
+ type: "auth_success",
372
+ data: {
373
+ id: "client-123",
374
+ type: "user",
375
+ address: "0xabc...",
376
+ rooms: sharedRooms
377
+ }
378
+ } as AuthSuccessMessage;
379
+
380
+ await handler.handle(message, mockContext);
381
+
382
+ expect(mockRoomManagementManager.setSharedRooms).toHaveBeenCalledWith(sharedRooms);
383
+ });
384
+
385
+ it("should initialize with mixed rooms", async () => {
386
+ const rooms: RoomInfo[] = [
387
+ {
388
+ id: "room-1",
389
+ name: "My Room",
390
+ is_owner: true
391
+ } as RoomInfo,
392
+ {
393
+ id: "room-2",
394
+ name: "Shared Room",
395
+ is_owner: false
396
+ } as RoomInfo,
397
+ {
398
+ id: "room-3",
399
+ name: "Another Owned",
400
+ is_owner: true
401
+ } as RoomInfo
402
+ ];
403
+
404
+ const message: AuthSuccessMessage = {
405
+ type: "auth_success",
406
+ data: {
407
+ id: "client-123",
408
+ type: "user",
409
+ address: "0xabc...",
410
+ rooms,
411
+ max_private_rooms: 3
412
+ }
413
+ } as AuthSuccessMessage;
414
+
415
+ await handler.handle(message, mockContext);
416
+
417
+ const ownedRooms = rooms.filter((r) => r.is_owner);
418
+ const sharedRooms = rooms.filter((r) => !r.is_owner);
419
+
420
+ expect(mockRoomManagementManager.setRoomLimit).toHaveBeenCalledWith(3);
421
+ expect(mockRoomManagementManager.setOwnedRooms).toHaveBeenCalledWith(ownedRooms);
422
+ expect(mockRoomManagementManager.setSharedRooms).toHaveBeenCalledWith(sharedRooms);
423
+ });
424
+
425
+ it("should log debug info after initialization", async () => {
426
+ const rooms: RoomInfo[] = [
427
+ { id: "room-1", name: "Owned", is_owner: true } as RoomInfo,
428
+ { id: "room-2", name: "Shared", is_owner: false } as RoomInfo
429
+ ];
430
+
431
+ const message: AuthSuccessMessage = {
432
+ type: "auth_success",
433
+ data: {
434
+ id: "client-123",
435
+ type: "user",
436
+ address: "0xabc...",
437
+ rooms,
438
+ max_private_rooms: 5
439
+ }
440
+ } as AuthSuccessMessage;
441
+
442
+ await handler.handle(message, mockContext);
443
+
444
+ expect(mockLogger.debug).toHaveBeenCalledWith(
445
+ "Room management initialized",
446
+ expect.objectContaining({
447
+ owned: 1,
448
+ shared: 1,
449
+ limit: 5
450
+ })
451
+ );
452
+ });
453
+
454
+ it("should work without room limit", async () => {
455
+ const message: AuthSuccessMessage = {
456
+ type: "auth_success",
457
+ data: {
458
+ id: "client-123",
459
+ type: "user",
460
+ address: "0xabc...",
461
+ rooms: []
462
+ // max_private_rooms is undefined
463
+ }
464
+ } as AuthSuccessMessage;
465
+
466
+ await handler.handle(message, mockContext);
467
+
468
+ // Should not call setRoomLimit
469
+ expect(mockRoomManagementManager.setRoomLimit).not.toHaveBeenCalled();
470
+
471
+ // Should still initialize room lists
472
+ expect(mockRoomManagementManager.setOwnedRooms).toHaveBeenCalledWith([]);
473
+ expect(mockRoomManagementManager.setSharedRooms).toHaveBeenCalledWith([]);
474
+ });
475
+ });
476
+
477
+ describe("Backward Compatibility", () => {
478
+ it("should include deprecated fields in auth state", async () => {
479
+ const rooms: RoomInfo[] = [
480
+ { id: "room-1", name: "Room 1", is_owner: true } as RoomInfo,
481
+ { id: "room-2", name: "Room 2", is_owner: false } as RoomInfo
482
+ ];
483
+
484
+ const message: AuthSuccessMessage = {
485
+ type: "auth_success",
486
+ data: {
487
+ id: "client-123",
488
+ type: "user",
489
+ address: "0xabc...",
490
+ rooms,
491
+ private_room_id: "room-1" // DEPRECATED field
492
+ }
493
+ } as AuthSuccessMessage;
494
+
495
+ await handler.handle(message, mockContext);
496
+
497
+ expect(updateAuthStateSpy).toHaveBeenCalledWith(
498
+ expect.objectContaining({
499
+ rooms: ["room-1", "room-2"], // DEPRECATED: flat list of IDs
500
+ privateRoomId: "room-1" // DEPRECATED: single ID
501
+ })
502
+ );
503
+ });
504
+
505
+ it("should include new v2.0 fields in auth state", async () => {
506
+ const rooms: RoomInfo[] = [
507
+ { id: "room-1", name: "Room 1", is_owner: true } as RoomInfo,
508
+ { id: "room-2", name: "Room 2", is_owner: false } as RoomInfo
509
+ ];
510
+
511
+ const message: AuthSuccessMessage = {
512
+ type: "auth_success",
513
+ data: {
514
+ id: "client-123",
515
+ type: "user",
516
+ address: "0xabc...",
517
+ rooms,
518
+ max_private_rooms: 3
519
+ }
520
+ } as AuthSuccessMessage;
521
+
522
+ await handler.handle(message, mockContext);
523
+
524
+ expect(updateAuthStateSpy).toHaveBeenCalledWith(
525
+ expect.objectContaining({
526
+ roomObjects: rooms, // v2.0: Full objects
527
+ privateRoomIds: ["room-1"], // v2.0: Owned room IDs
528
+ sharedRoomIds: ["room-2"], // v2.0: Shared room IDs
529
+ maxPrivateRooms: 3 // v2.0: Room limit
530
+ })
531
+ );
532
+ });
533
+ });
534
+
535
+ describe("Without Room Management Manager", () => {
536
+ it("should work without roomManagementManager in context", async () => {
537
+ const contextWithoutManager = { ...mockContext, roomManagementManager: undefined };
538
+
539
+ const message: AuthSuccessMessage = {
540
+ type: "auth_success",
541
+ data: {
542
+ id: "client-123",
543
+ type: "user",
544
+ address: "0xabc...",
545
+ rooms: [
546
+ { id: "room-1", name: "Room", is_owner: true } as RoomInfo
547
+ ],
548
+ max_private_rooms: 3
549
+ }
550
+ } as AuthSuccessMessage;
551
+
552
+ // Should not throw
553
+ await handler.handle(message, contextWithoutManager);
554
+
555
+ // Should still update auth state
556
+ expect(updateAuthStateSpy).toHaveBeenCalledWith(
557
+ expect.objectContaining({
558
+ roomObjects: expect.any(Array),
559
+ maxPrivateRooms: 3
560
+ })
561
+ );
562
+
563
+ // Should still emit events
564
+ expect(emitSpy).toHaveBeenCalledWith("auth:success", expect.anything());
565
+ expect(emitSpy).toHaveBeenCalledWith("ready");
566
+ });
567
+
568
+ it("should handle null roomManagementManager", async () => {
569
+ const contextWithNullManager = { ...mockContext, roomManagementManager: null };
570
+
571
+ const message: AuthSuccessMessage = {
572
+ type: "auth_success",
573
+ data: {
574
+ id: "client-123",
575
+ type: "user",
576
+ address: "0xabc...",
577
+ rooms: []
578
+ }
579
+ } as AuthSuccessMessage;
580
+
581
+ // Should not throw
582
+ await handler.handle(message, contextWithNullManager);
583
+
584
+ expect(emitSpy).toHaveBeenCalledWith("ready");
585
+ });
586
+ });
587
+
588
+ describe("Event Emission", () => {
589
+ it("should emit auth:success event with auth state", async () => {
590
+ const message: AuthSuccessMessage = {
591
+ type: "auth_success",
592
+ data: {
593
+ id: "client-123",
594
+ type: "user",
595
+ address: "0xabc..."
596
+ }
597
+ } as AuthSuccessMessage;
598
+
599
+ const mockAuthState = {
600
+ authenticated: true,
601
+ clientId: "client-123",
602
+ walletAddress: "0xabc..."
603
+ };
604
+ getAuthStateSpy.mockReturnValue(mockAuthState);
605
+
606
+ await handler.handle(message, mockContext);
607
+
608
+ expect(emitSpy).toHaveBeenCalledWith("auth:success", mockAuthState);
609
+ });
610
+
611
+ it("should emit ready event after auth:success", async () => {
612
+ const message: AuthSuccessMessage = {
613
+ type: "auth_success",
614
+ data: {
615
+ id: "client-123",
616
+ type: "user",
617
+ address: "0xabc..."
618
+ }
619
+ } as AuthSuccessMessage;
620
+
621
+ await handler.handle(message, mockContext);
622
+
623
+ // Check that ready was emitted
624
+ expect(emitSpy).toHaveBeenCalledWith("ready");
625
+
626
+ // Check order: auth:success before ready
627
+ const calls = emitSpy.mock.calls;
628
+ const authSuccessIndex = calls.findIndex((call) => call[0] === "auth:success");
629
+ const readyIndex = calls.findIndex((call) => call[0] === "ready");
630
+
631
+ expect(authSuccessIndex).toBeGreaterThanOrEqual(0);
632
+ expect(readyIndex).toBeGreaterThan(authSuccessIndex);
633
+ });
634
+
635
+ it("should log authentication success", async () => {
636
+ const message: AuthSuccessMessage = {
637
+ type: "auth_success",
638
+ data: {
639
+ id: "client-123",
640
+ type: "user",
641
+ address: "0xabc..."
642
+ }
643
+ } as AuthSuccessMessage;
644
+
645
+ await handler.handle(message, mockContext);
646
+
647
+ expect(mockLogger.info).toHaveBeenCalledWith("Authentication successful");
648
+ });
649
+ });
650
+
651
+ describe("State Updates", () => {
652
+ it("should update connection state to authenticated", async () => {
653
+ const message: AuthSuccessMessage = {
654
+ type: "auth_success",
655
+ data: {
656
+ id: "client-123",
657
+ type: "user",
658
+ address: "0xabc..."
659
+ }
660
+ } as AuthSuccessMessage;
661
+
662
+ await handler.handle(message, mockContext);
663
+
664
+ expect(updateConnectionStateSpy).toHaveBeenCalledWith(
665
+ expect.objectContaining({
666
+ authenticated: true
667
+ })
668
+ );
669
+ });
670
+
671
+ it("should update auth state with all user data", async () => {
672
+ const message: AuthSuccessMessage = {
673
+ type: "auth_success",
674
+ data: {
675
+ id: "client-123",
676
+ type: "user",
677
+ address: "0xabc...",
678
+ is_whitelisted: true,
679
+ is_admin_whitelisted: false,
680
+ nft_verified: true,
681
+ rooms: []
682
+ }
683
+ } as AuthSuccessMessage;
684
+
685
+ await handler.handle(message, mockContext);
686
+
687
+ expect(updateAuthStateSpy).toHaveBeenCalledWith(
688
+ expect.objectContaining({
689
+ authenticated: true,
690
+ clientId: "client-123",
691
+ walletAddress: "0xabc...",
692
+ isWhitelisted: true,
693
+ isAdmin: false,
694
+ nftVerified: true
695
+ })
696
+ );
697
+ });
698
+ });
699
+ });