@optimizely-opal/opal-tool-ocp-sdk 1.1.0-beta.1 → 1.1.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.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.IslandResponse = exports.IslandConfig = exports.IslandAction = exports.IslandField = exports.AuthRequirement = exports.OptiIdAuthData = exports.OptiIdAuthDataCredentials = exports.Parameter = exports.ParameterType = void 0;
3
+ exports.IslandResponse = exports.IslandConfig = exports.IslandAction = exports.IslandField = exports.AuthRequirement = exports.Parameter = exports.ParameterType = void 0;
4
4
  /* eslint-disable max-classes-per-file */
5
5
  /**
6
6
  * Types of parameters supported by Opal tools
@@ -48,34 +48,6 @@ class Parameter {
48
48
  }
49
49
  }
50
50
  exports.Parameter = Parameter;
51
- /**
52
- * Credentials structure
53
- */
54
- class OptiIdAuthDataCredentials {
55
- customer_id;
56
- instance_id;
57
- access_token;
58
- product_sku;
59
- constructor(customer_id, instance_id, access_token, product_sku) {
60
- this.customer_id = customer_id;
61
- this.instance_id = instance_id;
62
- this.access_token = access_token;
63
- this.product_sku = product_sku;
64
- }
65
- }
66
- exports.OptiIdAuthDataCredentials = OptiIdAuthDataCredentials;
67
- /**
68
- * Auth data structure
69
- */
70
- class OptiIdAuthData {
71
- provider;
72
- credentials;
73
- constructor(provider, credentials) {
74
- this.provider = provider;
75
- this.credentials = credentials;
76
- }
77
- }
78
- exports.OptiIdAuthData = OptiIdAuthData;
79
51
  /**
80
52
  * Authentication requirements for an Opal tool
81
53
  */
