@mojaloop/sdk-scheme-adapter 24.11.0-csi-1680.0 → 24.11.0-snapshot.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/.grype.yaml +1 -0
- package/.ncurc.yaml +3 -1
- package/.yarn/cache/{@babel-core-npm-7.28.3-fb967e901c-0faded84ed.zip → @babel-core-npm-7.28.4-cb5fd966cc-0593295241.zip} +0 -0
- package/.yarn/cache/{@babel-helpers-npm-7.28.3-8e4849da45-6d39031bf0.zip → @babel-helpers-npm-7.28.4-d9f7567704-5a70a82e19.zip} +0 -0
- package/.yarn/cache/@babel-parser-npm-7.28.4-e1b2cbaf6c-f54c46213e.zip +0 -0
- package/.yarn/cache/@babel-traverse-npm-7.28.4-ffade2903a-c3099364b7.zip +0 -0
- package/.yarn/cache/@babel-types-npm-7.28.4-7f16191818-db50bf257a.zip +0 -0
- package/.yarn/cache/@grpc-proto-loader-npm-0.8.0-b53ddeb647-216813bdca.zip +0 -0
- package/.yarn/cache/{@hapi-hapi-npm-21.4.2-b5f92c52c3-efe9025469.zip → @hapi-hapi-npm-21.4.3-3dc5e89aa0-f23bda02b4.zip} +0 -0
- package/.yarn/cache/@hapi-shot-npm-6.0.2-c6ccc15f52-8715f5759c.zip +0 -0
- package/.yarn/cache/{@hapi-subtext-npm-8.1.0-ea59196b68-3f7bf0c689.zip → @hapi-subtext-npm-8.1.1-8aefc21cfb-9ce8251d5e.zip} +0 -0
- package/.yarn/cache/@jest-transform-npm-30.1.2-14cfbd16d5-aec6c6a46f.zip +0 -0
- package/.yarn/cache/@jridgewell-remapping-npm-2.3.5-df8dacc063-c2bb01856e.zip +0 -0
- package/.yarn/cache/{@mojaloop-central-services-error-handling-npm-13.1.0-07ff108f3f-64e80a9ba2.zip → @mojaloop-central-services-error-handling-npm-13.1.1-990790968e-a5d8a46f3c.zip} +0 -0
- package/.yarn/cache/@mojaloop-central-services-error-handling-npm-13.1.2-680b8f106e-1357eefd24.zip +0 -0
- package/.yarn/cache/@mojaloop-central-services-logger-npm-11.9.3-7669d33c28-58cca19ace.zip +0 -0
- package/.yarn/cache/@mojaloop-central-services-metrics-npm-12.7.1-a82eece473-0aa6374c0e.zip +0 -0
- package/.yarn/cache/{@mojaloop-central-services-shared-npm-18.30.6-d528dafb13-a2f47b26cb.zip → @mojaloop-central-services-shared-npm-18.32.1-82382b67a6-6f8a3ab043.zip} +0 -0
- package/.yarn/cache/{@mojaloop-central-services-shared-npm-18.30.7-291fa1aea3-aee239baa2.zip → @mojaloop-central-services-shared-npm-18.33.0-eee3a0674c-98d4f0e2ce.zip} +0 -0
- package/.yarn/cache/{@mojaloop-event-sdk-npm-14.6.1-a36281071d-5652aa9087.zip → @mojaloop-event-sdk-npm-14.7.0-dfe9fa1933-59adbf133d.zip} +0 -0
- package/.yarn/cache/@mojaloop-ml-number-npm-11.3.0-9858cadff5-8435794709.zip +0 -0
- package/.yarn/cache/{@mojaloop-ml-schema-transformer-lib-npm-2.7.7-ad2e66700a-0325beb4f9.zip → @mojaloop-ml-schema-transformer-lib-npm-2.7.8-270774b6ee-06bb19a304.zip} +0 -0
- package/.yarn/cache/{@mojaloop-sdk-standard-components-npm-19.16.4-59956a0e05-3feb521c69.zip → @mojaloop-sdk-standard-components-npm-19.17.0-0519957b97-95fef19fc4.zip} +0 -0
- package/.yarn/cache/{@redis-bloom-npm-5.8.1-139b45b8e4-f691b1dce2.zip → @redis-bloom-npm-5.8.2-1972c61f30-99ec4f127b.zip} +0 -0
- package/.yarn/cache/{@redis-client-npm-5.8.1-66d46a9ca1-329d76de06.zip → @redis-client-npm-5.8.2-68c2d31768-653ba2d0ef.zip} +0 -0
- package/.yarn/cache/{@redis-json-npm-5.8.1-1374d9e2de-9eabbf9a2c.zip → @redis-json-npm-5.8.2-99129c657f-2877cd93b7.zip} +0 -0
- package/.yarn/cache/{@redis-search-npm-5.8.1-ebc7760a31-a5e12dd2c7.zip → @redis-search-npm-5.8.2-c7014522ef-6cc0499a11.zip} +0 -0
- package/.yarn/cache/{@redis-time-series-npm-5.8.1-1f5e30ede4-c9440ce935.zip → @redis-time-series-npm-5.8.2-94d4c69124-a775817380.zip} +0 -0
- package/.yarn/cache/@rollup-rollup-linux-x64-musl-npm-4.50.2-1d19f7f518-10.zip +0 -0
- package/.yarn/cache/@types-node-npm-24.5.1-e2de7d4e53-1dd21dffe0.zip +0 -0
- package/.yarn/cache/@types-retry-npm-0.12.5-f1986a76a6-3fb6bf9183.zip +0 -0
- package/.yarn/cache/{@typescript-eslint-eslint-plugin-npm-8.39.1-8ad46b0385-446050aa43.zip → @typescript-eslint-eslint-plugin-npm-8.44.0-3a3d745bcf-38d0491d96.zip} +0 -0
- package/.yarn/cache/{@typescript-eslint-parser-npm-8.39.1-e931b25728-ff45ce7635.zip → @typescript-eslint-parser-npm-8.44.0-9be86aa2f8-8c7ddabf46.zip} +0 -0
- package/.yarn/cache/{@typescript-eslint-project-service-npm-8.39.1-f6db73ca22-1970633d1a.zip → @typescript-eslint-project-service-npm-8.44.0-3208cc873e-400b4981e6.zip} +0 -0
- package/.yarn/cache/{@typescript-eslint-scope-manager-npm-8.39.1-bf78e0253c-8874f74790.zip → @typescript-eslint-scope-manager-npm-8.44.0-02a051c9d1-5dae4a8386.zip} +0 -0
- package/.yarn/cache/{@typescript-eslint-tsconfig-utils-npm-8.39.1-e46dac00aa-38c1e19825.zip → @typescript-eslint-tsconfig-utils-npm-8.44.0-de2d92d917-c8535d481d.zip} +0 -0
- package/.yarn/cache/{@typescript-eslint-type-utils-npm-8.39.1-41cbec8085-1195d65970.zip → @typescript-eslint-type-utils-npm-8.44.0-4b4c61deae-513c6d3719.zip} +0 -0
- package/.yarn/cache/@typescript-eslint-types-npm-8.44.0-89c4325651-9e28c95feb.zip +0 -0
- package/.yarn/cache/{@typescript-eslint-typescript-estree-npm-8.39.1-eb0cf5436f-07ed9d7ab4.zip → @typescript-eslint-typescript-estree-npm-8.44.0-122f9245db-e2e579b15c.zip} +0 -0
- package/.yarn/cache/{@typescript-eslint-utils-npm-8.39.1-a6c63e4cf7-39bb105f26.zip → @typescript-eslint-utils-npm-8.44.0-fc612e2915-436e21e3d0.zip} +0 -0
- package/.yarn/cache/{@typescript-eslint-visitor-keys-npm-8.39.1-d0b0654c5b-6d4e4d0b19.zip → @typescript-eslint-visitor-keys-npm-8.44.0-131d4d0e8f-09b008b14f.zip} +0 -0
- package/.yarn/cache/{babel-jest-npm-30.0.5-8bced40b9f-39a36b8648.zip → babel-jest-npm-30.1.2-2d68b3440b-9697119fe4.zip} +0 -0
- package/.yarn/cache/bignumber.js-npm-9.3.1-d784181dd0-1be0372bf0.zip +0 -0
- package/.yarn/cache/dotenv-npm-17.2.2-f2cdf74d0a-258210c403.zip +0 -0
- package/.yarn/cache/iconv-lite-npm-0.7.0-89105876e3-5bfc897fed.zip +0 -0
- package/.yarn/cache/{jest-haste-map-npm-30.0.5-ff2b66456e-3539359589.zip → jest-haste-map-npm-30.1.0-8189548adb-bd39053fe1.zip} +0 -0
- package/.yarn/cache/{jest-worker-npm-30.0.5-dcae728924-04d9a58ddb.zip → jest-worker-npm-30.1.0-b4a01545e6-cc09d2ce86.zip} +0 -0
- package/.yarn/cache/joi-npm-18.0.1-f286682573-d8e391c0e9.zip +0 -0
- package/.yarn/cache/{openapi-backend-npm-5.14.0-231377503b-1bd3e6cb71.zip → openapi-backend-npm-5.15.0-b7b193973a-f7bdc40ac6.zip} +0 -0
- package/.yarn/cache/{protobufjs-npm-7.5.3-a54566937a-3e412d2e2f.zip → protobufjs-npm-7.5.4-4d6f681551-88d677bb6f.zip} +0 -0
- package/.yarn/cache/{raw-body-npm-3.0.0-cd8403b401-2443429bbb.zip → raw-body-npm-3.0.1-cbb0b09e07-3cc63e1541.zip} +0 -0
- package/.yarn/cache/{redis-npm-5.8.1-201a0a72a3-26d97c6ddf.zip → redis-npm-5.8.2-9c493c0c47-a7635cedf2.zip} +0 -0
- package/.yarn/cache/{ts-jest-npm-29.4.1-ab76d85d32-6aed48232c.zip → ts-jest-npm-29.4.2-1fc50073bc-09494224db.zip} +0 -0
- package/.yarn/cache/{undici-types-npm-7.10.0-cd8324b9eb-1f3fe77793.zip → undici-types-npm-7.12.0-af0c725921-4a0f927c98.zip} +0 -0
- package/.yarn/install-state.gz +0 -0
- package/.yarn/releases/{yarn-4.9.2.cjs → yarn-4.9.4.cjs} +358 -358
- package/.yarnrc.yml +1 -1
- package/CHANGELOG.md +37 -0
- package/modules/api-svc/package.json +15 -13
- package/modules/api-svc/src/InboundServer/handlers.js +26 -8
- package/modules/api-svc/src/config.js +17 -1
- package/modules/api-svc/src/lib/model/InboundTransfersModel.js +146 -6
- package/modules/api-svc/src/lib/model/OutboundTransfersModel.js +1 -1
- package/modules/api-svc/test/__mocks__/redis.js +5 -2
- package/modules/api-svc/test/unit/inboundApi/handlers-iso20022.test.js +3 -1
- package/modules/api-svc/test/unit/inboundApi/handlers.test.js +4 -2
- package/modules/api-svc/test/unit/lib/model/InboundTransfersModel.test.js +481 -117
- package/modules/outbound-command-event-handler/package.json +8 -8
- package/modules/outbound-domain-event-handler/package.json +7 -7
- package/modules/private-shared-lib/package.json +7 -7
- package/package.json +6 -6
- package/{sbom-v24.10.8.csv → sbom-v24.10.11.csv} +224 -224
- package/.yarn/cache/@grpc-proto-loader-npm-0.7.15-889e15aec1-2e2b33ace8.zip +0 -0
- package/.yarn/cache/@hapi-hapi-npm-21.4.0-2644a983d1-d49ae44142.zip +0 -0
- package/.yarn/cache/@hapi-shot-npm-6.0.1-2553675f4f-6eb387f9c6.zip +0 -0
- package/.yarn/cache/@hapi-topo-npm-5.1.0-5e0b776809-084bfa6470.zip +0 -0
- package/.yarn/cache/@jest-transform-npm-30.0.5-90874ed0b8-2b3e0bc39a.zip +0 -0
- package/.yarn/cache/@mojaloop-central-services-metrics-npm-12.6.0-6353d00803-e55c70c0b1.zip +0 -0
- package/.yarn/cache/@rollup-rollup-linux-x64-musl-npm-4.45.1-255fc04506-10.zip +0 -0
- package/.yarn/cache/@sideway-address-npm-4.1.5-a3852745c8-c4c73ac033.zip +0 -0
- package/.yarn/cache/@sideway-formula-npm-3.0.1-ee371b2ddf-8d3ee7f80d.zip +0 -0
- package/.yarn/cache/@sideway-pinpoint-npm-2.0.0-66d94e687e-1ed2180012.zip +0 -0
- package/.yarn/cache/@types-node-npm-24.2.1-00ab09acd1-cdfa7b30b2.zip +0 -0
- package/.yarn/cache/@typescript-eslint-eslint-plugin-npm-8.32.1-4a9716e105-442205dd4e.zip +0 -0
- package/.yarn/cache/@typescript-eslint-parser-npm-8.32.1-4842816d93-3c2ab90fec.zip +0 -0
- package/.yarn/cache/@typescript-eslint-scope-manager-npm-8.32.1-7708347a5f-f81f71bd88.zip +0 -0
- package/.yarn/cache/@typescript-eslint-type-utils-npm-8.32.1-e98f19d598-e50a6f2a16.zip +0 -0
- package/.yarn/cache/@typescript-eslint-types-npm-8.32.1-ded19751b6-3a310e4baf.zip +0 -0
- package/.yarn/cache/@typescript-eslint-types-npm-8.39.1-8cea531133-8013f4f48a.zip +0 -0
- package/.yarn/cache/@typescript-eslint-typescript-estree-npm-8.32.1-5eacb17d12-8b956ce05b.zip +0 -0
- package/.yarn/cache/@typescript-eslint-utils-npm-8.32.1-8a5bff5552-9383cea185.zip +0 -0
- package/.yarn/cache/@typescript-eslint-visitor-keys-npm-8.32.1-780bd4dae9-a1cbfbdac8.zip +0 -0
- package/.yarn/cache/dotenv-npm-17.2.0-4ee4b4bbd1-73d57d7ed8.zip +0 -0
- package/.yarn/cache/dotenv-npm-17.2.1-33fbb0afbc-8fde672d1c.zip +0 -0
- package/.yarn/cache/joi-npm-17.13.3-866dad5bc8-4c150db0c8.zip +0 -0
- package/.yarn/cache/joi-npm-18.0.0-1ebac7eadf-568004d69f.zip +0 -0
- package/.yarn/cache/openapi-backend-npm-5.13.0-03ae1ecf2c-a8b1d0d167.zip +0 -0
- package/.yarn/cache/yaml-npm-2.8.0-01747dd315-7d4bd9c10d.zip +0 -0
- package/audit/k6/package.json +0 -17
- package/docker/k666/package.json +0 -17
|
@@ -295,44 +295,46 @@ describe('inboundModel', () => {
|
|
|
295
295
|
MojaloopRequests.__putTransfersError.mockClear();
|
|
296
296
|
BackendRequests.__postTransfers = jest.fn().mockReturnValue(Promise.resolve({}));
|
|
297
297
|
MojaloopRequests.__putTransfers = jest.fn().mockReturnValue(Promise.resolve({
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
298
|
+
originalRequest: {
|
|
299
|
+
headers: {},
|
|
300
|
+
body: {},
|
|
301
|
+
}
|
|
302
302
|
}));
|
|
303
303
|
|
|
304
304
|
cache = new Cache({
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
305
|
+
cacheUrl: 'redis://dummy:1234',
|
|
306
|
+
logger,
|
|
307
|
+
unsubscribeTimeoutMs: 5000
|
|
308
308
|
});
|
|
309
309
|
await cache.connect();
|
|
310
310
|
});
|
|
311
311
|
|
|
312
312
|
afterEach(async () => {
|
|
313
313
|
await cache.disconnect();
|
|
314
|
+
jest.clearAllTimers();
|
|
315
|
+
jest.useRealTimers();
|
|
314
316
|
});
|
|
315
317
|
|
|
316
318
|
test('fail on quote `expiration` deadline.', async () => {
|
|
317
319
|
const TRANSFER_ID = 'fake-transfer-id';
|
|
318
320
|
const model = new Model({
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
321
|
+
...config,
|
|
322
|
+
cache,
|
|
323
|
+
logger,
|
|
324
|
+
rejectTransfersOnExpiredQuotes: true,
|
|
323
325
|
});
|
|
324
326
|
cache.set(`transferModel_in_${TRANSFER_ID}`, {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
327
|
+
transferId: TRANSFER_ID,
|
|
328
|
+
quote: {
|
|
329
|
+
mojaloopResponse: {
|
|
330
|
+
expiration: new Date(new Date().getTime() - 1000).toISOString(),
|
|
330
331
|
}
|
|
332
|
+
}
|
|
331
333
|
});
|
|
332
334
|
const args = {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
335
|
+
body: {
|
|
336
|
+
transferId: TRANSFER_ID,
|
|
337
|
+
}
|
|
336
338
|
};
|
|
337
339
|
|
|
338
340
|
await model.prepareTransfer(args, mockArgs.fspId);
|
|
@@ -351,9 +353,9 @@ describe('inboundModel', () => {
|
|
|
351
353
|
BackendRequests.__getTransfers = jest.fn().mockReturnValue(Promise.resolve(backendResponse));
|
|
352
354
|
|
|
353
355
|
const model = new Model({
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
356
|
+
...config,
|
|
357
|
+
cache,
|
|
358
|
+
logger,
|
|
357
359
|
});
|
|
358
360
|
|
|
359
361
|
await model.getTransfer(TRANSFER_ID, mockArgs.fspId);
|
|
@@ -373,9 +375,9 @@ describe('inboundModel', () => {
|
|
|
373
375
|
BackendRequests.__getTransfers = jest.fn().mockReturnValue(Promise.resolve(backendResponse));
|
|
374
376
|
|
|
375
377
|
const model = new Model({
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
378
|
+
...config,
|
|
379
|
+
cache,
|
|
380
|
+
logger,
|
|
379
381
|
});
|
|
380
382
|
|
|
381
383
|
await model.getTransfer(TRANSFER_ID, mockArgs.fspId);
|
|
@@ -390,18 +392,18 @@ describe('inboundModel', () => {
|
|
|
390
392
|
const TRANSFER_ID = 'fake-transfer-id';
|
|
391
393
|
|
|
392
394
|
BackendRequests.__getTransfers = jest.fn().mockReturnValue(
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
395
|
+
Promise.reject(new HTTPResponseError({
|
|
396
|
+
res: {
|
|
397
|
+
data: {
|
|
398
|
+
statusCode: '3208'
|
|
399
|
+
},
|
|
400
|
+
}
|
|
401
|
+
})));
|
|
400
402
|
|
|
401
403
|
const model = new Model({
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
404
|
+
...config,
|
|
405
|
+
cache,
|
|
406
|
+
logger,
|
|
405
407
|
});
|
|
406
408
|
|
|
407
409
|
await model.getTransfer(TRANSFER_ID, mockArgs.fspId);
|
|
@@ -415,22 +417,22 @@ describe('inboundModel', () => {
|
|
|
415
417
|
test('fail on transfer without quote.', async () => {
|
|
416
418
|
const TRANSFER_ID = 'without_quote-transfer-id';
|
|
417
419
|
const args = {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
420
|
+
body: {
|
|
421
|
+
transferId: TRANSFER_ID,
|
|
422
|
+
amount: {
|
|
423
|
+
currency: 'USD',
|
|
424
|
+
amount: 20.13
|
|
425
|
+
},
|
|
426
|
+
ilpPacket: 'mockBase64encodedIlpPacket',
|
|
427
|
+
condition: 'mockGeneratedCondition'
|
|
428
|
+
}
|
|
427
429
|
};
|
|
428
430
|
|
|
429
431
|
const model = new Model({
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
432
|
+
...config,
|
|
433
|
+
cache,
|
|
434
|
+
logger,
|
|
435
|
+
allowTransferWithoutQuote: false,
|
|
434
436
|
});
|
|
435
437
|
|
|
436
438
|
await model.prepareTransfer(args, mockArgs.fspId);
|
|
@@ -448,38 +450,38 @@ describe('inboundModel', () => {
|
|
|
448
450
|
|
|
449
451
|
// mock response from dfsp acting as payee
|
|
450
452
|
BackendRequests.__postTransfers = jest.fn().mockReturnValueOnce(Promise.resolve({
|
|
451
|
-
|
|
452
|
-
|
|
453
|
+
homeTransactionId: HOME_TRANSACTION_ID,
|
|
454
|
+
transferId: TRANSFER_ID
|
|
453
455
|
}));
|
|
454
456
|
|
|
455
457
|
const args = {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
458
|
+
body: {
|
|
459
|
+
transferId: TRANSFER_ID,
|
|
460
|
+
amount: {
|
|
461
|
+
currency: 'USD',
|
|
462
|
+
amount: 20.13
|
|
463
|
+
},
|
|
464
|
+
ilpPacket: 'mockBase64encodedIlpPacket',
|
|
465
|
+
condition: 'mockGeneratedCondition'
|
|
466
|
+
}
|
|
465
467
|
};
|
|
466
468
|
|
|
467
469
|
const model = new Model({
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
470
|
+
...config,
|
|
471
|
+
cache,
|
|
472
|
+
logger,
|
|
473
|
+
checkIlp: false,
|
|
474
|
+
rejectTransfersOnExpiredQuotes: false
|
|
473
475
|
});
|
|
474
476
|
|
|
475
477
|
cache.set(`transferModel_in_${TRANSFER_ID}`, {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
}
|
|
478
|
+
transferId: TRANSFER_ID,
|
|
479
|
+
quote: {
|
|
480
|
+
fulfilment: 'mockFulfilment',
|
|
481
|
+
mojaloopResponse: {
|
|
482
|
+
condition: 'mockCondition',
|
|
482
483
|
}
|
|
484
|
+
}
|
|
483
485
|
});
|
|
484
486
|
|
|
485
487
|
await model.prepareTransfer(args, mockArgs.fspId);
|
|
@@ -488,36 +490,36 @@ describe('inboundModel', () => {
|
|
|
488
490
|
expect(BackendRequests.__postTransfers).toHaveBeenCalledTimes(1);
|
|
489
491
|
expect(MojaloopRequests.__putTransfers).toHaveBeenCalledTimes(1);
|
|
490
492
|
expect((await cache.get(`transferModel_in_${TRANSFER_ID}`)).homeTransactionId)
|
|
491
|
-
|
|
493
|
+
.toEqual(HOME_TRANSACTION_ID);
|
|
492
494
|
});
|
|
493
495
|
|
|
494
496
|
test('pass on transfer without quote.', async () => {
|
|
495
497
|
const TRANSFER_ID = 'without_quote-transfer-id';
|
|
496
498
|
cache.set(`transferModel_in_${TRANSFER_ID}`, {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
499
|
+
fulfilment: '',
|
|
500
|
+
mojaloopResponse: {
|
|
501
|
+
response: ''
|
|
502
|
+
},
|
|
503
|
+
quote: null
|
|
502
504
|
});
|
|
503
505
|
|
|
504
506
|
const args = {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
507
|
+
body: {
|
|
508
|
+
transferId: TRANSFER_ID,
|
|
509
|
+
amount: {
|
|
510
|
+
currency: 'USD',
|
|
511
|
+
amount: 20.13
|
|
512
|
+
},
|
|
513
|
+
ilpPacket: 'mockBase64encodedIlpPacket',
|
|
514
|
+
condition: 'mockGeneratedCondition'
|
|
515
|
+
}
|
|
514
516
|
};
|
|
515
517
|
|
|
516
518
|
const model = new Model({
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
519
|
+
...config,
|
|
520
|
+
cache,
|
|
521
|
+
logger,
|
|
522
|
+
allowTransferWithoutQuote: true,
|
|
521
523
|
});
|
|
522
524
|
|
|
523
525
|
await model.prepareTransfer(args, mockArgs.fspId);
|
|
@@ -533,36 +535,36 @@ describe('inboundModel', () => {
|
|
|
533
535
|
shared.mojaloopPrepareToInternalTransfer = jest.fn().mockReturnValueOnce({});
|
|
534
536
|
|
|
535
537
|
cache.set(`transferModel_in_${transactionId}`, {
|
|
536
|
-
|
|
538
|
+
fulfilment: '',
|
|
539
|
+
mojaloopResponse: {
|
|
540
|
+
response: ''
|
|
541
|
+
},
|
|
542
|
+
quote: {
|
|
543
|
+
fulfilment: 'mockFulfilment',
|
|
537
544
|
mojaloopResponse: {
|
|
538
|
-
|
|
539
|
-
},
|
|
540
|
-
quote: {
|
|
541
|
-
fulfilment: 'mockFulfilment',
|
|
542
|
-
mojaloopResponse: {
|
|
543
|
-
condition: 'mockCondition',
|
|
544
|
-
}
|
|
545
|
+
condition: 'mockCondition',
|
|
545
546
|
}
|
|
547
|
+
}
|
|
546
548
|
});
|
|
547
549
|
|
|
548
550
|
const args = {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
551
|
+
body: {
|
|
552
|
+
transferId: TRANSFER_ID,
|
|
553
|
+
amount: {
|
|
554
|
+
currency: 'USD',
|
|
555
|
+
amount: 20.13
|
|
556
|
+
},
|
|
557
|
+
ilpPacket: 'mockIlpPacket',
|
|
558
|
+
condition: 'mockGeneratedCondition'
|
|
559
|
+
}
|
|
558
560
|
};
|
|
559
561
|
|
|
560
562
|
const model = new Model({
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
563
|
+
...config,
|
|
564
|
+
cache,
|
|
565
|
+
logger,
|
|
566
|
+
allowDifferentTransferTransactionId: true,
|
|
567
|
+
checkIlp: false,
|
|
566
568
|
});
|
|
567
569
|
|
|
568
570
|
await model.prepareTransfer(args, mockArgs.fspId);
|
|
@@ -571,6 +573,173 @@ describe('inboundModel', () => {
|
|
|
571
573
|
expect(BackendRequests.__postTransfers).toHaveBeenCalledTimes(1);
|
|
572
574
|
expect(MojaloopRequests.__putTransfers).toHaveBeenCalledTimes(1);
|
|
573
575
|
});
|
|
576
|
+
|
|
577
|
+
// --- Additional tests for patch notification retry logic ---
|
|
578
|
+
|
|
579
|
+
test('should getTransfer if patch notification is not called within grace time', async () => {
|
|
580
|
+
jest.useFakeTimers();
|
|
581
|
+
const TRANSFER_ID = 'patch-notify-transfer-id';
|
|
582
|
+
const PATCH_GRACE_MS = 100;
|
|
583
|
+
const backendResponse = JSON.parse(JSON.stringify(getTransfersBackendResponse));
|
|
584
|
+
backendResponse.to.fspId = config.dfspId;
|
|
585
|
+
BackendRequests.__getTransfers = jest.fn().mockResolvedValue(backendResponse);
|
|
586
|
+
// Mock shared.mojaloopPrepareToInternalTransfer to avoid undefined property errors
|
|
587
|
+
shared.mojaloopPrepareToInternalTransfer = jest.fn().mockReturnValue({
|
|
588
|
+
transferId: TRANSFER_ID,
|
|
589
|
+
amount: { currency: 'USD', amount: 20.13 },
|
|
590
|
+
ilpPacket: 'mockBase64encodedIlpPacket',
|
|
591
|
+
condition: 'mockCondition'
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
// Mock cache.set for patch notification logic to avoid real Redis calls and allow timer logic to proceed
|
|
595
|
+
jest.spyOn(cache, 'set').mockImplementation(async (key, value, ttl) => {
|
|
596
|
+
// For patchNotificationSent_ keys, simulate not notified yet
|
|
597
|
+
if (key.startsWith('patchNotificationSent_')) {
|
|
598
|
+
return true;
|
|
599
|
+
}
|
|
600
|
+
// For other keys, fallback to original set
|
|
601
|
+
return Cache.prototype.set.call(cache, key, value, ttl);
|
|
602
|
+
});
|
|
603
|
+
// Also mock cache.get for patch notification logic to always return false (not notified)
|
|
604
|
+
jest.spyOn(cache, 'get').mockImplementation(async (key) => {
|
|
605
|
+
if (key.startsWith('patchNotificationSent_')) {
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
return Cache.prototype.get.call(cache, key);
|
|
609
|
+
});
|
|
610
|
+
// Mock subscribeToOneMessageWithTimer to immediately resolve with a message to simulate successful notification
|
|
611
|
+
jest.spyOn(cache, 'subscribeToOneMessageWithTimer').mockImplementation(async () => {
|
|
612
|
+
// Simulate a successful message received
|
|
613
|
+
return { data: { transferState: 'COMMITTED' } };
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
// Mock backendRequests.putTransfersNotification to return 200
|
|
617
|
+
jest.spyOn(BackendRequests, '__putTransfersNotification').mockResolvedValue({ status: 200 });
|
|
618
|
+
|
|
619
|
+
// Prepare model with patch notification grace time enabled
|
|
620
|
+
const model = new Model({
|
|
621
|
+
...config,
|
|
622
|
+
cache,
|
|
623
|
+
logger,
|
|
624
|
+
patchNotificationGraceTimeMs: PATCH_GRACE_MS,
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
// Mock _load to avoid actual cache access
|
|
628
|
+
jest.spyOn(model, '_load').mockImplementation(async (transferId) => {
|
|
629
|
+
return {
|
|
630
|
+
transferId,
|
|
631
|
+
quote: {
|
|
632
|
+
fulfilment: 'mockFulfilment',
|
|
633
|
+
mojaloopResponse: {
|
|
634
|
+
condition: 'mockCondition',
|
|
635
|
+
expiration: new Date(Date.now() + 10000).toISOString(),
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
// Mock _save to avoid actual cache writes
|
|
642
|
+
jest.spyOn(model, '_save').mockImplementation(async () => {});
|
|
643
|
+
|
|
644
|
+
// Set up cache with quote for transfer
|
|
645
|
+
cache.set(`transferModel_in_${TRANSFER_ID}`, {
|
|
646
|
+
transferId: TRANSFER_ID,
|
|
647
|
+
quote: {
|
|
648
|
+
fulfilment: 'mockFulfilment',
|
|
649
|
+
mojaloopResponse: {
|
|
650
|
+
condition: 'mockCondition',
|
|
651
|
+
expiration: new Date(Date.now() + 10000).toISOString(),
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
const args = {
|
|
657
|
+
body: {
|
|
658
|
+
transferId: TRANSFER_ID,
|
|
659
|
+
amount: {
|
|
660
|
+
currency: 'USD',
|
|
661
|
+
amount: 20.13
|
|
662
|
+
},
|
|
663
|
+
ilpPacket: 'mockBase64encodedIlpPacket',
|
|
664
|
+
condition: 'mockCondition'
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
|
|
668
|
+
// Spy on getTransfer to check if it's called by timer
|
|
669
|
+
const getTransfersSpy = jest.spyOn(model._mojaloopRequests, 'getTransfers').mockResolvedValue();
|
|
670
|
+
|
|
671
|
+
await model.prepareTransfer(args, mockArgs.fspId);
|
|
672
|
+
|
|
673
|
+
// Advance timers to simulate PATCH_GRACE_MS passing
|
|
674
|
+
jest.advanceTimersByTime(PATCH_GRACE_MS + 50);
|
|
675
|
+
|
|
676
|
+
// Wait for any pending promises
|
|
677
|
+
await Promise.resolve();
|
|
678
|
+
expect(getTransfersSpy).toHaveBeenCalled();
|
|
679
|
+
|
|
680
|
+
expect(getTransfersSpy).toHaveBeenCalledWith(TRANSFER_ID, mockArgs.fspId, undefined);
|
|
681
|
+
getTransfersSpy.mockRestore();
|
|
682
|
+
jest.useRealTimers();
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
test('should not set up patch notification timer if patchNotificationGraceTimeMs is 0', async () => {
|
|
686
|
+
const TRANSFER_ID = 'patch-notify-transfer-id-no-timer';
|
|
687
|
+
shared.mojaloopPrepareToInternalTransfer = jest.fn().mockReturnValue({
|
|
688
|
+
transferId: TRANSFER_ID,
|
|
689
|
+
amount: { currency: 'USD', amount: 20.13 },
|
|
690
|
+
ilpPacket: 'mockBase64encodedIlpPacket',
|
|
691
|
+
condition: 'mockCondition'
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
const model = new Model({
|
|
695
|
+
...config,
|
|
696
|
+
cache,
|
|
697
|
+
logger,
|
|
698
|
+
patchNotificationGraceTimeMs: 0,
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
jest.spyOn(model._mojaloopRequests, 'getTransfers');
|
|
702
|
+
jest.spyOn(model, '_load').mockImplementation(async (transferId) => {
|
|
703
|
+
return {
|
|
704
|
+
transferId,
|
|
705
|
+
quote: {
|
|
706
|
+
fulfilment: 'mockFulfilment',
|
|
707
|
+
mojaloopResponse: {
|
|
708
|
+
condition: 'mockCondition',
|
|
709
|
+
expiration: new Date(Date.now() + 10000).toISOString(),
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
});
|
|
714
|
+
jest.spyOn(model, '_save').mockImplementation(async () => {});
|
|
715
|
+
|
|
716
|
+
cache.set(`transferModel_in_${TRANSFER_ID}`, {
|
|
717
|
+
transferId: TRANSFER_ID,
|
|
718
|
+
quote: {
|
|
719
|
+
fulfilment: 'mockFulfilment',
|
|
720
|
+
mojaloopResponse: {
|
|
721
|
+
condition: 'mockCondition',
|
|
722
|
+
expiration: new Date(Date.now() + 10000).toISOString(),
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
const args = {
|
|
728
|
+
body: {
|
|
729
|
+
transferId: TRANSFER_ID,
|
|
730
|
+
amount: {
|
|
731
|
+
currency: 'USD',
|
|
732
|
+
amount: 20.13
|
|
733
|
+
},
|
|
734
|
+
ilpPacket: 'mockBase64encodedIlpPacket',
|
|
735
|
+
condition: 'mockCondition'
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
await model.prepareTransfer(args, mockArgs.fspId);
|
|
740
|
+
// getTransfers should not be called at all
|
|
741
|
+
expect(model._mojaloopRequests.getTransfers).not.toHaveBeenCalled();
|
|
742
|
+
});
|
|
574
743
|
});
|
|
575
744
|
|
|
576
745
|
describe('prepareBulkTransfer:', () => {
|
|
@@ -774,7 +943,7 @@ describe('inboundModel', () => {
|
|
|
774
943
|
});
|
|
775
944
|
|
|
776
945
|
test('sends notification to fsp backend', async () => {
|
|
777
|
-
BackendRequests.__putTransfersNotification = jest.fn().mockReturnValue(Promise.resolve({}));
|
|
946
|
+
BackendRequests.__putTransfersNotification = jest.fn().mockReturnValue(Promise.resolve({ status: 200 }));
|
|
778
947
|
const notif = JSON.parse(JSON.stringify(notificationToPayee));
|
|
779
948
|
|
|
780
949
|
const expectedRequest = {
|
|
@@ -796,7 +965,7 @@ describe('inboundModel', () => {
|
|
|
796
965
|
});
|
|
797
966
|
|
|
798
967
|
test('sends ABORTED notification to fsp backend', async () => {
|
|
799
|
-
BackendRequests.__putTransfersNotification = jest.fn().mockReturnValue(Promise.resolve({}));
|
|
968
|
+
BackendRequests.__putTransfersNotification = jest.fn().mockReturnValue(Promise.resolve({ status: 200 }));
|
|
800
969
|
const notif = JSON.parse(JSON.stringify(notificationAbortedToPayee));
|
|
801
970
|
|
|
802
971
|
const expectedRequest = {
|
|
@@ -818,7 +987,7 @@ describe('inboundModel', () => {
|
|
|
818
987
|
});
|
|
819
988
|
|
|
820
989
|
test('sends RESERVED notification to fsp backend', async () => {
|
|
821
|
-
BackendRequests.__putTransfersNotification = jest.fn().mockReturnValue(Promise.resolve({}));
|
|
990
|
+
BackendRequests.__putTransfersNotification = jest.fn().mockReturnValue(Promise.resolve({ status: 200 }));
|
|
822
991
|
const notif = JSON.parse(JSON.stringify(notificationReservedToPayee));
|
|
823
992
|
|
|
824
993
|
const expectedRequest = {
|
|
@@ -840,6 +1009,50 @@ describe('inboundModel', () => {
|
|
|
840
1009
|
expect(call[1]).toEqual(transferId);
|
|
841
1010
|
});
|
|
842
1011
|
|
|
1012
|
+
test('retries notification to fsp backend on failure', async () => {
|
|
1013
|
+
// Simulate failure for first 2 attempts, then success
|
|
1014
|
+
const mockFn = jest.fn()
|
|
1015
|
+
.mockRejectedValueOnce(new Error('fail1'))
|
|
1016
|
+
.mockRejectedValueOnce(new Error('fail2'))
|
|
1017
|
+
.mockResolvedValue({ status: 200 });
|
|
1018
|
+
BackendRequests.__putTransfersNotification = mockFn;
|
|
1019
|
+
|
|
1020
|
+
const notif = JSON.parse(JSON.stringify(notificationToPayee));
|
|
1021
|
+
const model = new Model({
|
|
1022
|
+
...config,
|
|
1023
|
+
cache,
|
|
1024
|
+
logger,
|
|
1025
|
+
backendRequestRetry: {
|
|
1026
|
+
enabled: true,
|
|
1027
|
+
maxRetries: 3,
|
|
1028
|
+
retryDelayMs: 10,
|
|
1029
|
+
maxRetryDelayMs: 20,
|
|
1030
|
+
backoffFactor: 1
|
|
1031
|
+
}
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
await model.sendNotificationToPayee(notif.data, transferId);
|
|
1035
|
+
expect(BackendRequests.__putTransfersNotification).toHaveBeenCalledTimes(3);
|
|
1036
|
+
});
|
|
1037
|
+
|
|
1038
|
+
test('does not retry notification to fsp backend if disabled', async () => {
|
|
1039
|
+
const mockFn = jest.fn().mockResolvedValue({ status: 200 });
|
|
1040
|
+
BackendRequests.__putTransfersNotification = mockFn;
|
|
1041
|
+
|
|
1042
|
+
const notif = JSON.parse(JSON.stringify(notificationToPayee));
|
|
1043
|
+
const model = new Model({
|
|
1044
|
+
...config,
|
|
1045
|
+
cache,
|
|
1046
|
+
logger,
|
|
1047
|
+
backendRequestRetry: {
|
|
1048
|
+
enabled: false
|
|
1049
|
+
}
|
|
1050
|
+
});
|
|
1051
|
+
|
|
1052
|
+
await model.sendNotificationToPayee(notif.data, transferId);
|
|
1053
|
+
expect(BackendRequests.__putTransfersNotification).toHaveBeenCalledTimes(1);
|
|
1054
|
+
});
|
|
1055
|
+
|
|
843
1056
|
});
|
|
844
1057
|
|
|
845
1058
|
describe('sendFxPutNotificationToBackend:', () => {
|
|
@@ -860,7 +1073,7 @@ describe('inboundModel', () => {
|
|
|
860
1073
|
});
|
|
861
1074
|
|
|
862
1075
|
test('sends notification to fsp backend', async () => {
|
|
863
|
-
BackendRequests.__putFxTransfersNotification = jest.fn().mockReturnValue(Promise.resolve({}));
|
|
1076
|
+
BackendRequests.__putFxTransfersNotification = jest.fn().mockReturnValue(Promise.resolve({ status: 200 }));
|
|
864
1077
|
const notif = JSON.parse(JSON.stringify(fxNotificationToBackend));
|
|
865
1078
|
|
|
866
1079
|
const model = new Model({
|
|
@@ -878,7 +1091,7 @@ describe('inboundModel', () => {
|
|
|
878
1091
|
});
|
|
879
1092
|
|
|
880
1093
|
test('sends ABORTED notification to fsp backend', async () => {
|
|
881
|
-
BackendRequests.__putFxTransfersNotification = jest.fn().mockReturnValue(Promise.resolve({}));
|
|
1094
|
+
BackendRequests.__putFxTransfersNotification = jest.fn().mockReturnValue(Promise.resolve({ status: 200 }));
|
|
882
1095
|
const notif = JSON.parse(JSON.stringify(fxNotificationAbortedToBackend));
|
|
883
1096
|
|
|
884
1097
|
const model = new Model({
|
|
@@ -896,7 +1109,7 @@ describe('inboundModel', () => {
|
|
|
896
1109
|
});
|
|
897
1110
|
|
|
898
1111
|
test('sends RESERVED notification to fsp backend', async () => {
|
|
899
|
-
BackendRequests.__putFxTransfersNotification = jest.fn().mockReturnValue(Promise.resolve({}));
|
|
1112
|
+
BackendRequests.__putFxTransfersNotification = jest.fn().mockReturnValue(Promise.resolve({ status: 200 }));
|
|
900
1113
|
const notif = JSON.parse(JSON.stringify(fxNotificationReservedToBackend));
|
|
901
1114
|
|
|
902
1115
|
const model = new Model({
|
|
@@ -913,6 +1126,157 @@ describe('inboundModel', () => {
|
|
|
913
1126
|
expect(call[1]).toEqual(conversionId);
|
|
914
1127
|
});
|
|
915
1128
|
|
|
1129
|
+
test('retries notification to backend on failure for sendFxPutNotificationToBackend if enabled', async () => {
|
|
1130
|
+
// Simulate failure for first 2 attempts, then success
|
|
1131
|
+
const mockFn = jest.fn()
|
|
1132
|
+
.mockRejectedValueOnce(new Error('fail1'))
|
|
1133
|
+
.mockRejectedValueOnce(new Error('fail2'))
|
|
1134
|
+
.mockResolvedValue({ status: 200 });
|
|
1135
|
+
BackendRequests.__putFxTransfersNotification = mockFn;
|
|
1136
|
+
|
|
1137
|
+
const notif = JSON.parse(JSON.stringify(fxNotificationToBackend));
|
|
1138
|
+
const model = new Model({
|
|
1139
|
+
...config,
|
|
1140
|
+
cache,
|
|
1141
|
+
logger,
|
|
1142
|
+
backendRequestRetry: {
|
|
1143
|
+
enabled: true,
|
|
1144
|
+
maxRetries: 3,
|
|
1145
|
+
retryDelayMs: 10,
|
|
1146
|
+
maxRetryDelayMs: 20,
|
|
1147
|
+
backoffFactor: 1
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
model.saveFxState = jest.fn().mockReturnValue(Promise.resolve({}));
|
|
1151
|
+
|
|
1152
|
+
await model.sendFxPutNotificationToBackend(notif.data, conversionId);
|
|
1153
|
+
expect(BackendRequests.__putFxTransfersNotification).toHaveBeenCalledTimes(3);
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
test('does not retry notification to backend for sendFxPutNotificationToBackend if disabled', async () => {
|
|
1157
|
+
const mockFn = jest.fn().mockResolvedValue({ status: 200 });
|
|
1158
|
+
BackendRequests.__putFxTransfersNotification = mockFn;
|
|
1159
|
+
|
|
1160
|
+
const notif = JSON.parse(JSON.stringify(fxNotificationToBackend));
|
|
1161
|
+
const model = new Model({
|
|
1162
|
+
...config,
|
|
1163
|
+
cache,
|
|
1164
|
+
logger,
|
|
1165
|
+
backendRequestRetry: {
|
|
1166
|
+
enabled: false
|
|
1167
|
+
}
|
|
1168
|
+
});
|
|
1169
|
+
model.saveFxState = jest.fn().mockReturnValue(Promise.resolve({}));
|
|
1170
|
+
|
|
1171
|
+
await model.sendFxPutNotificationToBackend(notif.data, conversionId);
|
|
1172
|
+
expect(BackendRequests.__putFxTransfersNotification).toHaveBeenCalledTimes(1);
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
test('sendFxPutNotificationToBackend handles error and still saves state', async () => {
|
|
1176
|
+
BackendRequests.__putFxTransfersNotification = jest.fn().mockRejectedValue(new Error('fail'));
|
|
1177
|
+
const notif = JSON.parse(JSON.stringify(fxNotificationToBackend));
|
|
1178
|
+
const model = new Model({
|
|
1179
|
+
...config,
|
|
1180
|
+
cache,
|
|
1181
|
+
logger,
|
|
1182
|
+
backendRequestRetry: {
|
|
1183
|
+
enabled: false
|
|
1184
|
+
}
|
|
1185
|
+
});
|
|
1186
|
+
const saveFxStateSpy = jest.spyOn(model, 'saveFxState').mockResolvedValue({});
|
|
1187
|
+
await expect(model.sendFxPutNotificationToBackend(notif.data, conversionId)).resolves.toBeUndefined();
|
|
1188
|
+
expect(saveFxStateSpy).toHaveBeenCalled();
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
test('sendNotificationToPayee handles error and still returns', async () => {
|
|
1192
|
+
BackendRequests.__putTransfersNotification = jest.fn().mockRejectedValue(new Error('fail'));
|
|
1193
|
+
const notif = JSON.parse(JSON.stringify(notificationToPayee));
|
|
1194
|
+
const model = new Model({
|
|
1195
|
+
...config,
|
|
1196
|
+
cache,
|
|
1197
|
+
logger,
|
|
1198
|
+
backendRequestRetry: {
|
|
1199
|
+
enabled: false
|
|
1200
|
+
}
|
|
1201
|
+
});
|
|
1202
|
+
await expect(model.sendNotificationToPayee(notif.data, 'some-transfer-id')).resolves.toBeUndefined();
|
|
1203
|
+
});
|
|
1204
|
+
|
|
1205
|
+
test('retries notification to backend on failure for sendFxPutNotificationToBackend if enabled', async () => {
|
|
1206
|
+
// Simulate failure for first 2 attempts, then success
|
|
1207
|
+
const mockFn = jest.fn()
|
|
1208
|
+
.mockRejectedValueOnce(new Error('fail1'))
|
|
1209
|
+
.mockRejectedValueOnce(new Error('fail2'))
|
|
1210
|
+
.mockResolvedValue({ status: 200 });
|
|
1211
|
+
BackendRequests.__putFxTransfersNotification = mockFn;
|
|
1212
|
+
|
|
1213
|
+
const notif = JSON.parse(JSON.stringify(fxNotificationToBackend));
|
|
1214
|
+
const model = new Model({
|
|
1215
|
+
...config,
|
|
1216
|
+
cache,
|
|
1217
|
+
logger,
|
|
1218
|
+
backendRequestRetry: {
|
|
1219
|
+
enabled: true,
|
|
1220
|
+
maxRetries: 3,
|
|
1221
|
+
retryDelayMs: 10,
|
|
1222
|
+
maxRetryDelayMs: 20,
|
|
1223
|
+
backoffFactor: 1
|
|
1224
|
+
}
|
|
1225
|
+
});
|
|
1226
|
+
model.saveFxState = jest.fn().mockReturnValue(Promise.resolve({}));
|
|
1227
|
+
|
|
1228
|
+
await model.sendFxPutNotificationToBackend(notif.data, conversionId);
|
|
1229
|
+
expect(BackendRequests.__putFxTransfersNotification).toHaveBeenCalledTimes(3);
|
|
1230
|
+
});
|
|
1231
|
+
|
|
1232
|
+
test('does not retry notification to backend for sendFxPutNotificationToBackend if disabled', async () => {
|
|
1233
|
+
const mockFn = jest.fn().mockResolvedValue({ status: 200 });
|
|
1234
|
+
BackendRequests.__putFxTransfersNotification = mockFn;
|
|
1235
|
+
|
|
1236
|
+
const notif = JSON.parse(JSON.stringify(fxNotificationToBackend));
|
|
1237
|
+
const model = new Model({
|
|
1238
|
+
...config,
|
|
1239
|
+
cache,
|
|
1240
|
+
logger,
|
|
1241
|
+
backendRequestRetry: {
|
|
1242
|
+
enabled: false
|
|
1243
|
+
}
|
|
1244
|
+
});
|
|
1245
|
+
model.saveFxState = jest.fn().mockReturnValue(Promise.resolve({}));
|
|
1246
|
+
|
|
1247
|
+
await model.sendFxPutNotificationToBackend(notif.data, conversionId);
|
|
1248
|
+
expect(BackendRequests.__putFxTransfersNotification).toHaveBeenCalledTimes(1);
|
|
1249
|
+
});
|
|
1250
|
+
|
|
1251
|
+
test('sendFxPutNotificationToBackend handles error and still saves state', async () => {
|
|
1252
|
+
BackendRequests.__putFxTransfersNotification = jest.fn().mockRejectedValue(new Error('fail'));
|
|
1253
|
+
const notif = JSON.parse(JSON.stringify(fxNotificationToBackend));
|
|
1254
|
+
const model = new Model({
|
|
1255
|
+
...config,
|
|
1256
|
+
cache,
|
|
1257
|
+
logger,
|
|
1258
|
+
backendRequestRetry: {
|
|
1259
|
+
enabled: false
|
|
1260
|
+
}
|
|
1261
|
+
});
|
|
1262
|
+
const saveFxStateSpy = jest.spyOn(model, 'saveFxState').mockResolvedValue({});
|
|
1263
|
+
await expect(model.sendFxPutNotificationToBackend(notif.data, conversionId)).resolves.toBeUndefined();
|
|
1264
|
+
expect(saveFxStateSpy).toHaveBeenCalled();
|
|
1265
|
+
});
|
|
1266
|
+
|
|
1267
|
+
test('sendNotificationToPayee handles error and still returns', async () => {
|
|
1268
|
+
BackendRequests.__putTransfersNotification = jest.fn().mockRejectedValue(new Error('fail'));
|
|
1269
|
+
const notif = JSON.parse(JSON.stringify(notificationToPayee));
|
|
1270
|
+
const model = new Model({
|
|
1271
|
+
...config,
|
|
1272
|
+
cache,
|
|
1273
|
+
logger,
|
|
1274
|
+
backendRequestRetry: {
|
|
1275
|
+
enabled: false
|
|
1276
|
+
}
|
|
1277
|
+
});
|
|
1278
|
+
await expect(model.sendNotificationToPayee(notif.data, 'some-transfer-id')).resolves.toBeUndefined();
|
|
1279
|
+
});
|
|
916
1280
|
});
|
|
917
1281
|
|
|
918
1282
|
describe('error handling:', () => {
|