@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.
- package/.github/workflows/publish-npm.yml +8 -6
- package/CHANGELOG.md +265 -0
- package/README.md +406 -53
- package/dist/core/websocket-client.d.ts +12 -0
- package/dist/core/websocket-client.d.ts.map +1 -1
- package/dist/core/websocket-client.js +22 -2
- package/dist/core/websocket-client.js.map +1 -1
- package/dist/handlers/message-handlers/agent-room-operation-response-handler.d.ts +76 -0
- package/dist/handlers/message-handlers/agent-room-operation-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agent-room-operation-response-handler.js +70 -0
- package/dist/handlers/message-handlers/agent-room-operation-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts +92 -38
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/agent-status-update-handler.d.ts +904 -0
- package/dist/handlers/message-handlers/agent-status-update-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agent-status-update-handler.js +51 -0
- package/dist/handlers/message-handlers/agent-status-update-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-error-handler.d.ts +45 -31
- package/dist/handlers/message-handlers/auth-error-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/auth-message-handler.d.ts +6 -0
- package/dist/handlers/message-handlers/auth-message-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/auth-message-handler.js +65 -5
- package/dist/handlers/message-handlers/auth-message-handler.js.map +1 -1
- package/dist/handlers/message-handlers/auth-required-handler.d.ts +49 -31
- package/dist/handlers/message-handlers/auth-required-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/auth-success-handler.d.ts +6 -0
- package/dist/handlers/message-handlers/auth-success-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/auth-success-handler.js +46 -4
- package/dist/handlers/message-handlers/auth-success-handler.js.map +1 -1
- package/dist/handlers/message-handlers/challenge-handler.d.ts +45 -31
- package/dist/handlers/message-handlers/challenge-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/error-message-handler.d.ts +49 -31
- package/dist/handlers/message-handlers/error-message-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/index.d.ts +5 -0
- package/dist/handlers/message-handlers/index.d.ts.map +1 -1
- package/dist/handlers/message-handlers/index.js +23 -1
- package/dist/handlers/message-handlers/index.js.map +1 -1
- package/dist/handlers/message-handlers/list-available-agents-handler.d.ts +877 -0
- package/dist/handlers/message-handlers/list-available-agents-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/list-available-agents-handler.js +38 -0
- package/dist/handlers/message-handlers/list-available-agents-handler.js.map +1 -0
- package/dist/handlers/message-handlers/list-room-agents-handler.d.ts +886 -0
- package/dist/handlers/message-handlers/list-room-agents-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/list-room-agents-handler.js +51 -0
- package/dist/handlers/message-handlers/list-room-agents-handler.js.map +1 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts +178 -89
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts +62 -58
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/regular-message-handler.d.ts +31 -29
- package/dist/handlers/message-handlers/regular-message-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/room-operation-response-handler.d.ts +328 -0
- package/dist/handlers/message-handlers/room-operation-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/room-operation-response-handler.js +92 -0
- package/dist/handlers/message-handlers/room-operation-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts +53 -31
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts.map +1 -1
- package/dist/handlers/message-handlers/types.d.ts +2 -0
- package/dist/handlers/message-handlers/types.d.ts.map +1 -1
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts +53 -31
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts.map +1 -1
- package/dist/managers/agent-room-manager.d.ts +222 -0
- package/dist/managers/agent-room-manager.d.ts.map +1 -0
- package/dist/managers/agent-room-manager.js +508 -0
- package/dist/managers/agent-room-manager.js.map +1 -0
- package/dist/managers/index.d.ts +2 -0
- package/dist/managers/index.d.ts.map +1 -1
- package/dist/managers/index.js +5 -1
- package/dist/managers/index.js.map +1 -1
- package/dist/managers/room-management-manager.d.ts +213 -0
- package/dist/managers/room-management-manager.d.ts.map +1 -0
- package/dist/managers/room-management-manager.js +440 -0
- package/dist/managers/room-management-manager.js.map +1 -0
- package/dist/managers/room-manager.d.ts +4 -4
- package/dist/managers/room-manager.d.ts.map +1 -1
- package/dist/managers/room-manager.js.map +1 -1
- package/dist/teneo-sdk.d.ts +333 -13
- package/dist/teneo-sdk.d.ts.map +1 -1
- package/dist/teneo-sdk.js +468 -1
- package/dist/teneo-sdk.js.map +1 -1
- package/dist/types/config.d.ts +63 -54
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +8 -4
- package/dist/types/config.js.map +1 -1
- package/dist/types/error-codes.d.ts +2 -0
- package/dist/types/error-codes.d.ts.map +1 -1
- package/dist/types/error-codes.js +3 -0
- package/dist/types/error-codes.js.map +1 -1
- package/dist/types/events.d.ts +132 -68
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/events.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +27 -2
- package/dist/types/index.js.map +1 -1
- package/dist/types/messages.d.ts +11396 -2559
- package/dist/types/messages.d.ts.map +1 -1
- package/dist/types/messages.js +294 -27
- package/dist/types/messages.js.map +1 -1
- package/examples/.env.example +1 -1
- package/examples/agent-room-management-example.ts +334 -0
- package/examples/claude-agent-x-follower/.env.example +2 -2
- package/examples/claude-agent-x-follower/QUICKSTART.md +1 -1
- package/examples/claude-agent-x-follower/README.md +1 -1
- package/examples/n8n-teneo/.env.example +2 -2
- package/examples/n8n-teneo/README.md +1 -1
- package/examples/openai-teneo/.env.example +2 -2
- package/examples/openai-teneo/README.md +1 -1
- package/examples/production-dashboard/.env.example +2 -2
- package/examples/production-dashboard/README.md +89 -12
- package/examples/production-dashboard/public/dashboard.html +1173 -601
- package/examples/production-dashboard/server.ts +347 -5
- package/examples/room-management-example.ts +285 -0
- package/examples/usage/.env.example +1 -1
- package/examples/usage/01-connect.ts +1 -1
- package/examples/usage/02-list-agents.ts +1 -1
- package/examples/usage/03-pick-agent.ts +1 -1
- package/examples/usage/04-find-by-capability.ts +1 -1
- package/examples/usage/05-webhook-example.ts +1 -1
- package/examples/usage/06-simple-api-server.ts +1 -1
- package/examples/usage/07-event-listener.ts +1 -1
- package/examples/usage/README.md +1 -1
- package/package.json +9 -1
- package/src/core/websocket-client.ts +26 -2
- package/src/handlers/message-handlers/agent-room-operation-response-handler.ts +83 -0
- package/src/handlers/message-handlers/agent-status-update-handler.ts +58 -0
- package/src/handlers/message-handlers/auth-message-handler.ts +73 -5
- package/src/handlers/message-handlers/auth-success-handler.ts +58 -6
- package/src/handlers/message-handlers/index.ts +19 -0
- package/src/handlers/message-handlers/list-available-agents-handler.ts +41 -0
- package/src/handlers/message-handlers/list-room-agents-handler.ts +61 -0
- package/src/handlers/message-handlers/room-operation-response-handler.ts +105 -0
- package/src/handlers/message-handlers/types.ts +6 -0
- package/src/managers/agent-room-manager.ts +609 -0
- package/src/managers/index.ts +2 -0
- package/src/managers/room-management-manager.ts +523 -0
- package/src/managers/room-manager.ts +4 -5
- package/src/teneo-sdk.ts +505 -4
- package/src/types/config.ts +10 -5
- package/src/types/error-codes.ts +4 -0
- package/src/types/events.ts +24 -0
- package/src/types/index.ts +55 -0
- package/src/types/messages.ts +374 -41
- package/tests/integration/room-management.test.ts +514 -0
- package/tests/integration/websocket.test.ts +1 -1
- package/tests/unit/handlers/agent-room-operation-response-handler.test.ts +394 -0
- package/tests/unit/handlers/agent-status-update-handler.test.ts +407 -0
- package/tests/unit/handlers/auth-success-handler-rooms.test.ts +699 -0
- package/tests/unit/handlers/list-available-agents-handler.test.ts +256 -0
- package/tests/unit/handlers/list-room-agents-handler.test.ts +294 -0
- package/tests/unit/handlers/room-operation-response-handler.test.ts +527 -0
- package/tests/unit/managers/agent-room-manager.test.ts +534 -0
- package/tests/unit/managers/room-management-manager.test.ts +438 -0
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for RoomOperationResponseHandler
|
|
3
|
+
* Tests response handling for room CRUD operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
7
|
+
import { RoomOperationResponseHandler } from "../../../src/handlers/message-handlers/room-operation-response-handler";
|
|
8
|
+
import { HandlerContext } from "../../../src/handlers/message-handlers/types";
|
|
9
|
+
import { RoomInfo, Logger } from "../../../src/types";
|
|
10
|
+
import { SDKError } from "../../../src/types/events";
|
|
11
|
+
import { ErrorCode } from "../../../src/types/error-codes";
|
|
12
|
+
|
|
13
|
+
describe("RoomOperationResponseHandler", () => {
|
|
14
|
+
let handler: RoomOperationResponseHandler;
|
|
15
|
+
let mockContext: HandlerContext;
|
|
16
|
+
let mockLogger: Logger;
|
|
17
|
+
let mockRoomManagementManager: any;
|
|
18
|
+
let emitSpy: ReturnType<typeof vi.fn>;
|
|
19
|
+
let sendWebhookSpy: 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
|
+
upsertRoom: vi.fn(),
|
|
33
|
+
removeRoom: vi.fn()
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Create spies
|
|
37
|
+
emitSpy = vi.fn();
|
|
38
|
+
sendWebhookSpy = vi.fn().mockResolvedValue(undefined);
|
|
39
|
+
|
|
40
|
+
// Create mock context
|
|
41
|
+
mockContext = {
|
|
42
|
+
emit: emitSpy,
|
|
43
|
+
sendWebhook: sendWebhookSpy,
|
|
44
|
+
logger: mockLogger,
|
|
45
|
+
getConnectionState: vi.fn(),
|
|
46
|
+
getAuthState: vi.fn(),
|
|
47
|
+
updateConnectionState: vi.fn(),
|
|
48
|
+
updateAuthState: vi.fn(),
|
|
49
|
+
sendMessage: vi.fn(),
|
|
50
|
+
roomManagementManager: mockRoomManagementManager
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Create handler instance
|
|
54
|
+
handler = new RoomOperationResponseHandler();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe("Handler Metadata", () => {
|
|
58
|
+
it("should have correct type", () => {
|
|
59
|
+
expect(handler.type).toBe("room_operation_response");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should have schema defined", () => {
|
|
63
|
+
expect(handler.schema).toBeDefined();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should identify messages it can handle", () => {
|
|
67
|
+
const message = { type: "room_operation_response", data: {} };
|
|
68
|
+
expect(handler.canHandle(message as any)).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("should not handle other message types", () => {
|
|
72
|
+
const message = { type: "other_type", data: {} };
|
|
73
|
+
expect(handler.canHandle(message as any)).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("Success Responses - Create/Update (with room object)", () => {
|
|
78
|
+
it("should handle successful create/update with room object", async () => {
|
|
79
|
+
const room: RoomInfo = {
|
|
80
|
+
id: "room-123",
|
|
81
|
+
name: "Test Room",
|
|
82
|
+
description: "Test Description",
|
|
83
|
+
is_public: false,
|
|
84
|
+
created_by: "user-123",
|
|
85
|
+
created_at: new Date().toISOString(),
|
|
86
|
+
updated_at: new Date().toISOString(),
|
|
87
|
+
is_owner: true
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const message = {
|
|
91
|
+
type: "room_operation_response" as const,
|
|
92
|
+
data: {
|
|
93
|
+
success: true,
|
|
94
|
+
room
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
await handler.handle(message, mockContext);
|
|
99
|
+
|
|
100
|
+
// Should update cache
|
|
101
|
+
expect(mockRoomManagementManager.upsertRoom).toHaveBeenCalledWith(room);
|
|
102
|
+
|
|
103
|
+
// Should emit both created and updated events
|
|
104
|
+
expect(emitSpy).toHaveBeenCalledWith("room:created", room);
|
|
105
|
+
expect(emitSpy).toHaveBeenCalledWith("room:updated", room);
|
|
106
|
+
|
|
107
|
+
// Should send webhook
|
|
108
|
+
expect(sendWebhookSpy).toHaveBeenCalledWith(
|
|
109
|
+
"room_operation",
|
|
110
|
+
expect.objectContaining({
|
|
111
|
+
success: true,
|
|
112
|
+
room
|
|
113
|
+
}),
|
|
114
|
+
undefined
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
// Should log
|
|
118
|
+
expect(mockLogger.info).toHaveBeenCalledWith(
|
|
119
|
+
"Room operation succeeded",
|
|
120
|
+
expect.objectContaining({
|
|
121
|
+
roomId: room.id,
|
|
122
|
+
roomName: room.name
|
|
123
|
+
})
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should handle success with minimal room data", async () => {
|
|
128
|
+
const room: RoomInfo = {
|
|
129
|
+
id: "room-456",
|
|
130
|
+
name: "Minimal Room",
|
|
131
|
+
is_owner: false
|
|
132
|
+
} as RoomInfo;
|
|
133
|
+
|
|
134
|
+
const message = {
|
|
135
|
+
type: "room_operation_response" as const,
|
|
136
|
+
data: {
|
|
137
|
+
success: true,
|
|
138
|
+
room
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
await handler.handle(message, mockContext);
|
|
143
|
+
|
|
144
|
+
expect(mockRoomManagementManager.upsertRoom).toHaveBeenCalledWith(room);
|
|
145
|
+
expect(emitSpy).toHaveBeenCalledWith("room:created", room);
|
|
146
|
+
expect(emitSpy).toHaveBeenCalledWith("room:updated", room);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("should work without roomManagementManager in context", async () => {
|
|
150
|
+
const contextWithoutManager = { ...mockContext, roomManagementManager: undefined };
|
|
151
|
+
const room: RoomInfo = {
|
|
152
|
+
id: "room-789",
|
|
153
|
+
name: "Test Room",
|
|
154
|
+
is_owner: true
|
|
155
|
+
} as RoomInfo;
|
|
156
|
+
|
|
157
|
+
const message = {
|
|
158
|
+
type: "room_operation_response" as const,
|
|
159
|
+
data: {
|
|
160
|
+
success: true,
|
|
161
|
+
room
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
await handler.handle(message, contextWithoutManager);
|
|
166
|
+
|
|
167
|
+
// Should still emit events
|
|
168
|
+
expect(emitSpy).toHaveBeenCalledWith("room:created", room);
|
|
169
|
+
expect(emitSpy).toHaveBeenCalledWith("room:updated", room);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe("Success Responses - Delete (with room_id only)", () => {
|
|
174
|
+
it("should handle successful delete with room_id", async () => {
|
|
175
|
+
const roomId = "room-123";
|
|
176
|
+
|
|
177
|
+
const message = {
|
|
178
|
+
type: "room_operation_response" as const,
|
|
179
|
+
data: {
|
|
180
|
+
success: true,
|
|
181
|
+
room_id: roomId
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
await handler.handle(message, mockContext);
|
|
186
|
+
|
|
187
|
+
// Should remove from cache
|
|
188
|
+
expect(mockRoomManagementManager.removeRoom).toHaveBeenCalledWith(roomId);
|
|
189
|
+
|
|
190
|
+
// Should emit deleted event
|
|
191
|
+
expect(emitSpy).toHaveBeenCalledWith("room:deleted", roomId);
|
|
192
|
+
|
|
193
|
+
// Should send webhook
|
|
194
|
+
expect(sendWebhookSpy).toHaveBeenCalledWith(
|
|
195
|
+
"room_deleted",
|
|
196
|
+
expect.objectContaining({
|
|
197
|
+
success: true,
|
|
198
|
+
room_id: roomId
|
|
199
|
+
}),
|
|
200
|
+
undefined
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// Should log
|
|
204
|
+
expect(mockLogger.info).toHaveBeenCalledWith("Room deleted", { roomId });
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("should work without roomManagementManager for delete", async () => {
|
|
208
|
+
const contextWithoutManager = { ...mockContext, roomManagementManager: undefined };
|
|
209
|
+
const roomId = "room-456";
|
|
210
|
+
|
|
211
|
+
const message = {
|
|
212
|
+
type: "room_operation_response" as const,
|
|
213
|
+
data: {
|
|
214
|
+
success: true,
|
|
215
|
+
room_id: roomId
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
await handler.handle(message, contextWithoutManager);
|
|
220
|
+
|
|
221
|
+
// Should still emit event
|
|
222
|
+
expect(emitSpy).toHaveBeenCalledWith("room:deleted", roomId);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
describe("Error Responses", () => {
|
|
227
|
+
it("should handle error response with message", async () => {
|
|
228
|
+
const errorMessage = "Room name already exists";
|
|
229
|
+
const roomId = "room-123";
|
|
230
|
+
|
|
231
|
+
const message = {
|
|
232
|
+
type: "room_operation_response" as const,
|
|
233
|
+
data: {
|
|
234
|
+
success: false,
|
|
235
|
+
message: errorMessage,
|
|
236
|
+
room_id: roomId
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
await handler.handle(message, mockContext);
|
|
241
|
+
|
|
242
|
+
// Should emit all error events (since we can't determine operation type)
|
|
243
|
+
expect(emitSpy).toHaveBeenCalledWith(
|
|
244
|
+
"room:create_error",
|
|
245
|
+
expect.any(SDKError)
|
|
246
|
+
);
|
|
247
|
+
expect(emitSpy).toHaveBeenCalledWith(
|
|
248
|
+
"room:update_error",
|
|
249
|
+
expect.any(SDKError),
|
|
250
|
+
roomId
|
|
251
|
+
);
|
|
252
|
+
expect(emitSpy).toHaveBeenCalledWith(
|
|
253
|
+
"room:delete_error",
|
|
254
|
+
expect.any(SDKError),
|
|
255
|
+
roomId
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
// Verify error details
|
|
259
|
+
const createErrorCall = emitSpy.mock.calls.find(
|
|
260
|
+
(call) => call[0] === "room:create_error"
|
|
261
|
+
);
|
|
262
|
+
const error = createErrorCall[1] as SDKError;
|
|
263
|
+
expect(error.message).toBe(errorMessage);
|
|
264
|
+
expect(error.code).toBe(ErrorCode.OPERATION_FAILED);
|
|
265
|
+
|
|
266
|
+
// Should send webhook
|
|
267
|
+
expect(sendWebhookSpy).toHaveBeenCalledWith(
|
|
268
|
+
"room_operation_error",
|
|
269
|
+
expect.objectContaining({
|
|
270
|
+
success: false,
|
|
271
|
+
message: errorMessage,
|
|
272
|
+
room_id: roomId
|
|
273
|
+
}),
|
|
274
|
+
undefined
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
// Should not update cache
|
|
278
|
+
expect(mockRoomManagementManager.upsertRoom).not.toHaveBeenCalled();
|
|
279
|
+
expect(mockRoomManagementManager.removeRoom).not.toHaveBeenCalled();
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it("should handle error without message", async () => {
|
|
283
|
+
const message = {
|
|
284
|
+
type: "room_operation_response" as const,
|
|
285
|
+
data: {
|
|
286
|
+
success: false
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
await handler.handle(message, mockContext);
|
|
291
|
+
|
|
292
|
+
// Should use default error message
|
|
293
|
+
const createErrorCall = emitSpy.mock.calls.find(
|
|
294
|
+
(call) => call[0] === "room:create_error"
|
|
295
|
+
);
|
|
296
|
+
const error = createErrorCall[1] as SDKError;
|
|
297
|
+
expect(error.message).toBe("Room operation failed");
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it("should handle error without room_id", async () => {
|
|
301
|
+
const message = {
|
|
302
|
+
type: "room_operation_response" as const,
|
|
303
|
+
data: {
|
|
304
|
+
success: false,
|
|
305
|
+
message: "Generic error"
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
await handler.handle(message, mockContext);
|
|
310
|
+
|
|
311
|
+
// Should still emit error events
|
|
312
|
+
expect(emitSpy).toHaveBeenCalledWith("room:create_error", expect.any(SDKError));
|
|
313
|
+
expect(emitSpy).toHaveBeenCalledWith(
|
|
314
|
+
"room:update_error",
|
|
315
|
+
expect.any(SDKError),
|
|
316
|
+
undefined
|
|
317
|
+
);
|
|
318
|
+
expect(emitSpy).toHaveBeenCalledWith(
|
|
319
|
+
"room:delete_error",
|
|
320
|
+
expect.any(SDKError),
|
|
321
|
+
undefined
|
|
322
|
+
);
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
describe("Edge Cases", () => {
|
|
327
|
+
it("should handle success without room or room_id", async () => {
|
|
328
|
+
const message = {
|
|
329
|
+
type: "room_operation_response" as const,
|
|
330
|
+
data: {
|
|
331
|
+
success: true
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
await handler.handle(message, mockContext);
|
|
336
|
+
|
|
337
|
+
// Should log warning
|
|
338
|
+
expect(mockLogger.warn).toHaveBeenCalledWith(
|
|
339
|
+
"Room operation succeeded but no room data provided"
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
// Should not emit any success events
|
|
343
|
+
expect(emitSpy).not.toHaveBeenCalledWith("room:created", expect.anything());
|
|
344
|
+
expect(emitSpy).not.toHaveBeenCalledWith("room:updated", expect.anything());
|
|
345
|
+
expect(emitSpy).not.toHaveBeenCalledWith("room:deleted", expect.anything());
|
|
346
|
+
|
|
347
|
+
// Should not update cache
|
|
348
|
+
expect(mockRoomManagementManager.upsertRoom).not.toHaveBeenCalled();
|
|
349
|
+
expect(mockRoomManagementManager.removeRoom).not.toHaveBeenCalled();
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it("should handle roomManagementManager without upsertRoom method", async () => {
|
|
353
|
+
const invalidManager = { someOtherMethod: vi.fn() };
|
|
354
|
+
const contextWithInvalidManager = {
|
|
355
|
+
...mockContext,
|
|
356
|
+
roomManagementManager: invalidManager
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
const room: RoomInfo = {
|
|
360
|
+
id: "room-123",
|
|
361
|
+
name: "Test Room",
|
|
362
|
+
is_owner: true
|
|
363
|
+
} as RoomInfo;
|
|
364
|
+
|
|
365
|
+
const message = {
|
|
366
|
+
type: "room_operation_response" as const,
|
|
367
|
+
data: {
|
|
368
|
+
success: true,
|
|
369
|
+
room
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
// Should not throw
|
|
374
|
+
await handler.handle(message, contextWithInvalidManager);
|
|
375
|
+
|
|
376
|
+
// Should still emit events
|
|
377
|
+
expect(emitSpy).toHaveBeenCalledWith("room:created", room);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it("should handle roomManagementManager without removeRoom method", async () => {
|
|
381
|
+
const invalidManager = { someOtherMethod: vi.fn() };
|
|
382
|
+
const contextWithInvalidManager = {
|
|
383
|
+
...mockContext,
|
|
384
|
+
roomManagementManager: invalidManager
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
const message = {
|
|
388
|
+
type: "room_operation_response" as const,
|
|
389
|
+
data: {
|
|
390
|
+
success: true,
|
|
391
|
+
room_id: "room-123"
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
// Should not throw
|
|
396
|
+
await handler.handle(message, contextWithInvalidManager);
|
|
397
|
+
|
|
398
|
+
// Should still emit event
|
|
399
|
+
expect(emitSpy).toHaveBeenCalledWith("room:deleted", "room-123");
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
describe("Webhook Errors", () => {
|
|
404
|
+
it("should handle webhook failures gracefully", async () => {
|
|
405
|
+
const webhookError = new Error("Webhook failed");
|
|
406
|
+
sendWebhookSpy.mockRejectedValueOnce(webhookError);
|
|
407
|
+
|
|
408
|
+
const room: RoomInfo = {
|
|
409
|
+
id: "room-123",
|
|
410
|
+
name: "Test Room",
|
|
411
|
+
is_owner: true
|
|
412
|
+
} as RoomInfo;
|
|
413
|
+
|
|
414
|
+
const message = {
|
|
415
|
+
type: "room_operation_response" as const,
|
|
416
|
+
data: {
|
|
417
|
+
success: true,
|
|
418
|
+
room
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
// Should not throw (webhook errors are logged but don't fail the handler)
|
|
423
|
+
await handler.handle(message, mockContext);
|
|
424
|
+
|
|
425
|
+
// Should still emit events and update cache
|
|
426
|
+
expect(emitSpy).toHaveBeenCalledWith("room:created", room);
|
|
427
|
+
expect(mockRoomManagementManager.upsertRoom).toHaveBeenCalledWith(room);
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
describe("Message Validation", () => {
|
|
432
|
+
it("should handle invalid message structure", async () => {
|
|
433
|
+
const invalidMessage = {
|
|
434
|
+
type: "room_operation_response",
|
|
435
|
+
// Missing data field
|
|
436
|
+
} as any;
|
|
437
|
+
|
|
438
|
+
await handler.handle(invalidMessage, mockContext);
|
|
439
|
+
|
|
440
|
+
// Should log error
|
|
441
|
+
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
442
|
+
expect.stringContaining("Error handling room_operation_response"),
|
|
443
|
+
expect.any(Error)
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
// Should emit message:error event
|
|
447
|
+
expect(emitSpy).toHaveBeenCalledWith(
|
|
448
|
+
"message:error",
|
|
449
|
+
expect.any(Error),
|
|
450
|
+
invalidMessage
|
|
451
|
+
);
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it("should accept valid message with extra fields", async () => {
|
|
455
|
+
const room: RoomInfo = {
|
|
456
|
+
id: "room-123",
|
|
457
|
+
name: "Test Room",
|
|
458
|
+
is_owner: true
|
|
459
|
+
} as RoomInfo;
|
|
460
|
+
|
|
461
|
+
const message = {
|
|
462
|
+
type: "room_operation_response" as const,
|
|
463
|
+
data: {
|
|
464
|
+
success: true,
|
|
465
|
+
room,
|
|
466
|
+
extra_field: "should be ignored", // Schema uses .passthrough()
|
|
467
|
+
another_extra: 123
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
// Should not throw
|
|
472
|
+
await handler.handle(message, mockContext);
|
|
473
|
+
|
|
474
|
+
expect(emitSpy).toHaveBeenCalledWith("room:created", room);
|
|
475
|
+
});
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
describe("Debug Logging", () => {
|
|
479
|
+
it("should log debug info for successful operation", async () => {
|
|
480
|
+
const room: RoomInfo = {
|
|
481
|
+
id: "room-123",
|
|
482
|
+
name: "Test Room",
|
|
483
|
+
is_owner: true
|
|
484
|
+
} as RoomInfo;
|
|
485
|
+
|
|
486
|
+
const message = {
|
|
487
|
+
type: "room_operation_response" as const,
|
|
488
|
+
data: {
|
|
489
|
+
success: true,
|
|
490
|
+
room
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
await handler.handle(message, mockContext);
|
|
495
|
+
|
|
496
|
+
expect(mockLogger.debug).toHaveBeenCalledWith(
|
|
497
|
+
"Handling room_operation_response",
|
|
498
|
+
expect.objectContaining({
|
|
499
|
+
success: true,
|
|
500
|
+
roomId: undefined, // room_id is undefined in this case
|
|
501
|
+
hasRoom: true
|
|
502
|
+
})
|
|
503
|
+
);
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
it("should log debug info for delete operation", async () => {
|
|
507
|
+
const message = {
|
|
508
|
+
type: "room_operation_response" as const,
|
|
509
|
+
data: {
|
|
510
|
+
success: true,
|
|
511
|
+
room_id: "room-456"
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
await handler.handle(message, mockContext);
|
|
516
|
+
|
|
517
|
+
expect(mockLogger.debug).toHaveBeenCalledWith(
|
|
518
|
+
"Handling room_operation_response",
|
|
519
|
+
expect.objectContaining({
|
|
520
|
+
success: true,
|
|
521
|
+
roomId: "room-456",
|
|
522
|
+
hasRoom: false
|
|
523
|
+
})
|
|
524
|
+
);
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
});
|