@optimizely-opal/opal-tool-ocp-sdk 0.0.0-dev.1
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/README.md +437 -0
- package/dist/decorator/Decorator.d.ts +46 -0
- package/dist/decorator/Decorator.d.ts.map +1 -0
- package/dist/decorator/Decorator.js +31 -0
- package/dist/decorator/Decorator.js.map +1 -0
- package/dist/decorator/Decorator.test.d.ts +2 -0
- package/dist/decorator/Decorator.test.d.ts.map +1 -0
- package/dist/decorator/Decorator.test.js +418 -0
- package/dist/decorator/Decorator.test.js.map +1 -0
- package/dist/function/ToolFunction.d.ts +15 -0
- package/dist/function/ToolFunction.d.ts.map +1 -0
- package/dist/function/ToolFunction.js +25 -0
- package/dist/function/ToolFunction.js.map +1 -0
- package/dist/function/ToolFunction.test.d.ts +2 -0
- package/dist/function/ToolFunction.test.d.ts.map +1 -0
- package/dist/function/ToolFunction.test.js +189 -0
- package/dist/function/ToolFunction.test.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/service/Service.d.ts +78 -0
- package/dist/service/Service.d.ts.map +1 -0
- package/dist/service/Service.js +204 -0
- package/dist/service/Service.js.map +1 -0
- package/dist/service/Service.test.d.ts +2 -0
- package/dist/service/Service.test.d.ts.map +1 -0
- package/dist/service/Service.test.js +341 -0
- package/dist/service/Service.test.js.map +1 -0
- package/dist/types/Models.d.ts +126 -0
- package/dist/types/Models.d.ts.map +1 -0
- package/dist/types/Models.js +181 -0
- package/dist/types/Models.js.map +1 -0
- package/package.json +58 -0
- package/src/decorator/Decorator.test.ts +523 -0
- package/src/decorator/Decorator.ts +83 -0
- package/src/function/ToolFunction.test.ts +224 -0
- package/src/function/ToolFunction.ts +25 -0
- package/src/index.ts +4 -0
- package/src/service/Service.test.ts +550 -0
- package/src/service/Service.ts +182 -0
- package/src/types/Models.ts +163 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
import { toolsService, Tool, Interaction } from './Service';
|
|
2
|
+
import { Parameter, ParameterType, AuthRequirement, OptiIdAuthDataCredentials, OptiIdAuthData } from '../types/Models';
|
|
3
|
+
import { logger } from '@zaiusinc/app-sdk';
|
|
4
|
+
|
|
5
|
+
// Mock the logger
|
|
6
|
+
jest.mock('@zaiusinc/app-sdk', () => ({
|
|
7
|
+
logger: {
|
|
8
|
+
error: jest.fn()
|
|
9
|
+
},
|
|
10
|
+
Response: jest.fn().mockImplementation((status, data) => ({
|
|
11
|
+
status,
|
|
12
|
+
data,
|
|
13
|
+
bodyJSON: data,
|
|
14
|
+
bodyAsU8Array: new Uint8Array()
|
|
15
|
+
}))
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
describe('ToolsService', () => {
|
|
19
|
+
let mockTool: Tool<unknown>;
|
|
20
|
+
let mockInteraction: Interaction<unknown>;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
// Clear registered functions and interactions before each test
|
|
24
|
+
(toolsService as any).functions = new Map();
|
|
25
|
+
(toolsService as any).interactions = new Map();
|
|
26
|
+
|
|
27
|
+
// Reset all mocks
|
|
28
|
+
jest.clearAllMocks();
|
|
29
|
+
|
|
30
|
+
// Create mock tool handler
|
|
31
|
+
const mockToolHandler = jest.fn().mockResolvedValue({ result: 'success' });
|
|
32
|
+
|
|
33
|
+
// Create mock interaction handler
|
|
34
|
+
const mockInteractionHandler = jest.fn().mockResolvedValue({ message: 'interaction success' });
|
|
35
|
+
|
|
36
|
+
// Create mock tool
|
|
37
|
+
mockTool = new Tool(
|
|
38
|
+
'testTool',
|
|
39
|
+
'Test tool description',
|
|
40
|
+
[new Parameter('param1', ParameterType.String, 'Test parameter', true)],
|
|
41
|
+
'/test-tool',
|
|
42
|
+
mockToolHandler
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// Create mock interaction
|
|
46
|
+
mockInteraction = new Interaction(
|
|
47
|
+
'testInteraction',
|
|
48
|
+
'/test-interaction',
|
|
49
|
+
mockInteractionHandler
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const createMockRequest = (overrides: any = {}): any => {
|
|
54
|
+
// Create a mock headers object with get method
|
|
55
|
+
const createHeadersMap = (headersObj: any = {}) => {
|
|
56
|
+
const map = new Map();
|
|
57
|
+
Object.entries(headersObj).forEach(([key, value]) => {
|
|
58
|
+
map.set(key, value);
|
|
59
|
+
});
|
|
60
|
+
return map;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const baseRequest = {
|
|
64
|
+
path: '/test-tool',
|
|
65
|
+
method: 'POST',
|
|
66
|
+
bodyJSON: { parameters: { param1: 'test-value' } },
|
|
67
|
+
body: JSON.stringify({ parameters: { param1: 'test-value' } }),
|
|
68
|
+
bodyData: Buffer.from(''),
|
|
69
|
+
headers: createHeadersMap(),
|
|
70
|
+
params: {},
|
|
71
|
+
contentType: 'application/json'
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
...baseRequest,
|
|
76
|
+
...overrides,
|
|
77
|
+
headers: createHeadersMap(overrides.headers)
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
describe('processRequest', () => {
|
|
82
|
+
describe('discovery endpoint', () => {
|
|
83
|
+
it('should return registered functions for discovery endpoint', async () => {
|
|
84
|
+
toolsService.registerTool(
|
|
85
|
+
mockTool.name,
|
|
86
|
+
mockTool.description,
|
|
87
|
+
mockTool.handler,
|
|
88
|
+
mockTool.parameters,
|
|
89
|
+
mockTool.endpoint
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const discoveryRequest = createMockRequest({ path: '/discovery' });
|
|
93
|
+
const response = await toolsService.processRequest(discoveryRequest);
|
|
94
|
+
|
|
95
|
+
expect(response.status).toBe(200);
|
|
96
|
+
expect(response).toHaveProperty('bodyJSON');
|
|
97
|
+
expect(response.bodyAsU8Array).toBeDefined();
|
|
98
|
+
|
|
99
|
+
// Test the actual response structure
|
|
100
|
+
const responseData = response.bodyJSON as { functions: unknown[] };
|
|
101
|
+
expect(responseData).toHaveProperty('functions');
|
|
102
|
+
expect(Array.isArray(responseData.functions)).toBe(true);
|
|
103
|
+
expect(responseData.functions).toHaveLength(1);
|
|
104
|
+
|
|
105
|
+
// Test the registered function structure
|
|
106
|
+
const registeredFunction = responseData.functions[0];
|
|
107
|
+
expect(registeredFunction).toEqual({
|
|
108
|
+
name: mockTool.name,
|
|
109
|
+
description: mockTool.description,
|
|
110
|
+
parameters: mockTool.parameters.map((p: Parameter) => p.toJSON()),
|
|
111
|
+
endpoint: mockTool.endpoint,
|
|
112
|
+
http_method: 'POST'
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should return empty functions array when no tools are registered', async () => {
|
|
117
|
+
const discoveryRequest = createMockRequest({ path: '/discovery' });
|
|
118
|
+
const response = await toolsService.processRequest(discoveryRequest);
|
|
119
|
+
|
|
120
|
+
expect(response.status).toBe(200);
|
|
121
|
+
expect(response.bodyAsU8Array).toBeDefined();
|
|
122
|
+
|
|
123
|
+
// Test the actual response structure
|
|
124
|
+
const responseData = response.bodyJSON as { functions: unknown[] };
|
|
125
|
+
expect(responseData).toHaveProperty('functions');
|
|
126
|
+
expect(Array.isArray(responseData.functions)).toBe(true);
|
|
127
|
+
expect(responseData.functions).toHaveLength(0);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should return multiple registered functions in discovery endpoint', async () => {
|
|
131
|
+
// Register first tool
|
|
132
|
+
toolsService.registerTool(
|
|
133
|
+
mockTool.name,
|
|
134
|
+
mockTool.description,
|
|
135
|
+
mockTool.handler,
|
|
136
|
+
mockTool.parameters,
|
|
137
|
+
mockTool.endpoint
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
// Register second tool with auth requirements
|
|
141
|
+
const authRequirements = [new AuthRequirement('oauth2', 'calendar', true)];
|
|
142
|
+
toolsService.registerTool(
|
|
143
|
+
'second_tool',
|
|
144
|
+
'Second test tool',
|
|
145
|
+
jest.fn(),
|
|
146
|
+
[],
|
|
147
|
+
'/second-tool',
|
|
148
|
+
authRequirements
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const discoveryRequest = createMockRequest({ path: '/discovery' });
|
|
152
|
+
const response = await toolsService.processRequest(discoveryRequest);
|
|
153
|
+
|
|
154
|
+
expect(response.status).toBe(200);
|
|
155
|
+
|
|
156
|
+
// Test the actual response structure
|
|
157
|
+
const responseData = response.bodyJSON as { functions: unknown[] };
|
|
158
|
+
expect(responseData).toHaveProperty('functions');
|
|
159
|
+
expect(Array.isArray(responseData.functions)).toBe(true);
|
|
160
|
+
expect(responseData.functions).toHaveLength(2);
|
|
161
|
+
|
|
162
|
+
// Find and test both functions
|
|
163
|
+
const firstFunction = responseData.functions.find((f: any) => f.name === mockTool.name);
|
|
164
|
+
const secondFunction = responseData.functions.find((f: any) => f.name === 'second_tool');
|
|
165
|
+
|
|
166
|
+
expect(firstFunction).toEqual({
|
|
167
|
+
name: mockTool.name,
|
|
168
|
+
description: mockTool.description,
|
|
169
|
+
parameters: mockTool.parameters.map((p: Parameter) => p.toJSON()),
|
|
170
|
+
endpoint: mockTool.endpoint,
|
|
171
|
+
http_method: 'POST'
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
expect(secondFunction).toEqual({
|
|
175
|
+
name: 'second_tool',
|
|
176
|
+
description: 'Second test tool',
|
|
177
|
+
parameters: [],
|
|
178
|
+
endpoint: '/second-tool',
|
|
179
|
+
http_method: 'POST',
|
|
180
|
+
auth_requirements: authRequirements.map((auth) => auth.toJSON())
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
describe('tool execution', () => {
|
|
186
|
+
beforeEach(() => {
|
|
187
|
+
toolsService.registerTool(
|
|
188
|
+
mockTool.name,
|
|
189
|
+
mockTool.description,
|
|
190
|
+
mockTool.handler,
|
|
191
|
+
mockTool.parameters,
|
|
192
|
+
mockTool.endpoint
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should execute tool successfully with parameters', async () => {
|
|
197
|
+
const mockRequest = createMockRequest();
|
|
198
|
+
const response = await toolsService.processRequest(mockRequest);
|
|
199
|
+
|
|
200
|
+
expect(response.status).toBe(200);
|
|
201
|
+
expect(mockTool.handler).toHaveBeenCalledWith(
|
|
202
|
+
{ param1: 'test-value' },
|
|
203
|
+
undefined
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('should execute tool with OptiID auth data when provided', async () => {
|
|
208
|
+
const authData = new OptiIdAuthData(
|
|
209
|
+
'optiId',
|
|
210
|
+
new OptiIdAuthDataCredentials('customer123', 'instance123', 'token123', 'sku123')
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const requestWithAuth = createMockRequest({
|
|
214
|
+
bodyJSON: {
|
|
215
|
+
parameters: { param1: 'test-value' },
|
|
216
|
+
auth: authData
|
|
217
|
+
},
|
|
218
|
+
body: JSON.stringify({
|
|
219
|
+
parameters: { param1: 'test-value' },
|
|
220
|
+
auth: authData
|
|
221
|
+
})
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const response = await toolsService.processRequest(requestWithAuth);
|
|
225
|
+
|
|
226
|
+
expect(response.status).toBe(200);
|
|
227
|
+
expect(mockTool.handler).toHaveBeenCalledWith(
|
|
228
|
+
{ param1: 'test-value' },
|
|
229
|
+
authData
|
|
230
|
+
);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should handle request body without parameters wrapper', async () => {
|
|
234
|
+
const requestWithoutWrapper = createMockRequest({
|
|
235
|
+
bodyJSON: { param1: 'test-value' },
|
|
236
|
+
body: JSON.stringify({ param1: 'test-value' })
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const response = await toolsService.processRequest(requestWithoutWrapper);
|
|
240
|
+
|
|
241
|
+
expect(response.status).toBe(200);
|
|
242
|
+
expect(mockTool.handler).toHaveBeenCalledWith(
|
|
243
|
+
{ param1: 'test-value' },
|
|
244
|
+
undefined
|
|
245
|
+
);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('should return 500 error when tool handler throws an error', async () => {
|
|
249
|
+
const errorMessage = 'Tool execution failed';
|
|
250
|
+
jest.mocked(mockTool.handler).mockRejectedValueOnce(new Error(errorMessage));
|
|
251
|
+
|
|
252
|
+
const mockRequest = createMockRequest();
|
|
253
|
+
const response = await toolsService.processRequest(mockRequest);
|
|
254
|
+
|
|
255
|
+
expect(response.status).toBe(500);
|
|
256
|
+
expect(logger.error).toHaveBeenCalledWith(
|
|
257
|
+
`Error in function ${mockTool.name}:`,
|
|
258
|
+
expect.any(Error)
|
|
259
|
+
);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('should return 500 error with generic message when error has no message', async () => {
|
|
263
|
+
jest.mocked(mockTool.handler).mockRejectedValueOnce({});
|
|
264
|
+
|
|
265
|
+
const mockRequest = createMockRequest();
|
|
266
|
+
const response = await toolsService.processRequest(mockRequest);
|
|
267
|
+
|
|
268
|
+
expect(response.status).toBe(500);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
describe('interaction execution', () => {
|
|
273
|
+
beforeEach(() => {
|
|
274
|
+
toolsService.registerInteraction(
|
|
275
|
+
mockInteraction.name,
|
|
276
|
+
mockInteraction.handler,
|
|
277
|
+
mockInteraction.endpoint
|
|
278
|
+
);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should execute interaction successfully with data', async () => {
|
|
282
|
+
const interactionRequest = createMockRequest({
|
|
283
|
+
path: '/test-interaction',
|
|
284
|
+
bodyJSON: { data: { param1: 'test-value' } },
|
|
285
|
+
body: JSON.stringify({ data: { param1: 'test-value' } })
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
const response = await toolsService.processRequest(interactionRequest);
|
|
289
|
+
|
|
290
|
+
expect(response.status).toBe(200);
|
|
291
|
+
expect(mockInteraction.handler).toHaveBeenCalledWith({ param1: 'test-value' }, undefined);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('should handle interaction request body without data wrapper', async () => {
|
|
295
|
+
const interactionRequest = createMockRequest({
|
|
296
|
+
path: '/test-interaction',
|
|
297
|
+
bodyJSON: { param1: 'test-value' },
|
|
298
|
+
body: JSON.stringify({ param1: 'test-value' })
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const response = await toolsService.processRequest(interactionRequest);
|
|
302
|
+
|
|
303
|
+
expect(response.status).toBe(200);
|
|
304
|
+
expect(mockInteraction.handler).toHaveBeenCalledWith({ param1: 'test-value' }, undefined);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('should execute interaction with OptiID auth data when provided', async () => {
|
|
308
|
+
const authData = new OptiIdAuthData(
|
|
309
|
+
'optiId',
|
|
310
|
+
new OptiIdAuthDataCredentials('customer123', 'instance123', 'token123', 'sku123')
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
const interactionRequest = createMockRequest({
|
|
314
|
+
path: '/test-interaction',
|
|
315
|
+
bodyJSON: {
|
|
316
|
+
data: { param1: 'test-value' },
|
|
317
|
+
auth: authData
|
|
318
|
+
},
|
|
319
|
+
body: JSON.stringify({
|
|
320
|
+
data: { param1: 'test-value' },
|
|
321
|
+
auth: authData
|
|
322
|
+
})
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
const response = await toolsService.processRequest(interactionRequest);
|
|
326
|
+
|
|
327
|
+
expect(response.status).toBe(200);
|
|
328
|
+
expect(mockInteraction.handler).toHaveBeenCalledWith(
|
|
329
|
+
{ param1: 'test-value' },
|
|
330
|
+
authData
|
|
331
|
+
);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('should handle interaction request without data wrapper but with auth data', async () => {
|
|
335
|
+
const authData = new OptiIdAuthData(
|
|
336
|
+
'optiId',
|
|
337
|
+
new OptiIdAuthDataCredentials('customer123', 'instance123', 'token123', 'sku123')
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
const interactionRequest = createMockRequest({
|
|
341
|
+
path: '/test-interaction',
|
|
342
|
+
bodyJSON: {
|
|
343
|
+
param1: 'test-value',
|
|
344
|
+
auth: authData
|
|
345
|
+
},
|
|
346
|
+
body: JSON.stringify({
|
|
347
|
+
param1: 'test-value',
|
|
348
|
+
auth: authData
|
|
349
|
+
})
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
const response = await toolsService.processRequest(interactionRequest);
|
|
353
|
+
|
|
354
|
+
expect(response.status).toBe(200);
|
|
355
|
+
expect(mockInteraction.handler).toHaveBeenCalledWith(
|
|
356
|
+
{
|
|
357
|
+
param1: 'test-value',
|
|
358
|
+
auth: authData
|
|
359
|
+
},
|
|
360
|
+
authData
|
|
361
|
+
);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('should return 500 error when interaction handler throws an error', async () => {
|
|
365
|
+
const errorMessage = 'Interaction execution failed';
|
|
366
|
+
jest.mocked(mockInteraction.handler).mockRejectedValueOnce(new Error(errorMessage));
|
|
367
|
+
|
|
368
|
+
const interactionRequest = createMockRequest({
|
|
369
|
+
path: '/test-interaction',
|
|
370
|
+
bodyJSON: { data: { param1: 'test-value' } }
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
const response = await toolsService.processRequest(interactionRequest);
|
|
374
|
+
|
|
375
|
+
expect(response.status).toBe(500);
|
|
376
|
+
expect(logger.error).toHaveBeenCalledWith(
|
|
377
|
+
`Error in function ${mockInteraction.name}:`,
|
|
378
|
+
expect.any(Error)
|
|
379
|
+
);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
describe('error cases', () => {
|
|
384
|
+
it('should return 404 when no matching tool or interaction is found', async () => {
|
|
385
|
+
const unknownRequest = createMockRequest({ path: '/unknown-endpoint' });
|
|
386
|
+
const response = await toolsService.processRequest(unknownRequest);
|
|
387
|
+
|
|
388
|
+
expect(response.status).toBe(404);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it('should handle tool with OptiID auth requirements', async () => {
|
|
392
|
+
const authRequirements = [
|
|
393
|
+
new AuthRequirement('OptiID', 'calendar', true)
|
|
394
|
+
];
|
|
395
|
+
|
|
396
|
+
toolsService.registerTool(
|
|
397
|
+
'optiIdAuthTool',
|
|
398
|
+
'Tool with OptiID auth',
|
|
399
|
+
mockTool.handler,
|
|
400
|
+
mockTool.parameters,
|
|
401
|
+
'/optid-auth-tool',
|
|
402
|
+
authRequirements
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
const authRequest = createMockRequest({
|
|
406
|
+
path: '/optid-auth-tool'
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
const response = await toolsService.processRequest(authRequest);
|
|
410
|
+
|
|
411
|
+
expect(response.status).toBe(200);
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
describe('edge cases', () => {
|
|
416
|
+
it('should handle request with null bodyJSON', async () => {
|
|
417
|
+
toolsService.registerTool(
|
|
418
|
+
mockTool.name,
|
|
419
|
+
mockTool.description,
|
|
420
|
+
mockTool.handler,
|
|
421
|
+
mockTool.parameters,
|
|
422
|
+
mockTool.endpoint
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
const requestWithNullBody = createMockRequest({
|
|
426
|
+
bodyJSON: null,
|
|
427
|
+
body: null
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
const response = await toolsService.processRequest(requestWithNullBody);
|
|
431
|
+
|
|
432
|
+
expect(response.status).toBe(200);
|
|
433
|
+
expect(mockTool.handler).toHaveBeenCalledWith(null, undefined);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it('should handle request with undefined bodyJSON', async () => {
|
|
437
|
+
toolsService.registerTool(
|
|
438
|
+
mockTool.name,
|
|
439
|
+
mockTool.description,
|
|
440
|
+
mockTool.handler,
|
|
441
|
+
mockTool.parameters,
|
|
442
|
+
mockTool.endpoint
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
const requestWithUndefinedBody = createMockRequest({
|
|
446
|
+
bodyJSON: undefined,
|
|
447
|
+
body: undefined
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
const response = await toolsService.processRequest(requestWithUndefinedBody);
|
|
451
|
+
|
|
452
|
+
expect(response.status).toBe(200);
|
|
453
|
+
expect(mockTool.handler).toHaveBeenCalledWith(undefined, undefined);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it('should extract auth data from bodyJSON when body exists', async () => {
|
|
457
|
+
toolsService.registerTool(
|
|
458
|
+
mockTool.name,
|
|
459
|
+
mockTool.description,
|
|
460
|
+
mockTool.handler,
|
|
461
|
+
mockTool.parameters,
|
|
462
|
+
mockTool.endpoint
|
|
463
|
+
);
|
|
464
|
+
|
|
465
|
+
const authData = new OptiIdAuthData(
|
|
466
|
+
'optiId',
|
|
467
|
+
new OptiIdAuthDataCredentials('customer123', 'instance123', 'token123', 'sku123')
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
const requestWithAuth = createMockRequest({
|
|
471
|
+
bodyJSON: {
|
|
472
|
+
parameters: { param1: 'test-value' },
|
|
473
|
+
auth: authData
|
|
474
|
+
},
|
|
475
|
+
body: JSON.stringify({
|
|
476
|
+
parameters: { param1: 'test-value' },
|
|
477
|
+
auth: authData
|
|
478
|
+
})
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
const response = await toolsService.processRequest(requestWithAuth);
|
|
482
|
+
|
|
483
|
+
expect(response.status).toBe(200);
|
|
484
|
+
expect(mockTool.handler).toHaveBeenCalledWith(
|
|
485
|
+
{ param1: 'test-value' },
|
|
486
|
+
authData
|
|
487
|
+
);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it('should handle missing auth data gracefully', async () => {
|
|
491
|
+
toolsService.registerTool(
|
|
492
|
+
mockTool.name,
|
|
493
|
+
mockTool.description,
|
|
494
|
+
mockTool.handler,
|
|
495
|
+
mockTool.parameters,
|
|
496
|
+
mockTool.endpoint
|
|
497
|
+
);
|
|
498
|
+
|
|
499
|
+
const requestWithoutAuth = createMockRequest({
|
|
500
|
+
bodyJSON: {
|
|
501
|
+
parameters: { param1: 'test-value' }
|
|
502
|
+
// No auth property
|
|
503
|
+
},
|
|
504
|
+
body: JSON.stringify({
|
|
505
|
+
parameters: { param1: 'test-value' }
|
|
506
|
+
})
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
const response = await toolsService.processRequest(requestWithoutAuth);
|
|
510
|
+
|
|
511
|
+
expect(response.status).toBe(200);
|
|
512
|
+
expect(mockTool.handler).toHaveBeenCalledWith(
|
|
513
|
+
{ param1: 'test-value' },
|
|
514
|
+
undefined
|
|
515
|
+
);
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it('should handle auth extraction when body is falsy but bodyJSON has auth', async () => {
|
|
519
|
+
toolsService.registerTool(
|
|
520
|
+
mockTool.name,
|
|
521
|
+
mockTool.description,
|
|
522
|
+
mockTool.handler,
|
|
523
|
+
mockTool.parameters,
|
|
524
|
+
mockTool.endpoint
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
const authData = new OptiIdAuthData(
|
|
528
|
+
'optiId',
|
|
529
|
+
new OptiIdAuthDataCredentials('customer123', 'instance123', 'token123', 'sku123')
|
|
530
|
+
);
|
|
531
|
+
|
|
532
|
+
const requestWithAuthButNoBody = createMockRequest({
|
|
533
|
+
bodyJSON: {
|
|
534
|
+
parameters: { param1: 'test-value' },
|
|
535
|
+
auth: authData
|
|
536
|
+
},
|
|
537
|
+
body: ''
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
const response = await toolsService.processRequest(requestWithAuthButNoBody);
|
|
541
|
+
|
|
542
|
+
expect(response.status).toBe(200);
|
|
543
|
+
expect(mockTool.handler).toHaveBeenCalledWith(
|
|
544
|
+
{ param1: 'test-value' },
|
|
545
|
+
authData
|
|
546
|
+
);
|
|
547
|
+
});
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
});
|