@@ -1 +1 @@
1
- {"version":3,"file":"Models.js","sourceRoot":"","sources":["../../src/types/Models.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Models.js","sourceRoot":"","sources":["../../src/types/Models.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optimizely-opal/opal-tool-ocp-sdk",
3
- "version": "1.1.0-beta.1",
3
+ "version": "1.1.0",
4
4
  "description": "OCP SDK for Opal tool",
5
5
  "scripts": {
6
6
  "validate-deps": "node scripts/validate-deps.js",
@@ -1,6 +1,6 @@
1
1
  import { getAppContext, logger, Request } from '@zaiusinc/app-sdk';
2
2
  import { getTokenVerifier } from './TokenVerifier';
3
- import { OptiIdAuthData } from '../types/Models';
3
+ import { AuthData, OptiIdAuthDataCredentials } from '../types/Models';
4
4
  import { ToolError } from '../types/ToolError';
5
5
 
6
6
  /**
@@ -34,8 +34,8 @@ async function validateAccessToken(accessToken: string | undefined): Promise<voi
34
34
  * @param request - The incoming request
35
35
  * @returns object with authData and accessToken, or null if auth is missing
36
36
  */
37
- export function extractAuthData(request: any): { authData: OptiIdAuthData; accessToken: string } | null {
38
- const authData = request?.bodyJSON?.auth as OptiIdAuthData;
37
+ export function extractAuthData(request: any): OptiIdAuthDataCredentials | null {
38
+ const authData = request?.bodyJSON?.auth as AuthData;
39
39
 
40
40
  if (!authData) {
41
41
  return null;
@@ -50,7 +50,7 @@ export function extractAuthData(request: any): { authData: OptiIdAuthData; acces
50
50
  return null;
51
51
  }
52
52
 
53
- return { authData, accessToken };
53
+ return authData?.credentials as OptiIdAuthDataCredentials;
54
54
  }
55
55
 
56
56
  /**
@@ -60,8 +60,8 @@ export function extractAuthData(request: any): { authData: OptiIdAuthData; acces
60
60
  * @returns object with authData and accessToken
61
61
  * @throws {ToolError} If auth data is invalid or missing
62
62
  */
63
- function extractAndValidateAuthData(request: any): { authData: OptiIdAuthData; accessToken: string } {
64
- const authData = request?.bodyJSON?.auth as OptiIdAuthData;
63
+ function extractAndValidateAuthData(request: any): OptiIdAuthDataCredentials {
64
+ const authData = request?.bodyJSON?.auth as AuthData;
65
65
 
66
66
  if (!authData) {
67
67
  throw new ToolError('Forbidden', 403, 'Authentication data is required');
@@ -76,7 +76,7 @@ function extractAndValidateAuthData(request: any): { authData: OptiIdAuthData; a
76
76
  throw new ToolError('Forbidden', 403, 'OptiID access token is required');
77
77
  }
78
78
 
79
- return { authData, accessToken };
79
+ return authData?.credentials as OptiIdAuthDataCredentials;
80
80
  }
81
81
 
82
82
  /**
@@ -120,14 +120,14 @@ async function authenticateRequest(request: any, validateOrg: boolean): Promise<
120
120
  return;
121
121
  }
122
122
 
123
- const { authData, accessToken } = extractAndValidateAuthData(request);
123
+ const optiIdCredentials = extractAndValidateAuthData(request);
124
124
 
125
125
  // Validate organization ID if required
126
126
  if (validateOrg) {
127
- validateOrganizationId(authData.credentials?.customer_id);
127
+ validateOrganizationId(optiIdCredentials.customer_id);
128
128
  }
129
129
 
130
- await validateAccessToken(accessToken);
130
+ await validateAccessToken(optiIdCredentials.access_token);
131
131
  }
132
132
 
133
133
  /**
@@ -544,7 +544,7 @@ describe('Decorators', () => {
544
544
  const registeredHandler = registerToolCall![2];
545
545
 
546
546
  // Call the handler
547
- const result = await registeredHandler(undefined as any, {}, {});
547
+ const result = await registeredHandler(undefined as any, {}, undefined as any);
548
548
 
549
549
  // Should be able to call class methods through 'this'
550
550
  expect((result as any).result).toBe('class-method-called');
@@ -574,7 +574,7 @@ describe('Decorators', () => {
574
574
  const registeredHandler = registerInteractionCall![1];
575
575
 
576
576
  // Call the handler
577
- const result = await registeredHandler(undefined as any, {});
577
+ const result = await registeredHandler(undefined as any, {}, undefined as any);
578
578
 
579
579
  // Should be able to call class methods through 'this'
580
580
  expect((result as any).message).toBe('interaction-processed');
@@ -606,7 +606,7 @@ describe('Decorators', () => {
606
606
  // Call the handler with an invalid functionContext (truthy but wrong type)
607
607
  // This should trigger the instanceof check and create a new instance instead
608
608
  const invalidContext = { notAnInstance: true, someOtherProperty: 'invalid' };
609
- const result = await registeredHandler(invalidContext as any, {}, {});
609
+ const result = await registeredHandler(invalidContext as any, {}, undefined as any);
610
610
 
611
611
  // Should still work by creating a new instance
612
612
  expect((result as any).result).toBe('class-method-called');
@@ -638,7 +638,7 @@ describe('Decorators', () => {
638
638
  // Call the handler with an invalid functionContext (truthy but wrong type)
639
639
  // This should trigger the instanceof check and create a new instance instead
640
640
  const invalidContext = { notAnInstance: true, someOtherProperty: 'invalid' };
641
- const result = await registeredHandler(invalidContext as any, {});
641
+ const result = await registeredHandler(invalidContext as any, {}, undefined as any);
642
642
 
643
643
  // Should still work by creating a new instance
644
644
  expect((result as any).message).toBe('interaction-processed');
@@ -30,8 +30,8 @@ export abstract class GlobalToolFunction extends GlobalFunction {
30
30
  const startTime = Date.now();
31
31
 
32
32
  // Extract customer_id from auth data for global context attribution
33
- const authInfo = extractAuthData(this.request);
34
- const customerId = authInfo?.authData?.credentials?.customer_id;
33
+ const optiIdCredentials = extractAuthData(this.request);
34
+ const customerId = optiIdCredentials?.customer_id;
35
35
 
36
36
  amendLogContext({
37
37
  opalThreadId: this.request.headers.get('x-opal-thread-id') || '',
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/no-unsafe-call */
2
2
  import { toolsService, Tool, Interaction } from './Service';
3
- import { Parameter, ParameterType, AuthRequirement, OptiIdAuthDataCredentials, OptiIdAuthData } from '../types/Models';
3
+ import { Parameter, ParameterType, AuthRequirement, AuthData } from '../types/Models';
4
4
  import { ToolError } from '../types/ToolError';
5
5
  import { ToolFunction } from '../function/ToolFunction';
6
6
  import { logger } from '@zaiusinc/app-sdk';
@@ -82,8 +82,8 @@ const mockKvStore = storage.kvStore;
82
82
 
83
83
 
84
84
  describe('ToolsService', () => {
85
- let mockTool: Tool<unknown>;
86
- let mockInteraction: Interaction<unknown>;
85
+ let mockTool: Tool;
86
+ let mockInteraction: Interaction;
87
87
  let mockToolFunction: ToolFunction;
88
88
 
89
89
  beforeEach(() => {
@@ -135,11 +135,21 @@ describe('ToolsService', () => {
135
135
  return map;
136
136
  };
137
137
 
138
+ const defaultAuth = {
139
+ provider: 'OptiID',
140
+ credentials: {
141
+ customer_id: 'test-customer',
142
+ instance_id: 'test-instance',
143
+ access_token: 'test-token',
144
+ product_sku: 'test-sku'
145
+ }
146
+ };
147
+
138
148
  const baseRequest = {
139
149
  path: '/test-tool',
140
150
  method: 'POST',
141
- bodyJSON: { parameters: { param1: 'test-value' } },
142
- body: JSON.stringify({ parameters: { param1: 'test-value' } }),
151
+ bodyJSON: { parameters: { param1: 'test-value' }, auth: defaultAuth },
152
+ body: JSON.stringify({ parameters: { param1: 'test-value' }, auth: defaultAuth }),
143
153
  bodyData: Buffer.from(''),
144
154
  headers: createHeadersMap(),
145
155
  params: {},
@@ -280,7 +290,7 @@ describe('ToolsService', () => {
280
290
  expect(mockTool.handler).toHaveBeenCalledWith(
281
291
  mockToolFunction, // functionContext
282
292
  { param1: 'test-value' },
283
- undefined
293
+ expect.objectContaining({ provider: 'OptiID' })
284
294
  );
285
295
  });
286
296
 
@@ -302,7 +312,7 @@ describe('ToolsService', () => {
302
312
  expect(mockTool.handler).toHaveBeenCalledWith(
303
313
  mockToolFunctionInstance, // functionContext - existing instance
304
314
  { param1: 'test-value' },
305
- undefined
315
+ expect.objectContaining({ provider: 'OptiID' })
306
316
  );
307
317
  });
308
318
 
@@ -349,9 +359,19 @@ describe('ToolsService', () => {
349
359
  '/test-toolfunction-access'
350
360
  );
351
361
 
362
+ const authData = {
363
+ provider: 'OptiID',
364
+ credentials: {
365
+ customer_id: 'test-customer',
366
+ instance_id: 'test-instance',
367
+ access_token: 'test-token',
368
+ product_sku: 'test-sku'
369
+ }
370
+ };
371
+
352
372
  const testRequest = createMockRequest({
353
373
  path: '/test-toolfunction-access',
354
- bodyJSON: { action: 'test' }
374
+ bodyJSON: { action: 'test', auth: authData }
355
375
  });
356
376
 
357
377
  const response = await toolsService.processRequest(testRequest, mockToolFunctionInstance);
@@ -361,19 +381,24 @@ describe('ToolsService', () => {
361
381
  expect((response as any).data.success).toBe(true);
362
382
  expect((response as any).data.requestPath).toBe('/test-path');
363
383
  expect((response as any).data.testMethodResult).toBe('path: /test-path');
364
- expect((response as any).data.receivedParams).toEqual({ action: 'test' });
384
+ expect((response as any).data.receivedParams).toEqual({ action: 'test', auth: authData });
365
385
  expect(handlerThatAccessesRequest).toHaveBeenCalledWith(
366
386
  mockToolFunctionInstance, // functionContext is the ToolFunction instance
367
- { action: 'test' },
368
- undefined
387
+ { action: 'test', auth: authData },
388
+ authData
369
389
  );
370
390
  });
371
391
 
372
392
  it('should execute tool with OptiID auth data when provided', async () => {
373
- const authData = new OptiIdAuthData(
374
- 'optiId',
375
- new OptiIdAuthDataCredentials('customer123', 'instance123', 'token123', 'sku123')
376
- );
393
+ const authData: AuthData = {
394
+ provider: 'OptiID',
395
+ credentials: {
396
+ customer_id: 'customer123',
397
+ instance_id: 'instance123',
398
+ access_token: 'token123',
399
+ product_sku: 'sku123'
400
+ }
401
+ };
377
402
 
378
403
  const requestWithAuth = createMockRequest({
379
404
  bodyJSON: {
@@ -397,9 +422,19 @@ describe('ToolsService', () => {
397
422
  });
398
423
 
399
424
  it('should handle request body without parameters wrapper', async () => {
425
+ const authData = {
426
+ provider: 'OptiID',
427
+ credentials: {
428
+ customer_id: 'test-customer',
429
+ instance_id: 'test-instance',
430
+ access_token: 'test-token',
431
+ product_sku: 'test-sku'
432
+ }
433
+ };
434
+
400
435
  const requestWithoutWrapper = createMockRequest({
401
- bodyJSON: { param1: 'test-value' },
402
- body: JSON.stringify({ param1: 'test-value' })
436
+ bodyJSON: { param1: 'test-value', auth: authData },
437
+ body: JSON.stringify({ param1: 'test-value', auth: authData })
403
438
  });
404
439
 
405
440
  const response = await toolsService.processRequest(requestWithoutWrapper, mockToolFunction);
@@ -407,8 +442,8 @@ describe('ToolsService', () => {
407
442
  expect(response.status).toBe(200);
408
443
  expect(mockTool.handler).toHaveBeenCalledWith(
409
444
  mockToolFunction, // functionContext
410
- { param1: 'test-value' },
411
- undefined
445
+ { param1: 'test-value', auth: authData },
446
+ authData
412
447
  );
413
448
  });
414
449
 
@@ -474,36 +509,62 @@ describe('ToolsService', () => {
474
509
  });
475
510
 
476
511
  it('should execute interaction successfully with data', async () => {
512
+ const authData = {
513
+ provider: 'OptiID',
514
+ credentials: {
515
+ customer_id: 'test-customer',
516
+ instance_id: 'test-instance',
517
+ access_token: 'test-token',
518
+ product_sku: 'test-sku'
519
+ }
520
+ };
521
+
477
522
  const interactionRequest = createMockRequest({
478
523
  path: '/test-interaction',
479
- bodyJSON: { data: { param1: 'test-value' } },
480
- body: JSON.stringify({ data: { param1: 'test-value' } })
524
+ bodyJSON: { data: { param1: 'test-value' }, auth: authData },
525
+ body: JSON.stringify({ data: { param1: 'test-value' }, auth: authData })
481
526
  });
482
527
 
483
528
  const response = await toolsService.processRequest(interactionRequest, mockToolFunction);
484
529
 
485
530
  expect(response.status).toBe(200);
486
- expect(mockInteraction.handler).toHaveBeenCalledWith(mockToolFunction, { param1: 'test-value' }, undefined);
531
+ expect(mockInteraction.handler).toHaveBeenCalledWith(mockToolFunction, { param1: 'test-value' }, authData);
487
532
  });
488
533
 
489
534
  it('should handle interaction request body without data wrapper', async () => {
535
+ const authData = {
536
+ provider: 'OptiID',
537
+ credentials: {
538
+ customer_id: 'test-customer',
539
+ instance_id: 'test-instance',
540
+ access_token: 'test-token',
541
+ product_sku: 'test-sku'
542
+ }
543
+ };
544
+
490
545
  const interactionRequest = createMockRequest({
491
546
  path: '/test-interaction',
492
- bodyJSON: { param1: 'test-value' },
493
- body: JSON.stringify({ param1: 'test-value' })
547
+ bodyJSON: { param1: 'test-value', auth: authData },
548
+ body: JSON.stringify({ param1: 'test-value', auth: authData })
494
549
  });
495
550
 
496
551
  const response = await toolsService.processRequest(interactionRequest, mockToolFunction);
497
552
 
498
553
  expect(response.status).toBe(200);
499
- expect(mockInteraction.handler).toHaveBeenCalledWith(mockToolFunction, { param1: 'test-value' }, undefined);
554
+ expect(mockInteraction.handler)
555
+ .toHaveBeenCalledWith(mockToolFunction, { param1: 'test-value', auth: authData }, authData);
500
556
  });
501
557
 
502
558
  it('should execute interaction with OptiID auth data when provided', async () => {
503
- const authData = new OptiIdAuthData(
504
- 'optiId',
505
- new OptiIdAuthDataCredentials('customer123', 'instance123', 'token123', 'sku123')
506
- );
559
+ const authData = {
560
+ provider: 'OptiID',
561
+ credentials: {
562
+ customer_id: 'customer123',
563
+ instance_id: 'instance123',
564
+ access_token: 'token123',
565
+ product_sku: 'sku123'
566
+ }
567
+ };
507
568
 
508
569
  const interactionRequest = createMockRequest({
509
570
  path: '/test-interaction',
@@ -528,10 +589,15 @@ describe('ToolsService', () => {
528
589
  });
529
590
 
530
591
  it('should handle interaction request without data wrapper but with auth data', async () => {
531
- const authData = new OptiIdAuthData(
532
- 'optiId',
533
- new OptiIdAuthDataCredentials('customer123', 'instance123', 'token123', 'sku123')
534
- );
592
+ const authData = {
593
+ provider: 'OptiID',
594
+ credentials: {
595
+ customer_id: 'customer123',
596
+ instance_id: 'instance123',
597
+ access_token: 'token123',
598
+ product_sku: 'sku123'
599
+ }
600
+ };
535
601
 
536
602
  const interactionRequest = createMockRequest({
537
603
  path: '/test-interaction',
@@ -559,12 +625,21 @@ describe('ToolsService', () => {
559
625
  });
560
626
 
561
627
  it('should throw error when interaction handler throws a regular error', async () => {
628
+ const authData = {
629
+ provider: 'OptiID',
630
+ credentials: {
631
+ customer_id: 'test-customer',
632
+ instance_id: 'test-instance',
633
+ access_token: 'test-token',
634
+ product_sku: 'test-sku'
635
+ }
636
+ };
562
637
  const errorMessage = 'Interaction execution failed';
563
638
  jest.mocked(mockInteraction.handler).mockRejectedValueOnce(new Error(errorMessage));
564
639
 
565
640
  const interactionRequest = createMockRequest({
566
641
  path: '/test-interaction',
567
- bodyJSON: { data: { param1: 'test-value' } }
642
+ bodyJSON: { data: { param1: 'test-value' }, auth: authData }
568
643
  });
569
644
 
570
645
  await expect(toolsService.processRequest(interactionRequest, mockToolFunction))
@@ -572,12 +647,21 @@ describe('ToolsService', () => {
572
647
  });
573
648
 
574
649
  it('should throw ToolError when interaction handler throws ToolError', async () => {
650
+ const authData = {
651
+ provider: 'OptiID',
652
+ credentials: {
653
+ customer_id: 'test-customer',
654
+ instance_id: 'test-instance',
655
+ access_token: 'test-token',
656
+ product_sku: 'test-sku'
657
+ }
658
+ };
575
659
  const toolError = new ToolError('Webhook validation failed', 400, 'Invalid signature');
576
660
  jest.mocked(mockInteraction.handler).mockRejectedValueOnce(toolError);
577
661
 
578
662
  const interactionRequest = createMockRequest({
579
663
  path: '/test-interaction',
580
- bodyJSON: { data: { param1: 'test-value' } }
664
+ bodyJSON: { data: { param1: 'test-value' }, auth: authData }
581
665
  });
582
666
 
583
667
  await expect(toolsService.processRequest(interactionRequest, mockToolFunction))
@@ -627,7 +711,7 @@ describe('ToolsService', () => {
627
711
  });
628
712
 
629
713
  describe('edge cases', () => {
630
- it('should handle request with null bodyJSON', async () => {
714
+ it('should throw 403 when request has null bodyJSON (no auth data)', async () => {
631
715
  // Create a tool without required parameters
632
716
  const toolWithoutRequiredParams = {
633
717
  name: 'no_required_params_tool',
@@ -651,13 +735,18 @@ describe('ToolsService', () => {
651
735
  body: null
652
736
  });
653
737
 
654
- const response = await toolsService.processRequest(requestWithNullBody, mockToolFunction);
738
+ await expect(toolsService.processRequest(requestWithNullBody, mockToolFunction))
739
+ .rejects.toThrow(ToolError);
655
740
 
656
- expect(response.status).toBe(200);
657
- expect(toolWithoutRequiredParams.handler).toHaveBeenCalledWith(mockToolFunction, null, undefined);
741
+ try {
742
+ await toolsService.processRequest(requestWithNullBody, mockToolFunction);
743
+ } catch (error) {
744
+ expect((error as ToolError).status).toBe(403);
745
+ expect((error as ToolError).message).toContain('Authentication data is required');
746
+ }
658
747
  });
659
748
 
660
- it('should handle request with undefined bodyJSON', async () => {
749
+ it('should throw 403 when request has undefined bodyJSON (no auth data)', async () => {
661
750
  // Create a tool without required parameters
662
751
  const toolWithoutRequiredParams = {
663
752
  name: 'no_required_params_tool_2',
@@ -681,10 +770,15 @@ describe('ToolsService', () => {
681
770
  body: undefined
682
771
  });
683
772
 
684
- const response = await toolsService.processRequest(requestWithUndefinedBody, mockToolFunction);
773
+ await expect(toolsService.processRequest(requestWithUndefinedBody, mockToolFunction))
774
+ .rejects.toThrow(ToolError);
685
775
 
686
- expect(response.status).toBe(200);
687
- expect(toolWithoutRequiredParams.handler).toHaveBeenCalledWith(mockToolFunction, undefined, undefined);
776
+ try {
777
+ await toolsService.processRequest(requestWithUndefinedBody, mockToolFunction);
778
+ } catch (error) {
779
+ expect((error as ToolError).status).toBe(403);
780
+ expect((error as ToolError).message).toContain('Authentication data is required');
781
+ }
688
782
  });
689
783
 
690
784
  it('should extract auth data from bodyJSON when body exists', async () => {
@@ -696,10 +790,15 @@ describe('ToolsService', () => {
696
790
  mockTool.endpoint
697
791
  );
698
792
 
699
- const authData = new OptiIdAuthData(
700
- 'optiId',
701
- new OptiIdAuthDataCredentials('customer123', 'instance123', 'token123', 'sku123')
702
- );
793
+ const authData = {
794
+ provider: 'OptiID',
795
+ credentials: {
796
+ customer_id: 'customer123',
797
+ instance_id: 'instance123',
798
+ access_token: 'token123',
799
+ product_sku: 'sku123'
800
+ }
801
+ };
703
802
 
704
803
  const requestWithAuth = createMockRequest({
705
804
  bodyJSON: {
@@ -722,7 +821,7 @@ describe('ToolsService', () => {
722
821
  );
723
822
  });
724
823
 
725
- it('should handle missing auth data gracefully', async () => {
824
+ it('should throw 403 when auth data is missing for tool with auth requirements', async () => {
726
825
  toolsService.registerTool(
727
826
  mockTool.name,
728
827
  mockTool.description,
@@ -741,14 +840,15 @@ describe('ToolsService', () => {
741
840
  })
742
841
  });
743
842
 
744
- const response = await toolsService.processRequest(requestWithoutAuth, mockToolFunction);
843
+ await expect(toolsService.processRequest(requestWithoutAuth, mockToolFunction))
844
+ .rejects.toThrow(ToolError);
745
845
 
746
- expect(response.status).toBe(200);
747
- expect(mockTool.handler).toHaveBeenCalledWith(
748
- mockToolFunction, // functionContext
749
- { param1: 'test-value' },
750
- undefined
751
- );
846
+ try {
847
+ await toolsService.processRequest(requestWithoutAuth, mockToolFunction);
848
+ } catch (error) {
849
+ expect((error as ToolError).status).toBe(403);
850
+ expect((error as ToolError).message).toContain('Authentication data is required');
851
+ }
752
852
  });
753
853
 
754
854
  it('should handle auth extraction when body is falsy but bodyJSON has auth', async () => {
@@ -760,10 +860,15 @@ describe('ToolsService', () => {
760
860
  mockTool.endpoint
761
861
  );
762
862
 
763
- const authData = new OptiIdAuthData(
764
- 'optiId',
765
- new OptiIdAuthDataCredentials('customer123', 'instance123', 'token123', 'sku123')
766
- );
863
+ const authData = {
864
+ provider: 'OptiID',
865
+ credentials: {
866
+ customer_id: 'customer123',
867
+ instance_id: 'instance123',
868
+ access_token: 'token123',
869
+ product_sku: 'sku123'
870
+ }
871
+ };
767
872
 
768
873
  const requestWithAuthButNoBody = createMockRequest({
769
874
  bodyJSON: {
@@ -864,13 +969,24 @@ describe('ToolsService', () => {
864
969
  toolWithoutParams.endpoint
865
970
  );
866
971
 
972
+ const authData = {
973
+ provider: 'OptiID',
974
+ credentials: {
975
+ customer_id: 'test-customer',
976
+ instance_id: 'test-instance',
977
+ access_token: 'test-token',
978
+ product_sku: 'test-sku'
979
+ }
980
+ };
981
+
867
982
  // Send request with any data (should be ignored)
868
983
  const request = createMockRequest({
869
984
  path: '/no-params-tool',
870
985
  bodyJSON: {
871
986
  parameters: {
872
987
  unexpected: 'value'
873
- }
988
+ },
989
+ auth: authData
874
990
  }
875
991
  });
876
992
 
@@ -880,7 +996,7 @@ describe('ToolsService', () => {
880
996
  expect(toolWithoutParams.handler).toHaveBeenCalledWith(
881
997
  mockToolFunction,
882
998
  { unexpected: 'value' },
883
- undefined
999
+ authData
884
1000
  );
885
1001
  });
886
1002
  });