@mojaloop/sdk-scheme-adapter 24.10.11 → 24.11.0-snapshot.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.yarn/cache/@mojaloop-central-services-error-handling-npm-13.1.2-680b8f106e-1357eefd24.zip +0 -0
- package/.yarn/cache/{@mojaloop-sdk-standard-components-npm-19.16.9-b012b439ba-7a4f57c3e7.zip → @mojaloop-sdk-standard-components-npm-19.17.0-0519957b97-95fef19fc4.zip} +0 -0
- package/.yarn/cache/{@types-node-npm-24.4.0-8e03fde567-8ed3a165f8.zip → @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/{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.11.0-7389c6cf1c-0cb7230eb4.zip → undici-types-npm-7.12.0-af0c725921-4a0f927c98.zip} +0 -0
- package/.yarn/install-state.gz +0 -0
- package/modules/api-svc/package.json +5 -3
- 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 +142 -2
- 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 +482 -117
- package/modules/outbound-command-event-handler/package.json +3 -3
- package/modules/outbound-domain-event-handler/package.json +3 -3
- package/modules/private-shared-lib/package.json +3 -3
- package/package.json +3 -3
- package/{sbom-v24.10.10.csv → sbom-v24.10.11.csv} +190 -190
package/.yarn/cache/@mojaloop-central-services-error-handling-npm-13.1.2-680b8f106e-1357eefd24.zip
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/.yarn/install-state.gz
CHANGED
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mojaloop/sdk-scheme-adapter-api-svc",
|
|
3
|
-
"version": "21.0.0-snapshot.
|
|
3
|
+
"version": "21.0.0-snapshot.58",
|
|
4
4
|
"description": "An adapter for connecting to Mojaloop API enabled switches.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@koa/cors": "5.0.0",
|
|
67
67
|
"@mojaloop/api-snippets": "18.1.1",
|
|
68
|
-
"@mojaloop/central-services-error-handling": "13.1.
|
|
68
|
+
"@mojaloop/central-services-error-handling": "13.1.2",
|
|
69
69
|
"@mojaloop/central-services-logger": "11.9.3",
|
|
70
70
|
"@mojaloop/central-services-metrics": "12.7.1",
|
|
71
71
|
"@mojaloop/central-services-shared": "18.33.0",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"@mojaloop/logging-bc-client-lib": "0.5.8",
|
|
74
74
|
"@mojaloop/ml-schema-transformer-lib": "2.7.8",
|
|
75
75
|
"@mojaloop/sdk-scheme-adapter-private-shared-lib": "workspace:^",
|
|
76
|
-
"@mojaloop/sdk-standard-components": "19.
|
|
76
|
+
"@mojaloop/sdk-standard-components": "19.17.0",
|
|
77
77
|
"ajv": "8.17.1",
|
|
78
78
|
"axios": "1.12.2",
|
|
79
79
|
"body-parser": "2.2.0",
|
|
@@ -97,6 +97,7 @@
|
|
|
97
97
|
"promise-timeout": "1.3.0",
|
|
98
98
|
"random-word-slugs": "0.1.7",
|
|
99
99
|
"redis": "5.8.2",
|
|
100
|
+
"retry": "^0.13.1",
|
|
100
101
|
"uuidv4": "6.2.13",
|
|
101
102
|
"ws": "8.18.3"
|
|
102
103
|
},
|
|
@@ -105,6 +106,7 @@
|
|
|
105
106
|
"@babel/preset-env": "7.28.3",
|
|
106
107
|
"@redocly/openapi-cli": "1.0.0-beta.95",
|
|
107
108
|
"@types/jest": "30.0.0",
|
|
109
|
+
"@types/retry": "^0",
|
|
108
110
|
"axios-mock-adapter": "2.1.0",
|
|
109
111
|
"babel-jest": "30.1.2",
|
|
110
112
|
"eslint": "9.15.0",
|
|
@@ -729,12 +729,22 @@ const patchTransfersById = async (ctx) => {
|
|
|
729
729
|
// use the transfers model to execute asynchronous stages with the switch
|
|
730
730
|
const model = createInboundTransfersModel(ctx);
|
|
731
731
|
|
|
732
|
-
//
|
|
733
|
-
|
|
732
|
+
// kick off an asynchronous operation to handle the request
|
|
733
|
+
(async () => {
|
|
734
|
+
try {
|
|
735
|
+
// sends notification to the payee fsp
|
|
736
|
+
const response = await model.sendNotificationToPayee(req.data, idValue);
|
|
737
|
+
|
|
738
|
+
// log the result
|
|
739
|
+
ctx.state.logger.isDebugEnabled && ctx.state.logger.push({ response }).
|
|
740
|
+
debug('Inbound transfers model handled PATCH /transfers/{ID} request');
|
|
741
|
+
} catch (err) {
|
|
742
|
+
ctx.state.logger.isErrorEnabled && ctx.state.logger.push({ err }).error('Error handling PATCH /transfers/{ID}');
|
|
743
|
+
}
|
|
744
|
+
})();
|
|
734
745
|
|
|
735
|
-
|
|
736
|
-
ctx.
|
|
737
|
-
debug('Inbound transfers model handled PATCH /transfers/{ID} request');
|
|
746
|
+
ctx.response.status = ReturnCodes.OK.CODE;
|
|
747
|
+
ctx.response.body = '';
|
|
738
748
|
};
|
|
739
749
|
|
|
740
750
|
/**
|
|
@@ -1080,10 +1090,18 @@ const patchFxTransfersById = async (ctx) => {
|
|
|
1080
1090
|
const data = { ...ctx.request.body };
|
|
1081
1091
|
const idValue = ctx.state.path.params.ID;
|
|
1082
1092
|
|
|
1083
|
-
|
|
1084
|
-
|
|
1093
|
+
(async () => {
|
|
1094
|
+
const model = createInboundTransfersModel(ctx);
|
|
1095
|
+
try {
|
|
1096
|
+
const response = await model.sendFxPutNotificationToBackend(data, idValue);
|
|
1097
|
+
ctx.state.logger.push({ response }).debug('Inbound Transfers model handled PATCH /fxTransfers/{ID} request');
|
|
1098
|
+
} catch (err) {
|
|
1099
|
+
ctx.state.logger.push({ err }).error('Error handling PATCH /fxTransfers/{ID}');
|
|
1100
|
+
}
|
|
1101
|
+
})();
|
|
1085
1102
|
|
|
1086
|
-
ctx.
|
|
1103
|
+
ctx.response.status = ReturnCodes.OK.CODE;
|
|
1104
|
+
ctx.response.body = '';
|
|
1087
1105
|
};
|
|
1088
1106
|
|
|
1089
1107
|
/**
|
|
@@ -264,5 +264,21 @@ module.exports = {
|
|
|
264
264
|
|
|
265
265
|
// Redis key ttl when stored in the cache, if value is used as zero it will
|
|
266
266
|
// persist throughout the session , value used is in seconds
|
|
267
|
-
redisCacheTtl: env.get('REDIS_CACHE_TTL').default('0').asInt()
|
|
267
|
+
redisCacheTtl: env.get('REDIS_CACHE_TTL').default('0').asInt(),
|
|
268
|
+
|
|
269
|
+
backendRequestRetry: {
|
|
270
|
+
enabled: env.get('BACKEND_REQUEST_RETRY_ENABLED').default('true').asBool(),
|
|
271
|
+
maxRetries: env.get('BACKEND_REQUEST_RETRY_MAX_RETRIES').default('5').asIntPositive(),
|
|
272
|
+
retryDelayMs: env.get('BACKEND_REQUEST_RETRY_DELAY_MS').default('1000').asIntPositive(),
|
|
273
|
+
maxRetryDelayMs: env.get('BACKEND_REQUEST_RETRY_MAX_DELAY_MS').default('10000').asIntPositive(),
|
|
274
|
+
backoffFactor: env.get('BACKEND_REQUEST_RETRY_BACKOFF_FACTOR').default('2').asIntPositive(),
|
|
275
|
+
},
|
|
276
|
+
getTransferRequestRetry: {
|
|
277
|
+
enabled: env.get('GET_TRANSFER_REQUEST_RETRY_ENABLED').default('false').asBool(),
|
|
278
|
+
maxRetries: env.get('GET_TRANSFER_REQUEST_RETRY_MAX_RETRIES').default('3').asIntPositive(),
|
|
279
|
+
retryDelayMs: env.get('GET_TRANSFER_REQUEST_RETRY_DELAY_MS').default('1000').asIntPositive(),
|
|
280
|
+
maxRetryDelayMs: env.get('GET_TRANSFER_REQUEST_RETRY_MAX_DELAY_MS').default('10000').asIntPositive(),
|
|
281
|
+
backoffFactor: env.get('GET_TRANSFER_REQUEST_RETRY_BACKOFF_FACTOR').default('2').asIntPositive(),
|
|
282
|
+
},
|
|
283
|
+
patchNotificationGraceTimeMs: env.get('PATCH_NOTIFICATION_GRACE_TIME_MS').default('15000').asIntPositive(),
|
|
268
284
|
};
|
|
@@ -30,6 +30,7 @@ const safeStringify = require('fast-safe-stringify');
|
|
|
30
30
|
const { MojaloopRequests, Ilp, Errors } = require('@mojaloop/sdk-standard-components');
|
|
31
31
|
const FSPIOPTransferStateEnum = require('@mojaloop/central-services-shared').Enum.Transfers.TransferState;
|
|
32
32
|
const FSPIOPBulkTransferStateEnum = require('@mojaloop/central-services-shared').Enum.Transfers.BulkTransferState;
|
|
33
|
+
const retry = require('retry');
|
|
33
34
|
|
|
34
35
|
const dto = require('../dto');
|
|
35
36
|
const shared = require('./lib/shared');
|
|
@@ -52,6 +53,9 @@ class InboundTransfersModel {
|
|
|
52
53
|
this._reserveNotification = config.reserveNotification;
|
|
53
54
|
this._allowDifferentTransferTransactionId = config.allowDifferentTransferTransactionId;
|
|
54
55
|
this._supportedCurrencies = config.supportedCurrencies;
|
|
56
|
+
this._patchNotificationGraceTimeMs = config.patchNotificationGraceTimeMs;
|
|
57
|
+
this._getTransferRequestRetry = config.getTransferRequestRetry;
|
|
58
|
+
this._backendRequestRetry = config.backendRequestRetry;
|
|
55
59
|
|
|
56
60
|
this._mojaloopRequests = new MojaloopRequests({
|
|
57
61
|
logger: this._logger,
|
|
@@ -489,6 +493,60 @@ class InboundTransfersModel {
|
|
|
489
493
|
this.data.currentState = response.transferState || (this._reserveNotification ? SDKStateEnum.RESERVED : SDKStateEnum.COMPLETED);
|
|
490
494
|
|
|
491
495
|
await this._save();
|
|
496
|
+
|
|
497
|
+
// --- PATCH NOTIFICATION TIMER LOGIC ---
|
|
498
|
+
// Set a timer to trigger GET /transfers/{ID} if sendNotificationToPayee is not called in time
|
|
499
|
+
if (this._patchNotificationGraceTimeMs > 0) {
|
|
500
|
+
const transferId = prepareRequest.transferId;
|
|
501
|
+
const cacheKey = `patchNotificationSent_${transferId}`;
|
|
502
|
+
// Mark as not notified yet
|
|
503
|
+
await this._cache.set(cacheKey, false, Math.ceil(this._patchNotificationGraceTimeMs / 1000) + 5);
|
|
504
|
+
|
|
505
|
+
setTimeout(async () => {
|
|
506
|
+
try {
|
|
507
|
+
const notified = await this._cache.get(cacheKey);
|
|
508
|
+
|
|
509
|
+
if (!notified) {
|
|
510
|
+
// Subscribe to transfer callback channel
|
|
511
|
+
const transferKey = `tf_${transferId}`;
|
|
512
|
+
const unsubscribeTimeout = this._getTransferRequestRetry?.retryDelayMs || 1000;
|
|
513
|
+
let gotCallback = false;
|
|
514
|
+
|
|
515
|
+
// Subscribe for one message with a timeout
|
|
516
|
+
const messagePromise = this._cache.subscribeToOneMessageWithTimer(transferKey, Math.ceil(unsubscribeTimeout / 1000));
|
|
517
|
+
// Kick off GET /transfers/{ID}
|
|
518
|
+
await this._mojaloopRequests.getTransfers(transferId, sourceFspId, headers);
|
|
519
|
+
|
|
520
|
+
// Retry logic
|
|
521
|
+
let attempts = 0;
|
|
522
|
+
const maxAttempts = (this._getTransferRequestRetry?.maxRetries || 3);
|
|
523
|
+
const retryDelay = this._getTransferRequestRetry?.retryDelayMs || 1000;
|
|
524
|
+
while (attempts < maxAttempts && !gotCallback) {
|
|
525
|
+
try {
|
|
526
|
+
const message = await messagePromise;
|
|
527
|
+
if (message && message.data) {
|
|
528
|
+
gotCallback = true;
|
|
529
|
+
// Mark as notified to prevent duplicate notification
|
|
530
|
+
await this._cache.set(cacheKey, true, 60);
|
|
531
|
+
// Send notification to payee
|
|
532
|
+
await this.sendNotificationToPayee(message.data, transferId);
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
} catch (err) {
|
|
536
|
+
this._logger.isErrorEnabled && this._logger.push({ err, transferId }).error('Error while waiting for transfer callback in patch notification grace timer logic');
|
|
537
|
+
}
|
|
538
|
+
attempts++;
|
|
539
|
+
if (!gotCallback && attempts < maxAttempts) {
|
|
540
|
+
await this._mojaloopRequests.getTransfers(transferId, sourceFspId, headers);
|
|
541
|
+
await new Promise(r => setTimeout(r, retryDelay));
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
} catch (err) {
|
|
546
|
+
this._logger.isErrorEnabled && this._logger.push({ err, transferId }).error('Error in patch notification grace timer logic');
|
|
547
|
+
}
|
|
548
|
+
}, this._patchNotificationGraceTimeMs);
|
|
549
|
+
}
|
|
492
550
|
return res;
|
|
493
551
|
} catch(err) {
|
|
494
552
|
this._logger.isErrorEnabled && this._logger.push({ err }).error(`Error in prepareTransfer: ${prepareRequest?.transferId}`);
|
|
@@ -982,7 +1040,47 @@ class InboundTransfersModel {
|
|
|
982
1040
|
};
|
|
983
1041
|
log.verbose('sendFxPutNotificationToBackend body sent to cc: ', { responseBody });
|
|
984
1042
|
|
|
985
|
-
const
|
|
1043
|
+
const { enabled, maxRetries, retryDelayMs, maxRetryDelayMs, backoffFactor } = this._backendRequestRetry || {};
|
|
1044
|
+
let res;
|
|
1045
|
+
const shouldRetry = enabled !== false; // default to true if not set
|
|
1046
|
+
|
|
1047
|
+
if (shouldRetry) {
|
|
1048
|
+
const operation = retry.operation({
|
|
1049
|
+
retries: maxRetries || 5,
|
|
1050
|
+
factor: backoffFactor || 2,
|
|
1051
|
+
minTimeout: retryDelayMs || 1000,
|
|
1052
|
+
maxTimeout: maxRetryDelayMs || 10000,
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
await new Promise((resolve) => {
|
|
1056
|
+
operation.attempt(async (currentAttempt) => {
|
|
1057
|
+
try {
|
|
1058
|
+
res = await this._backendRequests.putFxTransfersNotification(responseBody, conversionId);
|
|
1059
|
+
if ((res && (res.status === 200 || res.statusCode === 200)) || res === true) {
|
|
1060
|
+
return resolve();
|
|
1061
|
+
}
|
|
1062
|
+
log.warn(`putFxTransfersNotification attempt ${currentAttempt} failed, retrying...`);
|
|
1063
|
+
if (!operation.retry(new Error('Non-200 response'))) {
|
|
1064
|
+
resolve();
|
|
1065
|
+
}
|
|
1066
|
+
} catch (err) {
|
|
1067
|
+
log.warn(`putFxTransfersNotification attempt ${currentAttempt} threw error, retrying...`, err);
|
|
1068
|
+
if (!operation.retry(err)) {
|
|
1069
|
+
resolve();
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
});
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
if ((res && (res.status !== 200 && res.statusCode !== 200)) && res !== true) {
|
|
1076
|
+
log.error(`putFxTransfersNotification failed after ${operation.attempts()} attempts`);
|
|
1077
|
+
}
|
|
1078
|
+
} else {
|
|
1079
|
+
res = await this._backendRequests.putFxTransfersNotification(responseBody, conversionId);
|
|
1080
|
+
if ((res && (res.status !== 200 && res.statusCode !== 200)) && res !== true) {
|
|
1081
|
+
log.error('putFxTransfersNotification failed');
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
986
1084
|
return res;
|
|
987
1085
|
} catch (err) {
|
|
988
1086
|
log.error('error in sendFxPutNotificationToBackend: ', err);
|
|
@@ -1025,7 +1123,49 @@ class InboundTransfersModel {
|
|
|
1025
1123
|
|
|
1026
1124
|
await this._save();
|
|
1027
1125
|
|
|
1028
|
-
const
|
|
1126
|
+
const { enabled, maxRetries, retryDelayMs, maxRetryDelayMs, backoffFactor } = this._backendRequestRetry || {};
|
|
1127
|
+
let res;
|
|
1128
|
+
const shouldRetry = enabled !== false; // default to true if not set
|
|
1129
|
+
|
|
1130
|
+
if (shouldRetry) {
|
|
1131
|
+
const operation = retry.operation({
|
|
1132
|
+
retries: maxRetries || 5,
|
|
1133
|
+
factor: backoffFactor || 2,
|
|
1134
|
+
minTimeout: retryDelayMs || 1000,
|
|
1135
|
+
maxTimeout: maxRetryDelayMs || 10000,
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
await new Promise((resolve) => {
|
|
1139
|
+
operation.attempt(async (currentAttempt) => {
|
|
1140
|
+
try {
|
|
1141
|
+
res = await this._backendRequests.putTransfersNotification(this.data, transferId);
|
|
1142
|
+
if ((res && (res.status === 200 || res.statusCode === 200)) || res === true) {
|
|
1143
|
+
const cacheKey = `patchNotificationSent_${transferId}`;
|
|
1144
|
+
await this._cache.set(cacheKey, true, 60);
|
|
1145
|
+
return resolve();
|
|
1146
|
+
}
|
|
1147
|
+
this._logger.warn(`putTransfersNotification attempt ${currentAttempt} failed, retrying...`);
|
|
1148
|
+
if (!operation.retry(new Error('Non-200 response'))) {
|
|
1149
|
+
resolve();
|
|
1150
|
+
}
|
|
1151
|
+
} catch (err) {
|
|
1152
|
+
this._logger.warn(`putTransfersNotification attempt ${currentAttempt} threw error, retrying...`, err);
|
|
1153
|
+
if (!operation.retry(err)) {
|
|
1154
|
+
resolve();
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
});
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1160
|
+
if ((res && (res.status !== 200 && res.statusCode !== 200)) && res !== true) {
|
|
1161
|
+
this._logger.error(`putTransfersNotification failed after ${operation.attempts()} attempts`);
|
|
1162
|
+
}
|
|
1163
|
+
} else {
|
|
1164
|
+
res = await this._backendRequests.putTransfersNotification(this.data, transferId);
|
|
1165
|
+
if ((res && (res.status !== 200 && res.statusCode !== 200)) && res !== true) {
|
|
1166
|
+
this._logger.error('putTransfersNotification failed');
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1029
1169
|
return res;
|
|
1030
1170
|
} catch (err) {
|
|
1031
1171
|
this._logger.isErrorEnabled && this._logger.push({ err, transferId }).error(`Error notifying backend of final transfer state equal to: ${body.transferState}`);
|
|
@@ -56,8 +56,11 @@ class RedisClient extends redisMock.RedisClient {
|
|
|
56
56
|
process.nextTick(() => this.events.emit(...args));
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Note: This Redis mock implementation does not support options like TTL (time-to-live).
|
|
61
|
+
*/
|
|
62
|
+
set(key, value) {
|
|
63
|
+
return promisify(super.set.bind(this))(key, value);
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
get(...args) {
|
|
@@ -315,7 +315,7 @@ describe('Inbound API handlers transforming incoming ISO20022 message bodies', (
|
|
|
315
315
|
|
|
316
316
|
});
|
|
317
317
|
|
|
318
|
-
test('calls `prepareTransfer` with the expected arguments.', async () => {
|
|
318
|
+
test('calls `prepareTransfer` with the expected arguments and sets 200 status.', async () => {
|
|
319
319
|
const transferRequestSpy = jest.spyOn(Model.prototype, 'sendNotificationToPayee');
|
|
320
320
|
|
|
321
321
|
await expect(handlers['/transfers/{ID}'].patch(mockContext)).resolves.toBe(undefined);
|
|
@@ -324,6 +324,8 @@ describe('Inbound API handlers transforming incoming ISO20022 message bodies', (
|
|
|
324
324
|
expect(transferRequestSpy.mock.calls[0][0]).not.toBeUndefined();
|
|
325
325
|
expect(transferRequestSpy.mock.calls[0][0]).not.toEqual(isoBodies.patchTransfersRequest);
|
|
326
326
|
expect(transferRequestSpy.mock.calls[0][0].transferState).toBe('COMMITTED');
|
|
327
|
+
expect(mockContext.response.status).toBe(200);
|
|
328
|
+
console.log('Response body:', mockContext.response);
|
|
327
329
|
});
|
|
328
330
|
});
|
|
329
331
|
|
|
@@ -624,12 +624,13 @@ describe('Inbound API handlers:', () => {
|
|
|
624
624
|
};
|
|
625
625
|
});
|
|
626
626
|
|
|
627
|
-
test('calls `model.sendNotificationToPayee with expected arguments', async () => {
|
|
627
|
+
test('calls `model.sendNotificationToPayee` with expected arguments and responds 200', async () => {
|
|
628
628
|
const notificationSpy = jest.spyOn(Model.prototype, 'sendNotificationToPayee');
|
|
629
629
|
|
|
630
630
|
await expect(handlers['/transfers/{ID}'].patch(mockNotificationMessage)).resolves.toBe(undefined);
|
|
631
631
|
expect(notificationSpy).toHaveBeenCalledTimes(1);
|
|
632
632
|
expect(notificationSpy.mock.calls[0][1]).toBe(mockNotificationMessage.state.path.params.ID);
|
|
633
|
+
expect(mockNotificationMessage.response.status).toBe(200);
|
|
633
634
|
});
|
|
634
635
|
|
|
635
636
|
});
|
|
@@ -1075,12 +1076,13 @@ describe('Inbound API handlers:', () => {
|
|
|
1075
1076
|
};
|
|
1076
1077
|
});
|
|
1077
1078
|
|
|
1078
|
-
test('calls `model.sendFxPutNotificationToBackend with expected arguments', async () => {
|
|
1079
|
+
test('calls `model.sendFxPutNotificationToBackend` with expected arguments and responds 200', async () => {
|
|
1079
1080
|
const notificationSpy = jest.spyOn(Model.prototype, 'sendFxPutNotificationToBackend');
|
|
1080
1081
|
|
|
1081
1082
|
await expect(handlers['/fxTransfers/{ID}'].patch(mockNotificationMessage)).resolves.toBe(undefined);
|
|
1082
1083
|
expect(notificationSpy).toHaveBeenCalledTimes(1);
|
|
1083
1084
|
expect(notificationSpy.mock.calls[0][1]).toBe(mockNotificationMessage.state.path.params.ID);
|
|
1085
|
+
expect(mockNotificationMessage.response.status).toBe(200);
|
|
1084
1086
|
});
|
|
1085
1087
|
|
|
1086
1088
|
});
|