@commercetools/checkout-payments-processor-sdk 0.0.6

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 (105) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +42 -0
  3. package/biome.json +38 -0
  4. package/package.json +48 -0
  5. package/src/api/context/request-context.helper.test.ts +98 -0
  6. package/src/api/context/request-context.helper.ts +89 -0
  7. package/src/api/context/request-context.provider.ts +27 -0
  8. package/src/api/context/types/request-context.type.ts +18 -0
  9. package/src/api/handlers/payment-components.handler.test.ts +378 -0
  10. package/src/api/handlers/payment-components.handler.ts +80 -0
  11. package/src/api/handlers/payment-intents.handler.test.ts +475 -0
  12. package/src/api/handlers/payment-intents.handler.ts +169 -0
  13. package/src/api/handlers/status.handler.test.ts +551 -0
  14. package/src/api/handlers/status.handler.ts +216 -0
  15. package/src/api/handlers/transaction.handler.ts +86 -0
  16. package/src/api/handlers/types/handler.type.ts +49 -0
  17. package/src/api/hooks/authorize.hook.test.ts +62 -0
  18. package/src/api/hooks/authorize.hook.ts +51 -0
  19. package/src/api/hooks/jwt-auth.hook.test.ts +54 -0
  20. package/src/api/hooks/jwt-auth.hook.ts +56 -0
  21. package/src/api/hooks/oauth2-auth.hook.test.ts +173 -0
  22. package/src/api/hooks/oauth2-auth.hook.ts +46 -0
  23. package/src/api/hooks/session-header-auth.hook.ts +47 -0
  24. package/src/api/hooks/session-query-param-auth.hook.ts +46 -0
  25. package/src/api/hooks/types/hook.type.ts +12 -0
  26. package/src/api/index.ts +11 -0
  27. package/src/api/types.ts +3 -0
  28. package/src/commercetools/api/base-api.ts +28 -0
  29. package/src/commercetools/api/cart-api.test.ts +95 -0
  30. package/src/commercetools/api/cart-api.ts +55 -0
  31. package/src/commercetools/api/custom-type-api.ts +39 -0
  32. package/src/commercetools/api/order-api.ts +22 -0
  33. package/src/commercetools/api/payment-api.ts +64 -0
  34. package/src/commercetools/api/payment-method-api.ts +118 -0
  35. package/src/commercetools/api/root-api.ts +109 -0
  36. package/src/commercetools/errors/ct-api.error.ts +27 -0
  37. package/src/commercetools/helpers/currency.converter.test.ts +121 -0
  38. package/src/commercetools/helpers/currency.converter.test_data.json +2379 -0
  39. package/src/commercetools/helpers/currency.converter.ts +102 -0
  40. package/src/commercetools/helpers/taxrate.converter.test.ts +48 -0
  41. package/src/commercetools/helpers/taxrate.converter.ts +39 -0
  42. package/src/commercetools/index.ts +22 -0
  43. package/src/commercetools/services/ct-cart.service.test.ts +1616 -0
  44. package/src/commercetools/services/ct-cart.service.ts +316 -0
  45. package/src/commercetools/services/ct-custom-type.service.ts +62 -0
  46. package/src/commercetools/services/ct-order.service.test.ts +136 -0
  47. package/src/commercetools/services/ct-order.service.ts +47 -0
  48. package/src/commercetools/services/ct-payment-method.service.test.ts +878 -0
  49. package/src/commercetools/services/ct-payment-method.service.ts +223 -0
  50. package/src/commercetools/services/ct-payment.service.test.ts +1123 -0
  51. package/src/commercetools/services/ct-payment.service.ts +574 -0
  52. package/src/commercetools/types/api.type.ts +162 -0
  53. package/src/commercetools/types/cart.type.ts +83 -0
  54. package/src/commercetools/types/custom-type.type.ts +21 -0
  55. package/src/commercetools/types/order.type.ts +19 -0
  56. package/src/commercetools/types/payment-method.type.ts +130 -0
  57. package/src/commercetools/types/payment.type.ts +74 -0
  58. package/src/commercetools/types/predefined-custom-types.type.ts +240 -0
  59. package/src/commercetools/types.ts +7 -0
  60. package/src/errorx/errorx.ts +625 -0
  61. package/src/errorx/index.ts +1 -0
  62. package/src/errorx/types.ts +5 -0
  63. package/src/fetch/decorators/base.decorator.ts +13 -0
  64. package/src/fetch/decorators/monitoring.decorator.ts +41 -0
  65. package/src/fetch/fetch.test.ts +57 -0
  66. package/src/fetch/index.ts +3 -0
  67. package/src/fetch/types/fetch.type.ts +11 -0
  68. package/src/fetch/types.ts +1 -0
  69. package/src/index.ts +15 -0
  70. package/src/logger/commercetools-logger.ts +69 -0
  71. package/src/logger/index.ts +1 -0
  72. package/src/logger/types/logger.type.ts +6 -0
  73. package/src/logger/types.ts +1 -0
  74. package/src/mocks/auth.mock.ts +6 -0
  75. package/src/mocks/cart.mock.ts +239 -0
  76. package/src/mocks/ct-api-error.mock.ts +23 -0
  77. package/src/mocks/index.ts +4 -0
  78. package/src/mocks/payment.mock.ts +35 -0
  79. package/src/payment-processor.ts +317 -0
  80. package/src/security/authn/authns.ts +219 -0
  81. package/src/security/authn/bearer-utils.test.ts +35 -0
  82. package/src/security/authn/bearer-utils.ts +28 -0
  83. package/src/security/authn/jwt-authn-manager.ts +69 -0
  84. package/src/security/authn/oauth2-authn-manager.ts +105 -0
  85. package/src/security/authn/session-header-authn-manager.ts +58 -0
  86. package/src/security/authn/session-query-param-authn-manager.ts +53 -0
  87. package/src/security/authn/types/authn.type.ts +50 -0
  88. package/src/security/authz/authorization-manager.ts +39 -0
  89. package/src/security/authz/types/authz.type.ts +13 -0
  90. package/src/security/index.ts +13 -0
  91. package/src/security/services/authorization.service.ts +54 -0
  92. package/src/security/services/jwt.service.test.ts +27 -0
  93. package/src/security/services/jwt.service.ts +45 -0
  94. package/src/security/services/oauth2.service.ts +60 -0
  95. package/src/security/services/session.service.ts +141 -0
  96. package/src/security/services/types/authorization.type.ts +10 -0
  97. package/src/security/services/types/jwt.type.ts +3 -0
  98. package/src/security/services/types/oauth2.type.ts +15 -0
  99. package/src/security/services/types/session.type.ts +38 -0
  100. package/src/security/types.ts +6 -0
  101. package/tsconfig.json +28 -0
  102. package/tsconfig.prod.json +9 -0
  103. package/typedoc.json +6 -0
  104. package/vitest.config.ts +15 -0
  105. package/wiki/getting-started.md +13 -0
@@ -0,0 +1,551 @@
1
+ import type { Logger } from '@logger-types';
2
+ import type { AuthorizationService } from '@security-types';
3
+ import {
4
+ type HealthCheck,
5
+ healthCheckCommercetoolsPermissions,
6
+ type StatusConfig,
7
+ StatusHandler,
8
+ type StatusIncomingHttpHeaders,
9
+ type StatusRequest,
10
+ } from '@/api/handlers/status.handler.js';
11
+
12
+ // Concrete implementation for testing the abstract class
13
+ class TestStatusHandler extends StatusHandler {
14
+ private statusConfig: StatusConfig;
15
+
16
+ constructor(opts: { logger: Logger; statusConfig: StatusConfig }) {
17
+ super({ logger: opts.logger });
18
+ this.statusConfig = opts.statusConfig;
19
+ }
20
+
21
+ public handleStatus(): StatusConfig {
22
+ return this.statusConfig;
23
+ }
24
+ }
25
+
26
+ describe('StatusHandler', () => {
27
+ let handler: TestStatusHandler;
28
+ let mockLogger: Logger;
29
+ let mockHealthCheck1: HealthCheck;
30
+ let mockHealthCheck2: HealthCheck;
31
+ let mockMetadataFn: () => Promise<object>;
32
+
33
+ const mockHeaders: StatusIncomingHttpHeaders = {
34
+ authorization: 'Bearer test-token',
35
+ 'content-type': 'application/json',
36
+ accept: 'application/json',
37
+ 'x-request-id': '123',
38
+ 'x-correlation-id': '456',
39
+ };
40
+
41
+ beforeEach(() => {
42
+ mockLogger = {
43
+ error: vi.fn(),
44
+ info: vi.fn(),
45
+ debug: vi.fn(),
46
+ warn: vi.fn(),
47
+ } as unknown as Logger;
48
+
49
+ mockHealthCheck1 = vi.fn();
50
+ mockHealthCheck2 = vi.fn();
51
+ mockMetadataFn = vi.fn();
52
+
53
+ // Set default success behavior
54
+ vi.mocked(mockHealthCheck1).mockResolvedValue({
55
+ name: 'test-check-1',
56
+ status: 'UP',
57
+ details: { test: 'details' },
58
+ });
59
+
60
+ vi.mocked(mockHealthCheck2).mockResolvedValue({
61
+ name: 'test-check-2',
62
+ status: 'UP',
63
+ });
64
+
65
+ vi.mocked(mockMetadataFn).mockResolvedValue({ env: 'test' });
66
+
67
+ const defaultStatusConfig: StatusConfig = {
68
+ timeout: 5000,
69
+ checks: [mockHealthCheck1, mockHealthCheck2],
70
+ metadataFn: mockMetadataFn,
71
+ log: mockLogger,
72
+ };
73
+
74
+ handler = new TestStatusHandler({
75
+ logger: mockLogger,
76
+ statusConfig: defaultStatusConfig,
77
+ });
78
+
79
+ // Reset environment variable
80
+ delete process.env.npm_package_version;
81
+ });
82
+
83
+ describe('Basic handler methods', () => {
84
+ it('should return GET method', () => {
85
+ expect(handler.method()).toBe('GET');
86
+ });
87
+
88
+ it('should return correct endpoint', () => {
89
+ expect(handler.endpoint()).toBe('/operations/status');
90
+ });
91
+
92
+ it('should return JWT auth configuration', () => {
93
+ const authConfig = handler.auth();
94
+ expect(authConfig).toEqual([{ type: 'jwt' }]);
95
+ });
96
+ });
97
+
98
+ describe('handler method', () => {
99
+ const mockRequest: StatusRequest = undefined;
100
+
101
+ describe('successful scenarios', () => {
102
+ it('should return OK status when all checks pass', async () => {
103
+ const result = await handler.handler({
104
+ request: mockRequest,
105
+ headers: mockHeaders,
106
+ });
107
+
108
+ expect(result.status).toBe(200);
109
+ expect(result.body.status).toBe('OK');
110
+ expect(result.body.checks).toHaveLength(2);
111
+ expect(result.body.checks[0]).toEqual({
112
+ name: 'test-check-1',
113
+ status: 'UP',
114
+ details: { test: 'details' },
115
+ });
116
+ expect(result.body.checks[1]).toEqual({
117
+ name: 'test-check-2',
118
+ status: 'UP',
119
+ });
120
+ expect(result.body.timestamp).toBeDefined();
121
+ expect(result.body.version).toBe('0.0.0');
122
+ expect(result.body.metadata).toEqual({ env: 'test' });
123
+ });
124
+
125
+ it('should use npm_package_version if available', async () => {
126
+ process.env.npm_package_version = '1.2.3';
127
+
128
+ const result = await handler.handler({
129
+ request: mockRequest,
130
+ headers: mockHeaders,
131
+ });
132
+
133
+ expect(result.body.version).toBe('1.2.3');
134
+ });
135
+
136
+ it('should work without metadata function', async () => {
137
+ const configWithoutMetadata: StatusConfig = {
138
+ timeout: 5000,
139
+ checks: [mockHealthCheck1],
140
+ log: mockLogger,
141
+ };
142
+
143
+ const handlerWithoutMetadata = new TestStatusHandler({
144
+ logger: mockLogger,
145
+ statusConfig: configWithoutMetadata,
146
+ });
147
+
148
+ const result = await handlerWithoutMetadata.handler({
149
+ request: mockRequest,
150
+ headers: mockHeaders,
151
+ });
152
+
153
+ expect(result.status).toBe(200);
154
+ expect(result.body.metadata).toBeUndefined();
155
+ expect(mockMetadataFn).not.toHaveBeenCalled();
156
+ });
157
+
158
+ it('should return unavailable with empty checks array', async () => {
159
+ const configWithNoChecks: StatusConfig = {
160
+ timeout: 5000,
161
+ checks: [],
162
+ log: mockLogger,
163
+ };
164
+
165
+ const handlerWithNoChecks = new TestStatusHandler({
166
+ logger: mockLogger,
167
+ statusConfig: configWithNoChecks,
168
+ });
169
+
170
+ const result = await handlerWithNoChecks.handler({
171
+ request: mockRequest,
172
+ headers: mockHeaders,
173
+ });
174
+
175
+ expect(result.status).toBe(503);
176
+ expect(result.body.status).toBe('Unavailable');
177
+ expect(result.body.checks).toHaveLength(0);
178
+ });
179
+ });
180
+
181
+ describe('partial failure scenarios', () => {
182
+ it('should return Partially Available when some checks fail', async () => {
183
+ vi.mocked(mockHealthCheck1).mockResolvedValue({
184
+ name: 'test-check-1',
185
+ status: 'DOWN',
186
+ message: 'Test failure message',
187
+ details: { error: 'test error' },
188
+ });
189
+
190
+ const result = await handler.handler({
191
+ request: mockRequest,
192
+ headers: mockHeaders,
193
+ });
194
+
195
+ expect(result.status).toBe(200);
196
+ expect(result.body.status).toBe('Partially Available');
197
+ expect(result.body.checks).toHaveLength(2);
198
+ expect(result.body.checks[0].status).toBe('DOWN');
199
+ expect(result.body.checks[1].status).toBe('UP');
200
+
201
+ // Verify error logging
202
+ expect(mockLogger.error).toHaveBeenCalledWith(
203
+ { details: { error: 'test error' } },
204
+ 'Test failure message',
205
+ );
206
+ });
207
+
208
+ it('should log default message when no message provided for failed check', async () => {
209
+ vi.mocked(mockHealthCheck1).mockResolvedValue({
210
+ name: 'test-check-1',
211
+ status: 'DOWN',
212
+ details: { error: 'test error' },
213
+ });
214
+
215
+ await handler.handler({
216
+ request: mockRequest,
217
+ headers: mockHeaders,
218
+ });
219
+
220
+ expect(mockLogger.error).toHaveBeenCalledWith(
221
+ { details: { error: 'test error' } },
222
+ 'Health check for test-check-1 failed, check details',
223
+ );
224
+ });
225
+ });
226
+
227
+ describe('complete failure scenarios', () => {
228
+ it('should return Unavailable when all checks fail', async () => {
229
+ vi.mocked(mockHealthCheck1).mockResolvedValue({
230
+ name: 'test-check-1',
231
+ status: 'DOWN',
232
+ message: 'Check 1 failed',
233
+ });
234
+
235
+ vi.mocked(mockHealthCheck2).mockResolvedValue({
236
+ name: 'test-check-2',
237
+ status: 'DOWN',
238
+ message: 'Check 2 failed',
239
+ });
240
+
241
+ const result = await handler.handler({
242
+ request: mockRequest,
243
+ headers: mockHeaders,
244
+ });
245
+
246
+ expect(result.status).toBe(503);
247
+ expect(result.body.status).toBe('Unavailable');
248
+ expect(result.body.checks).toHaveLength(2);
249
+ expect(result.body.checks[0].status).toBe('DOWN');
250
+ expect(result.body.checks[1].status).toBe('DOWN');
251
+ });
252
+ });
253
+
254
+ describe('timeout scenarios', () => {
255
+ it('should handle timeout and return Unavailable', async () => {
256
+ const slowHealthCheck = vi.fn().mockImplementation(
257
+ () =>
258
+ new Promise((resolve) =>
259
+ setTimeout(
260
+ () =>
261
+ resolve({
262
+ name: 'slow-check',
263
+ status: 'UP',
264
+ }),
265
+ 100,
266
+ ),
267
+ ),
268
+ );
269
+
270
+ const timeoutConfig: StatusConfig = {
271
+ timeout: 50, // Short timeout
272
+ checks: [slowHealthCheck],
273
+ log: mockLogger,
274
+ };
275
+
276
+ const timeoutHandler = new TestStatusHandler({
277
+ logger: mockLogger,
278
+ statusConfig: timeoutConfig,
279
+ });
280
+
281
+ const result = await timeoutHandler.handler({
282
+ request: mockRequest,
283
+ headers: mockHeaders,
284
+ });
285
+
286
+ expect(result.status).toBe(503);
287
+ expect(result.body.status).toBe('Unavailable');
288
+ expect(result.body.checks).toHaveLength(0);
289
+ expect(slowHealthCheck).toHaveBeenCalled();
290
+ });
291
+
292
+ it('should handle non-timeout errors', async () => {
293
+ const errorHealthCheck = vi
294
+ .fn()
295
+ .mockRejectedValue(new Error('Custom error'));
296
+
297
+ const errorConfig: StatusConfig = {
298
+ timeout: 5000,
299
+ checks: [errorHealthCheck],
300
+ log: mockLogger,
301
+ };
302
+
303
+ const errorHandler = new TestStatusHandler({
304
+ logger: mockLogger,
305
+ statusConfig: errorConfig,
306
+ });
307
+
308
+ const result = await errorHandler.handler({
309
+ request: mockRequest,
310
+ headers: mockHeaders,
311
+ });
312
+
313
+ expect(result.status).toBe(503);
314
+ expect(result.body.status).toBe('Unavailable');
315
+ expect(result.body.checks).toHaveLength(0);
316
+ });
317
+
318
+ it('should log timeout errors specifically', async () => {
319
+ // Create a timeout error that matches exactly what the code expects
320
+ const timeoutError = new Error('Timeout');
321
+
322
+ const errorConfig: StatusConfig = {
323
+ timeout: 5000,
324
+ checks: [vi.fn().mockRejectedValue(timeoutError)],
325
+ log: mockLogger,
326
+ };
327
+
328
+ const errorHandler = new TestStatusHandler({
329
+ logger: mockLogger,
330
+ statusConfig: errorConfig,
331
+ });
332
+
333
+ const result = await errorHandler.handler({
334
+ request: mockRequest,
335
+ headers: mockHeaders,
336
+ });
337
+
338
+ expect(result.status).toBe(503);
339
+ expect(result.body.status).toBe('Unavailable');
340
+ expect(mockLogger.error).toHaveBeenCalledWith(
341
+ { err: timeoutError },
342
+ 'Health check timed out',
343
+ );
344
+ });
345
+ });
346
+ });
347
+ });
348
+
349
+ describe('healthCheckCommercetoolsPermissions', () => {
350
+ let mockAuthService: AuthorizationService;
351
+ const projectKey = 'test-project';
352
+ const requiredPermissions = ['manage_payments', 'view_customers'];
353
+
354
+ beforeEach(() => {
355
+ mockAuthService = {
356
+ getAccessToken: vi.fn(),
357
+ } as unknown as AuthorizationService;
358
+ });
359
+
360
+ describe('successful permission checks', () => {
361
+ it('should return UP when manage_project permission is available', async () => {
362
+ const mockToken = {
363
+ access_token: 'test-token',
364
+ token_type: 'Bearer',
365
+ scope: `manage_project:${projectKey} other_scope`,
366
+ expires_in: 3600,
367
+ };
368
+ vi.mocked(mockAuthService.getAccessToken).mockResolvedValue(mockToken);
369
+
370
+ const healthCheck = healthCheckCommercetoolsPermissions({
371
+ ctAuthorizationService: mockAuthService,
372
+ projectKey,
373
+ requiredPermissions,
374
+ });
375
+
376
+ const result = await healthCheck();
377
+
378
+ expect(result).toEqual({
379
+ name: 'commercetools permissions',
380
+ status: 'UP',
381
+ details: {
382
+ scope: mockToken.scope,
383
+ },
384
+ });
385
+ });
386
+
387
+ it('should return UP when all required permissions are available', async () => {
388
+ const mockToken = {
389
+ access_token: 'test-token',
390
+ token_type: 'Bearer',
391
+ scope: `manage_payments:${projectKey} view_customers:${projectKey} other_scope`,
392
+ expires_in: 3600,
393
+ };
394
+ vi.mocked(mockAuthService.getAccessToken).mockResolvedValue(mockToken);
395
+
396
+ const healthCheck = healthCheckCommercetoolsPermissions({
397
+ ctAuthorizationService: mockAuthService,
398
+ projectKey,
399
+ requiredPermissions,
400
+ });
401
+
402
+ const result = await healthCheck();
403
+
404
+ expect(result).toEqual({
405
+ name: 'commercetools permissions',
406
+ status: 'UP',
407
+ details: {
408
+ scope: mockToken.scope,
409
+ },
410
+ });
411
+ });
412
+
413
+ it('should return UP when some required permissions and manage_project are available', async () => {
414
+ const mockToken = {
415
+ access_token: 'test-token',
416
+ token_type: 'Bearer',
417
+ scope: `manage_project:${projectKey} view_customers:${projectKey}`,
418
+ expires_in: 3600,
419
+ };
420
+ vi.mocked(mockAuthService.getAccessToken).mockResolvedValue(mockToken);
421
+
422
+ const healthCheck = healthCheckCommercetoolsPermissions({
423
+ ctAuthorizationService: mockAuthService,
424
+ projectKey,
425
+ requiredPermissions,
426
+ });
427
+
428
+ const result = await healthCheck();
429
+
430
+ expect(result.status).toBe('UP');
431
+ });
432
+ });
433
+
434
+ describe('failed permission checks', () => {
435
+ it('should return DOWN when required permissions are missing', async () => {
436
+ const mockToken = {
437
+ access_token: 'test-token',
438
+ token_type: 'Bearer',
439
+ scope: 'other_scope manage_payments:other-project',
440
+ expires_in: 3600,
441
+ };
442
+ vi.mocked(mockAuthService.getAccessToken).mockResolvedValue(mockToken);
443
+
444
+ const healthCheck = healthCheckCommercetoolsPermissions({
445
+ ctAuthorizationService: mockAuthService,
446
+ projectKey,
447
+ requiredPermissions,
448
+ });
449
+
450
+ const result = await healthCheck();
451
+
452
+ expect(result).toEqual({
453
+ name: 'commercetools permissions',
454
+ status: 'DOWN',
455
+ message: `commercetools permissions are not correct, expected scopes: ${requiredPermissions.join(' ')}, actual scopes: ${mockToken.scope}`,
456
+ details: {
457
+ expectedScopes: requiredPermissions,
458
+ actualScopes: mockToken.scope,
459
+ reason: 'scopes not available',
460
+ },
461
+ });
462
+ });
463
+
464
+ it('should return DOWN when only some required permissions are available', async () => {
465
+ const mockToken = {
466
+ access_token: 'test-token',
467
+ token_type: 'Bearer',
468
+ scope: `manage_payments:${projectKey} other_scope`,
469
+ expires_in: 3600,
470
+ };
471
+ vi.mocked(mockAuthService.getAccessToken).mockResolvedValue(mockToken);
472
+
473
+ const healthCheck = healthCheckCommercetoolsPermissions({
474
+ ctAuthorizationService: mockAuthService,
475
+ projectKey,
476
+ requiredPermissions,
477
+ });
478
+
479
+ const result = await healthCheck();
480
+
481
+ expect(result.status).toBe('DOWN');
482
+ expect(result.message).toContain(
483
+ 'commercetools permissions are not correct',
484
+ );
485
+ });
486
+
487
+ it('should return DOWN when API call fails', async () => {
488
+ const error = new Error('API Error');
489
+ vi.mocked(mockAuthService.getAccessToken).mockRejectedValue(error);
490
+
491
+ const healthCheck = healthCheckCommercetoolsPermissions({
492
+ ctAuthorizationService: mockAuthService,
493
+ projectKey,
494
+ requiredPermissions,
495
+ });
496
+
497
+ const result = await healthCheck();
498
+
499
+ expect(result).toEqual({
500
+ name: 'commercetools permissions',
501
+ status: 'DOWN',
502
+ message: 'Not able to talk with commercetools API',
503
+ details: {
504
+ error,
505
+ },
506
+ });
507
+ });
508
+ });
509
+
510
+ describe('edge cases', () => {
511
+ it('should handle empty required permissions array', async () => {
512
+ const mockToken = {
513
+ access_token: 'test-token',
514
+ token_type: 'Bearer',
515
+ scope: 'some_scope',
516
+ expires_in: 3600,
517
+ };
518
+ vi.mocked(mockAuthService.getAccessToken).mockResolvedValue(mockToken);
519
+
520
+ const healthCheck = healthCheckCommercetoolsPermissions({
521
+ ctAuthorizationService: mockAuthService,
522
+ projectKey,
523
+ requiredPermissions: [],
524
+ });
525
+
526
+ const result = await healthCheck();
527
+
528
+ expect(result.status).toBe('UP');
529
+ });
530
+
531
+ it('should handle scope with extra spaces', async () => {
532
+ const mockToken = {
533
+ access_token: 'test-token',
534
+ token_type: 'Bearer',
535
+ scope: ` manage_payments:${projectKey} view_customers:${projectKey} `,
536
+ expires_in: 3600,
537
+ };
538
+ vi.mocked(mockAuthService.getAccessToken).mockResolvedValue(mockToken);
539
+
540
+ const healthCheck = healthCheckCommercetoolsPermissions({
541
+ ctAuthorizationService: mockAuthService,
542
+ projectKey,
543
+ requiredPermissions,
544
+ });
545
+
546
+ const result = await healthCheck();
547
+
548
+ expect(result.status).toBe('UP');
549
+ });
550
+ });
551
+ });