@mojaloop/sdk-scheme-adapter 12.3.0 → 13.0.2
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/.env.example +3 -0
- package/CHANGELOG.md +25 -0
- package/docker/ml-testing-toolkit/spec_files/api_definitions/fspiop_1.1/trigger_templates/transaction_request_followup.json +2 -2
- package/docker/ml-testing-toolkit/spec_files/rules_callback/default.json +7 -7
- package/docker/ml-testing-toolkit/spec_files/rules_response/default.json +16 -16
- package/docker/ml-testing-toolkit/spec_files/rules_response/default_pisp_rules.json +5 -5
- package/docker/ml-testing-toolkit/spec_files/rules_validation/default.json +10 -10
- package/package.json +3 -3
- package/src/InboundServer/handlers.js +114 -52
- package/src/OutboundServer/api.yaml +105 -32
- package/src/OutboundServer/api_interfaces/openapi.d.ts +46 -16
- package/src/OutboundServer/api_template/components/schemas/accountsResponse.yaml +9 -0
- package/src/OutboundServer/api_template/components/schemas/partiesByIdResponse.yaml +10 -3
- package/src/OutboundServer/api_template/components/schemas/quotesPostResponse.yaml +42 -34
- package/src/OutboundServer/api_template/components/schemas/simpleTransfersPostResponse.yaml +9 -2
- package/src/OutboundServer/api_template/components/schemas/transferRequest.yaml +3 -0
- package/src/OutboundServer/api_template/components/schemas/transferResponse.yaml +28 -2
- package/src/OutboundServer/api_template/components/schemas/transferStatusResponse.yaml +8 -1
- package/src/OutboundServer/handlers.js +1 -1
- package/src/config.js +1 -1
- package/src/lib/model/AccountsModel.js +13 -11
- package/src/lib/model/InboundTransfersModel.js +166 -24
- package/src/lib/model/OutboundRequestToPayModel.js +5 -6
- package/src/lib/model/OutboundRequestToPayTransferModel.js +2 -2
- package/src/lib/model/OutboundTransfersModel.js +261 -56
- package/src/lib/model/PartiesModel.js +15 -2
- package/src/lib/model/common/BackendError.js +28 -4
- package/src/lib/model/common/index.js +2 -1
- package/test/__mocks__/@mojaloop/sdk-standard-components.js +3 -2
- package/test/integration/lib/Outbound/parties.test.js +2 -0
- package/test/integration/lib/Outbound/quotes.test.js +2 -0
- package/test/integration/lib/Outbound/simpleTransfers.test.js +2 -0
- package/test/unit/InboundServer.test.js +9 -9
- package/test/unit/TestServer.test.js +11 -13
- package/test/unit/api/accounts/data/postAccountsErrorMojaloopResponse.json +11 -3
- package/test/unit/api/accounts/data/postAccountsSuccessResponse.json +14 -0
- package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError1.json +13 -0
- package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError2.json +18 -0
- package/test/unit/api/accounts/utils.js +15 -1
- package/test/unit/api/transfers/data/getTransfersCommittedResponse.json +18 -15
- package/test/unit/api/transfers/data/getTransfersErrorNotFound.json +1 -0
- package/test/unit/api/transfers/data/postTransfersErrorMojaloopResponse.json +9 -0
- package/test/unit/api/transfers/data/postTransfersErrorTimeoutResponse.json +1 -0
- package/test/unit/api/transfers/data/postTransfersSuccessResponse.json +74 -47
- package/test/unit/api/transfers/utils.js +85 -4
- package/test/unit/data/commonHttpHeaders.json +1 -0
- package/test/unit/inboundApi/handlers.test.js +45 -14
- package/test/unit/lib/model/AccountsModel.test.js +9 -6
- package/test/unit/lib/model/InboundTransfersModel.test.js +210 -30
- package/test/unit/lib/model/OutboundRequestToPayModel.test.js +1 -1
- package/test/unit/lib/model/OutboundRequestToPayTransferModel.test.js +3 -3
- package/test/unit/lib/model/OutboundTransfersModel.test.js +826 -157
- package/test/unit/lib/model/PartiesModel.test.js +13 -7
- package/test/unit/lib/model/QuotesModel.test.js +8 -2
- package/test/unit/lib/model/TransfersModel.test.js +8 -2
- package/test/unit/lib/model/data/defaultConfig.json +9 -9
- package/test/unit/lib/model/data/mockArguments.json +97 -40
- package/test/unit/lib/model/data/payeeParty.json +13 -11
- package/test/unit/lib/model/data/quoteResponse.json +36 -25
- package/test/unit/lib/model/data/transferFulfil.json +5 -3
|
@@ -35,7 +35,7 @@ function createGetTransfersTester({ reqInbound, reqOutbound, apiSpecsOutbound })
|
|
|
35
35
|
|
|
36
36
|
return reqInbound.put(putUrl)
|
|
37
37
|
.send(putBody)
|
|
38
|
-
.set('content-type', 'application/vnd.interoperability.transfers+json;version=1.
|
|
38
|
+
.set('content-type', 'application/vnd.interoperability.transfers+json;version=1.1')
|
|
39
39
|
.set('Date', new Date().toISOString())
|
|
40
40
|
.set('fspiop-source', 'mojaloop-sdk')
|
|
41
41
|
.expect(200);
|
|
@@ -50,9 +50,49 @@ function createGetTransfersTester({ reqInbound, reqOutbound, apiSpecsOutbound })
|
|
|
50
50
|
const res = await reqOutbound.get(`/transfers/${TRANSFER_ID}`);
|
|
51
51
|
const {body} = res;
|
|
52
52
|
expect(res.statusCode).toEqual(responseCode);
|
|
53
|
+
|
|
54
|
+
// remove elements of the response we do not want/need to compare for correctness.
|
|
55
|
+
// timestamps on requests/responses for example will be set by the HTTP framework
|
|
56
|
+
// and we dont want to compare against static values.
|
|
53
57
|
delete body.initiatedTimestamp;
|
|
54
58
|
if (body.transferState) {
|
|
55
59
|
delete body.transferState.initiatedTimestamp;
|
|
60
|
+
if(body.transferState.quoteResponse) {
|
|
61
|
+
delete body.transferState.quoteResponse.headers;
|
|
62
|
+
}
|
|
63
|
+
if(body.transferState.getPartiesResponse) {
|
|
64
|
+
delete body.transferState.getPartiesResponse.headers;
|
|
65
|
+
}
|
|
66
|
+
if(body.transferState.fulfil) {
|
|
67
|
+
delete body.transferState.fulfil.headers;
|
|
68
|
+
}
|
|
69
|
+
if(body.transferState.quoteRequest) {
|
|
70
|
+
delete body.transferState.quoteRequest;
|
|
71
|
+
}
|
|
72
|
+
if(body.transferState.getPartiesRequest) {
|
|
73
|
+
delete body.transferState.getPartiesRequest;
|
|
74
|
+
}
|
|
75
|
+
if(body.transferState.prepare) {
|
|
76
|
+
delete body.transferState.prepare;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if(body.quoteResponse) {
|
|
80
|
+
delete body.quoteResponse.headers;
|
|
81
|
+
}
|
|
82
|
+
if(body.getPartiesResponse) {
|
|
83
|
+
delete body.getPartiesResponse.headers;
|
|
84
|
+
}
|
|
85
|
+
if(body.fulfil) {
|
|
86
|
+
delete body.fulfil.headers;
|
|
87
|
+
}
|
|
88
|
+
if(body.quoteRequest) {
|
|
89
|
+
delete body.quoteRequest;
|
|
90
|
+
}
|
|
91
|
+
if(body.getPartiesRequest) {
|
|
92
|
+
delete body.getPartiesRequest;
|
|
93
|
+
}
|
|
94
|
+
if(body.prepare) {
|
|
95
|
+
delete body.prepare;
|
|
56
96
|
}
|
|
57
97
|
expect(body).toEqual(responseBody);
|
|
58
98
|
const responseValidator = new OpenAPIResponseValidator(apiSpecsOutbound.paths['/transfers/{transferId}'].get);
|
|
@@ -108,17 +148,17 @@ function createPostTransfersTester(
|
|
|
108
148
|
if (urlPath.startsWith('/parties/')) {
|
|
109
149
|
putBody = await Promise.resolve(bodyFn.parties.put());
|
|
110
150
|
putUrl = urlPath;
|
|
111
|
-
contentType = 'application/vnd.interoperability.parties+json;version=1.
|
|
151
|
+
contentType = 'application/vnd.interoperability.parties+json;version=1.1';
|
|
112
152
|
} else if (urlPath === '/quotes') {
|
|
113
153
|
expect(body).toEqual(bodyFn.quotes.post(body));
|
|
114
154
|
putBody = await Promise.resolve(bodyFn.quotes.put(body));
|
|
115
155
|
putUrl = `/quotes/${body.quoteId}`;
|
|
116
|
-
contentType = 'application/vnd.interoperability.quotes+json;version=1.
|
|
156
|
+
contentType = 'application/vnd.interoperability.quotes+json;version=1.1';
|
|
117
157
|
} else if (urlPath === '/transfers') {
|
|
118
158
|
expect(body).toEqual(bodyFn.transfers.post(body));
|
|
119
159
|
putBody = await Promise.resolve(bodyFn.transfers.put(body));
|
|
120
160
|
putUrl = `/transfers/${body.transferId}`;
|
|
121
|
-
contentType = 'application/vnd.interoperability.transfers+json;version=1.
|
|
161
|
+
contentType = 'application/vnd.interoperability.transfers+json;version=1.1';
|
|
122
162
|
} else {
|
|
123
163
|
throw new Error(`Unexpected url ${urlPath}`);
|
|
124
164
|
}
|
|
@@ -162,10 +202,51 @@ function createPostTransfersTester(
|
|
|
162
202
|
|
|
163
203
|
const res = await reqOutbound.post('/transfers').send(postTransfersSimpleBody);
|
|
164
204
|
const {body} = res;
|
|
205
|
+
|
|
165
206
|
expect(res.statusCode).toEqual(responseCode);
|
|
207
|
+
|
|
208
|
+
// remove elements of the response we do not want/need to compare for correctness.
|
|
209
|
+
// timestamps on requests/responses for example will be set by the HTTP framework
|
|
210
|
+
// and we dont want to compare against static values.
|
|
166
211
|
delete body.initiatedTimestamp;
|
|
167
212
|
if (body.transferState) {
|
|
168
213
|
delete body.transferState.initiatedTimestamp;
|
|
214
|
+
if(body.transferState.quoteResponse) {
|
|
215
|
+
delete body.transferState.quoteResponse.headers;
|
|
216
|
+
}
|
|
217
|
+
if(body.transferState.getPartiesResponse) {
|
|
218
|
+
delete body.transferState.getPartiesResponse.headers;
|
|
219
|
+
}
|
|
220
|
+
if(body.transferState.fulfil) {
|
|
221
|
+
delete body.transferState.fulfil.headers;
|
|
222
|
+
}
|
|
223
|
+
if(body.transferState.quoteRequest) {
|
|
224
|
+
delete body.transferState.quoteRequest;
|
|
225
|
+
}
|
|
226
|
+
if(body.transferState.getPartiesRequest) {
|
|
227
|
+
delete body.transferState.getPartiesRequest;
|
|
228
|
+
}
|
|
229
|
+
if(body.transferState.prepare) {
|
|
230
|
+
delete body.transferState.prepare;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if(body.quoteResponse) {
|
|
234
|
+
delete body.quoteResponse.headers;
|
|
235
|
+
}
|
|
236
|
+
if(body.getPartiesResponse) {
|
|
237
|
+
delete body.getPartiesResponse.headers;
|
|
238
|
+
}
|
|
239
|
+
if(body.fulfil) {
|
|
240
|
+
delete body.fulfil.headers;
|
|
241
|
+
}
|
|
242
|
+
if(body.quoteRequest) {
|
|
243
|
+
delete body.quoteRequest;
|
|
244
|
+
}
|
|
245
|
+
if(body.getPartiesRequest) {
|
|
246
|
+
delete body.getPartiesRequest;
|
|
247
|
+
}
|
|
248
|
+
if(body.prepare) {
|
|
249
|
+
delete body.prepare;
|
|
169
250
|
}
|
|
170
251
|
expect(body).toEqual(responseBody);
|
|
171
252
|
const responseValidator = new OpenAPIResponseValidator(apiSpecsOutbound.paths['/transfers'].post);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
+
"content-type": "application/vnd.interoperability.parties+json;version=1.1",
|
|
2
3
|
"fspiop-source": "other-dfsp",
|
|
3
4
|
"fspiop-destination": "mojaloop-sdk",
|
|
4
5
|
"fspiop-signature": "{\"signature\":\"aTTa1TTCBJA1K1VoEFgpSicWYU0q1VYXV-bjkk7uoeNicog7QSp9_AbwtYm4u8NJ1HFM_3mekE8wioAs5YNugnTlJ1k-q4Ouvp5Jo3ZnozoPVtnLaqdhxRMUBOHfDp0X8eCHEo7lETjKcCcH4r5_KT_9Vwx5TMytoG_y9Be8PpviJFkOqOV5jCeIl7XzL_pZQoY0pRJdkXDzYpXDu-HTYKr8ckxWQzx4HO-viJQd2ByQkbqPfQom9IQaAX1t4yztCCpOQn1LY9j9sbfEX9RPXG3UbY6UyDsNjUKYP9BAhXwI9pFWlgv2i9FvEtay2QYdwbW7XEpIiGZ_vi5d6yc12w\",\"protectedHeader\":\"eyJhbGciOiJSUzI1NiIsIkZTUElPUC1VUkkiOiIvcGFydGllcy9NU0lTRE4vMTIzNDU2Nzg5IiwiRlNQSU9QLUhUVFAtTWV0aG9kIjoiUFVUIiwiRlNQSU9QLVNvdXJjZSI6InNpbSIsIkZTUElPUC1EZXN0aW5hdGlvbiI6ImRmc3AiLCJEYXRlIjoiVGh1LCAzMSBPY3QgMjAxOSAxMTo0MTo0MyBHTVQifQ\"}",
|
|
@@ -58,7 +58,7 @@ describe('Inbound API handlers:', () => {
|
|
|
58
58
|
await expect(handlers['/quotes'].post(mockContext)).resolves.toBe(undefined);
|
|
59
59
|
|
|
60
60
|
expect(quoteRequestSpy).toHaveBeenCalledTimes(1);
|
|
61
|
-
expect(quoteRequestSpy.mock.calls[0][0]).
|
|
61
|
+
expect(quoteRequestSpy.mock.calls[0][0]).toStrictEqual(mockContext.request);
|
|
62
62
|
expect(quoteRequestSpy.mock.calls[0][1]).toBe(mockContext.request.headers['fspiop-source']);
|
|
63
63
|
});
|
|
64
64
|
|
|
@@ -138,7 +138,10 @@ describe('Inbound API handlers:', () => {
|
|
|
138
138
|
expect(triggerDeferredJobSpy).toHaveBeenCalledTimes(1);
|
|
139
139
|
expect(triggerDeferredJobSpy).toBeCalledWith({
|
|
140
140
|
cache: mockContext.state.cache,
|
|
141
|
-
message:
|
|
141
|
+
message: {
|
|
142
|
+
body: mockContext.request.body,
|
|
143
|
+
headers: mockContext.request.headers,
|
|
144
|
+
},
|
|
142
145
|
args: {
|
|
143
146
|
quoteId: mockContext.state.path.params.ID
|
|
144
147
|
}
|
|
@@ -215,8 +218,10 @@ describe('Inbound API handlers:', () => {
|
|
|
215
218
|
expect(bulkQuotesSpy).toHaveBeenCalledTimes(1);
|
|
216
219
|
expect(bulkQuotesSpy.mock.calls[0][1]).toMatchObject({
|
|
217
220
|
type: 'bulkQuoteResponse',
|
|
218
|
-
data:
|
|
219
|
-
|
|
221
|
+
data: {
|
|
222
|
+
body: mockContext.request.body,
|
|
223
|
+
headers: mockContext.request.headers,
|
|
224
|
+
}
|
|
220
225
|
});
|
|
221
226
|
});
|
|
222
227
|
});
|
|
@@ -263,7 +268,10 @@ describe('Inbound API handlers:', () => {
|
|
|
263
268
|
expect(bulkQuotesSpy).toHaveBeenCalledTimes(1);
|
|
264
269
|
expect(bulkQuotesSpy.mock.calls[0][1]).toMatchObject({
|
|
265
270
|
type: 'bulkQuoteResponseError',
|
|
266
|
-
data:
|
|
271
|
+
data: {
|
|
272
|
+
body: mockContext.request.body,
|
|
273
|
+
headers: mockContext.request.headers,
|
|
274
|
+
}
|
|
267
275
|
});
|
|
268
276
|
});
|
|
269
277
|
});
|
|
@@ -372,8 +380,10 @@ describe('Inbound API handlers:', () => {
|
|
|
372
380
|
expect(bulkTransfersSpy).toHaveBeenCalledTimes(1);
|
|
373
381
|
expect(bulkTransfersSpy.mock.calls[0][1]).toMatchObject({
|
|
374
382
|
type: 'bulkTransferResponse',
|
|
375
|
-
data:
|
|
376
|
-
|
|
383
|
+
data: {
|
|
384
|
+
body: mockContext.request.body,
|
|
385
|
+
headers: mockContext.request.headers,
|
|
386
|
+
}
|
|
377
387
|
});
|
|
378
388
|
});
|
|
379
389
|
});
|
|
@@ -420,7 +430,10 @@ describe('Inbound API handlers:', () => {
|
|
|
420
430
|
expect(bulkTransfersSpy).toHaveBeenCalledTimes(1);
|
|
421
431
|
expect(bulkTransfersSpy.mock.calls[0][1]).toMatchObject({
|
|
422
432
|
type: 'bulkTransferResponseError',
|
|
423
|
-
data:
|
|
433
|
+
data: {
|
|
434
|
+
body: mockContext.request.body,
|
|
435
|
+
headers: mockContext.request.headers,
|
|
436
|
+
}
|
|
424
437
|
});
|
|
425
438
|
});
|
|
426
439
|
});
|
|
@@ -601,7 +614,10 @@ describe('Inbound API handlers:', () => {
|
|
|
601
614
|
expect(triggerDeferredJobSpy).toHaveBeenCalledTimes(1);
|
|
602
615
|
expect(triggerDeferredJobSpy).toBeCalledWith({
|
|
603
616
|
cache: mockContext.state.cache,
|
|
604
|
-
message:
|
|
617
|
+
message: {
|
|
618
|
+
body: mockContext.request.body,
|
|
619
|
+
headers: mockContext.request.headers,
|
|
620
|
+
},
|
|
605
621
|
args: {
|
|
606
622
|
type: mockContext.state.path.params.Type,
|
|
607
623
|
id: mockContext.state.path.params.ID
|
|
@@ -620,7 +636,10 @@ describe('Inbound API handlers:', () => {
|
|
|
620
636
|
expect(triggerDeferredJobSpy).toHaveBeenCalledTimes(1);
|
|
621
637
|
expect(triggerDeferredJobSpy).toBeCalledWith({
|
|
622
638
|
cache: mockContext.state.cache,
|
|
623
|
-
message:
|
|
639
|
+
message: {
|
|
640
|
+
body: mockContext.request.body,
|
|
641
|
+
headers: mockContext.request.headers,
|
|
642
|
+
},
|
|
624
643
|
args: {
|
|
625
644
|
type: mockContext.state.path.params.Type,
|
|
626
645
|
id: mockContext.state.path.params.ID,
|
|
@@ -667,7 +686,10 @@ describe('Inbound API handlers:', () => {
|
|
|
667
686
|
expect(triggerDeferredJobSpy).toHaveBeenCalledTimes(1);
|
|
668
687
|
expect(triggerDeferredJobSpy).toBeCalledWith({
|
|
669
688
|
cache: mockContext.state.cache,
|
|
670
|
-
message:
|
|
689
|
+
message: {
|
|
690
|
+
body: mockContext.request.body,
|
|
691
|
+
headers: mockContext.request.headers,
|
|
692
|
+
},
|
|
671
693
|
args: {
|
|
672
694
|
transferId: mockContext.state.path.params.ID
|
|
673
695
|
}
|
|
@@ -682,7 +704,10 @@ describe('Inbound API handlers:', () => {
|
|
|
682
704
|
expect(triggerDeferredJobSpy).toHaveBeenCalledTimes(1);
|
|
683
705
|
expect(triggerDeferredJobSpy).toBeCalledWith({
|
|
684
706
|
cache: mockContext.state.cache,
|
|
685
|
-
message:
|
|
707
|
+
message: {
|
|
708
|
+
body: mockContext.request.body,
|
|
709
|
+
headers: mockContext.request.headers,
|
|
710
|
+
},
|
|
686
711
|
args: {
|
|
687
712
|
transferId: mockContext.state.path.params.ID,
|
|
688
713
|
}
|
|
@@ -728,7 +753,10 @@ describe('Inbound API handlers:', () => {
|
|
|
728
753
|
expect(triggerDeferredJobSpy).toHaveBeenCalledTimes(1);
|
|
729
754
|
expect(triggerDeferredJobSpy).toBeCalledWith({
|
|
730
755
|
cache: mockContext.state.cache,
|
|
731
|
-
message:
|
|
756
|
+
message: {
|
|
757
|
+
body: mockContext.request.body,
|
|
758
|
+
headers: mockContext.request.headers,
|
|
759
|
+
},
|
|
732
760
|
args: {
|
|
733
761
|
transferId: mockContext.state.path.params.ID
|
|
734
762
|
}
|
|
@@ -743,7 +771,10 @@ describe('Inbound API handlers:', () => {
|
|
|
743
771
|
expect(triggerDeferredJobSpy).toHaveBeenCalledTimes(1);
|
|
744
772
|
expect(triggerDeferredJobSpy).toBeCalledWith({
|
|
745
773
|
cache: mockContext.state.cache,
|
|
746
|
-
message:
|
|
774
|
+
message: {
|
|
775
|
+
body: mockContext.request.body,
|
|
776
|
+
headers: mockContext.request.headers,
|
|
777
|
+
},
|
|
747
778
|
args: {
|
|
748
779
|
transferId: mockContext.state.path.params.ID,
|
|
749
780
|
}
|
|
@@ -51,14 +51,17 @@ describe('AccountsModel', () => {
|
|
|
51
51
|
const response = {
|
|
52
52
|
type: 'accountsCreationSuccessfulResponse',
|
|
53
53
|
data: {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
body: {
|
|
55
|
+
partyList: request.partyList.map(party => ({
|
|
56
|
+
partyId: party,
|
|
57
|
+
// errorInformation: null
|
|
58
|
+
})),
|
|
59
|
+
currency: request.currency,
|
|
60
|
+
},
|
|
61
|
+
headers: {}
|
|
59
62
|
},
|
|
60
63
|
};
|
|
61
|
-
cache.publish(`ac_${request.requestId}`,
|
|
64
|
+
cache.publish(`ac_${request.requestId}`, response);
|
|
62
65
|
return Promise.resolve();
|
|
63
66
|
});
|
|
64
67
|
|
|
@@ -55,6 +55,12 @@ describe('inboundModel', () => {
|
|
|
55
55
|
beforeEach(async () => {
|
|
56
56
|
expectedQuoteResponseILP = Ilp.__response;
|
|
57
57
|
BackendRequests.__postQuoteRequests = jest.fn().mockReturnValue(Promise.resolve(mockArgs.internalQuoteResponse));
|
|
58
|
+
MojaloopRequests.__putQuotes = jest.fn().mockReturnValue(Promise.resolve({
|
|
59
|
+
originalRequest: {
|
|
60
|
+
headers: {},
|
|
61
|
+
body: {},
|
|
62
|
+
}
|
|
63
|
+
}));
|
|
58
64
|
|
|
59
65
|
cache = new Cache({
|
|
60
66
|
host: 'dummycachehost',
|
|
@@ -78,11 +84,18 @@ describe('inboundModel', () => {
|
|
|
78
84
|
test('calls `mojaloopRequests.putQuotes` with the expected arguments.', async () => {
|
|
79
85
|
await model.quoteRequest(mockArgs.quoteRequest, mockArgs.fspId);
|
|
80
86
|
|
|
87
|
+
expect(BackendRequests.__postQuoteRequests).toHaveBeenCalledTimes(1);
|
|
88
|
+
expect(BackendRequests.__postQuoteRequests.mock.calls[0][0]).toEqual(mockArgs.internalQuoteRequest);
|
|
89
|
+
|
|
81
90
|
expect(MojaloopRequests.__putQuotes).toHaveBeenCalledTimes(1);
|
|
82
91
|
expect(MojaloopRequests.__putQuotes.mock.calls[0][1].expiration).toBe(mockArgs.internalQuoteResponse.expiration);
|
|
83
92
|
expect(MojaloopRequests.__putQuotes.mock.calls[0][1].ilpPacket).toBe(expectedQuoteResponseILP.ilpPacket);
|
|
84
93
|
expect(MojaloopRequests.__putQuotes.mock.calls[0][1].condition).toBe(expectedQuoteResponseILP.condition);
|
|
85
94
|
expect(MojaloopRequests.__putQuotes.mock.calls[0][2]).toBe(mockArgs.fspId);
|
|
95
|
+
|
|
96
|
+
// check the extension list gets translated correctly to the mojaloop form
|
|
97
|
+
expect(MojaloopRequests.__putQuotes.mock.calls[0][1].extensionList)
|
|
98
|
+
.toStrictEqual(mockArgs.internalQuoteResponse.extensionList);
|
|
86
99
|
});
|
|
87
100
|
|
|
88
101
|
test('adds a custom `expiration` property in case it is not defined.', async() => {
|
|
@@ -247,7 +260,12 @@ describe('inboundModel', () => {
|
|
|
247
260
|
beforeEach(async () => {
|
|
248
261
|
MojaloopRequests.__putTransfersError.mockClear();
|
|
249
262
|
BackendRequests.__postTransfers = jest.fn().mockReturnValue(Promise.resolve({}));
|
|
250
|
-
MojaloopRequests.__putTransfers = jest.fn().mockReturnValue(Promise.resolve({
|
|
263
|
+
MojaloopRequests.__putTransfers = jest.fn().mockReturnValue(Promise.resolve({
|
|
264
|
+
originalRequest: {
|
|
265
|
+
headers: {},
|
|
266
|
+
body: {},
|
|
267
|
+
}
|
|
268
|
+
}));
|
|
251
269
|
|
|
252
270
|
cache = new Cache({
|
|
253
271
|
host: 'dummycachehost',
|
|
@@ -269,13 +287,17 @@ describe('inboundModel', () => {
|
|
|
269
287
|
logger,
|
|
270
288
|
rejectTransfersOnExpiredQuotes: true,
|
|
271
289
|
});
|
|
272
|
-
cache.set(`
|
|
273
|
-
|
|
274
|
-
|
|
290
|
+
cache.set(`transferModel_in_${TRANSFER_ID}`, {
|
|
291
|
+
quote: {
|
|
292
|
+
mojaloopResponse: {
|
|
293
|
+
expiration: new Date(new Date().getTime() - 1000).toISOString(),
|
|
294
|
+
}
|
|
275
295
|
}
|
|
276
296
|
});
|
|
277
297
|
const args = {
|
|
278
|
-
|
|
298
|
+
body: {
|
|
299
|
+
transferId: TRANSFER_ID,
|
|
300
|
+
}
|
|
279
301
|
};
|
|
280
302
|
|
|
281
303
|
await model.prepareTransfer(args, mockArgs.fspId);
|
|
@@ -358,13 +380,15 @@ describe('inboundModel', () => {
|
|
|
358
380
|
test('fail on transfer without quote.', async () => {
|
|
359
381
|
const TRANSFER_ID = 'without_quote-transfer-id';
|
|
360
382
|
const args = {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
383
|
+
body: {
|
|
384
|
+
transferId: TRANSFER_ID,
|
|
385
|
+
amount: {
|
|
386
|
+
currency: 'USD',
|
|
387
|
+
amount: 20.13
|
|
388
|
+
},
|
|
389
|
+
ilpPacket: 'mockBase64encodedIlpPacket',
|
|
390
|
+
condition: 'mockGeneratedCondition'
|
|
391
|
+
}
|
|
368
392
|
};
|
|
369
393
|
|
|
370
394
|
const model = new Model({
|
|
@@ -382,16 +406,76 @@ describe('inboundModel', () => {
|
|
|
382
406
|
expect(call[1].errorInformation.errorCode).toEqual('2001');
|
|
383
407
|
});
|
|
384
408
|
|
|
385
|
-
test('
|
|
386
|
-
const TRANSFER_ID = '
|
|
409
|
+
test('stores homeTransactionId in cache when received by dfsp acting as payee', async () => {
|
|
410
|
+
const TRANSFER_ID = 'transfer-id';
|
|
411
|
+
const HOME_TRANSACTION_ID = 'mockHomeTransactionId';
|
|
412
|
+
shared.mojaloopPrepareToInternalTransfer = jest.fn().mockReturnValueOnce({});
|
|
413
|
+
|
|
414
|
+
// mock response from dfsp acting as payee
|
|
415
|
+
BackendRequests.__postTransfers = jest.fn().mockReturnValueOnce(Promise.resolve({
|
|
416
|
+
homeTransactionId: HOME_TRANSACTION_ID,
|
|
417
|
+
transferId: TRANSFER_ID
|
|
418
|
+
}));
|
|
419
|
+
|
|
387
420
|
const args = {
|
|
421
|
+
body: {
|
|
422
|
+
transferId: TRANSFER_ID,
|
|
423
|
+
amount: {
|
|
424
|
+
currency: 'USD',
|
|
425
|
+
amount: 20.13
|
|
426
|
+
},
|
|
427
|
+
ilpPacket: 'mockBase64encodedIlpPacket',
|
|
428
|
+
condition: 'mockGeneratedCondition'
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
const model = new Model({
|
|
433
|
+
...config,
|
|
434
|
+
cache,
|
|
435
|
+
logger,
|
|
436
|
+
checkIlp: false,
|
|
437
|
+
rejectTransfersOnExpiredQuotes: false
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
cache.set(`transferModel_in_${TRANSFER_ID}`, {
|
|
388
441
|
transferId: TRANSFER_ID,
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
442
|
+
quote: {
|
|
443
|
+
fulfilment: 'mockFulfilment',
|
|
444
|
+
mojaloopResponse: {
|
|
445
|
+
condition: 'mockCondition',
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
await model.prepareTransfer(args, mockArgs.fspId);
|
|
451
|
+
|
|
452
|
+
expect(MojaloopRequests.__putTransfersError).toHaveBeenCalledTimes(0);
|
|
453
|
+
expect(BackendRequests.__postTransfers).toHaveBeenCalledTimes(1);
|
|
454
|
+
expect(MojaloopRequests.__putTransfers).toHaveBeenCalledTimes(1);
|
|
455
|
+
expect((await cache.get(`transferModel_in_${TRANSFER_ID}`)).homeTransactionId)
|
|
456
|
+
.toEqual(HOME_TRANSACTION_ID);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
test('pass on transfer without quote.', async () => {
|
|
460
|
+
const TRANSFER_ID = 'without_quote-transfer-id';
|
|
461
|
+
cache.set(`transferModel_in_${TRANSFER_ID}`, {
|
|
462
|
+
fulfilment: '',
|
|
463
|
+
mojaloopResponse: {
|
|
464
|
+
response: ''
|
|
392
465
|
},
|
|
393
|
-
|
|
394
|
-
|
|
466
|
+
quote: null
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
const args = {
|
|
470
|
+
body: {
|
|
471
|
+
transferId: TRANSFER_ID,
|
|
472
|
+
amount: {
|
|
473
|
+
currency: 'USD',
|
|
474
|
+
amount: 20.13
|
|
475
|
+
},
|
|
476
|
+
ilpPacket: 'mockBase64encodedIlpPacket',
|
|
477
|
+
condition: 'mockGeneratedCondition'
|
|
478
|
+
}
|
|
395
479
|
};
|
|
396
480
|
|
|
397
481
|
const model = new Model({
|
|
@@ -413,21 +497,29 @@ describe('inboundModel', () => {
|
|
|
413
497
|
const TRANSFER_ID = 'transfer-id';
|
|
414
498
|
shared.mojaloopPrepareToInternalTransfer = jest.fn().mockReturnValueOnce({});
|
|
415
499
|
|
|
416
|
-
cache.set(`
|
|
500
|
+
cache.set(`transferModel_in_${transactionId}`, {
|
|
417
501
|
fulfilment: '',
|
|
418
502
|
mojaloopResponse: {
|
|
419
503
|
response: ''
|
|
504
|
+
},
|
|
505
|
+
quote: {
|
|
506
|
+
fulfilment: 'mockFulfilment',
|
|
507
|
+
mojaloopResponse: {
|
|
508
|
+
condition: 'mockCondition',
|
|
509
|
+
}
|
|
420
510
|
}
|
|
421
511
|
});
|
|
422
512
|
|
|
423
513
|
const args = {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
514
|
+
body: {
|
|
515
|
+
transferId: TRANSFER_ID,
|
|
516
|
+
amount: {
|
|
517
|
+
currency: 'USD',
|
|
518
|
+
amount: 20.13
|
|
519
|
+
},
|
|
520
|
+
ilpPacket: 'mockIlpPacket',
|
|
521
|
+
condition: 'mockGeneratedCondition'
|
|
522
|
+
}
|
|
431
523
|
};
|
|
432
524
|
|
|
433
525
|
const model = new Model({
|
|
@@ -648,7 +740,12 @@ describe('inboundModel', () => {
|
|
|
648
740
|
|
|
649
741
|
test('sends notification to fsp backend', async () => {
|
|
650
742
|
BackendRequests.__putTransfersNotification = jest.fn().mockReturnValue(Promise.resolve({}));
|
|
651
|
-
const
|
|
743
|
+
const notif = JSON.parse(JSON.stringify(notificationToPayee));
|
|
744
|
+
|
|
745
|
+
const expectedRequest = {
|
|
746
|
+
currentState: 'COMPLETED',
|
|
747
|
+
finalNotification: notif.data,
|
|
748
|
+
};
|
|
652
749
|
|
|
653
750
|
const model = new Model({
|
|
654
751
|
...config,
|
|
@@ -656,11 +753,94 @@ describe('inboundModel', () => {
|
|
|
656
753
|
logger,
|
|
657
754
|
});
|
|
658
755
|
|
|
659
|
-
await model.sendNotificationToPayee(
|
|
756
|
+
await model.sendNotificationToPayee(notif.data, transferId);
|
|
660
757
|
expect(BackendRequests.__putTransfersNotification).toHaveBeenCalledTimes(1);
|
|
661
758
|
const call = BackendRequests.__putTransfersNotification.mock.calls[0];
|
|
662
|
-
expect(call[0]).toEqual(
|
|
759
|
+
expect(call[0]).toEqual(expectedRequest);
|
|
663
760
|
expect(call[1]).toEqual(transferId);
|
|
664
761
|
});
|
|
665
762
|
});
|
|
763
|
+
|
|
764
|
+
describe('error handling:', () => {
|
|
765
|
+
let cache;
|
|
766
|
+
beforeEach(async () => {
|
|
767
|
+
cache = new Cache({
|
|
768
|
+
host: 'dummycachehost',
|
|
769
|
+
port: 1234,
|
|
770
|
+
logger,
|
|
771
|
+
});
|
|
772
|
+
await cache.connect();
|
|
773
|
+
});
|
|
774
|
+
afterEach(async () => {
|
|
775
|
+
await cache.disconnect();
|
|
776
|
+
});
|
|
777
|
+
test('creates mojaloop spec error body when backend returns standard error code', async () => {
|
|
778
|
+
const model = new Model({
|
|
779
|
+
...config,
|
|
780
|
+
cache,
|
|
781
|
+
logger,
|
|
782
|
+
});
|
|
783
|
+
const testErr = new HTTPResponseError({
|
|
784
|
+
msg: 'Request returned non-success status code 500',
|
|
785
|
+
res: {
|
|
786
|
+
data: {
|
|
787
|
+
statusCode: '3200',
|
|
788
|
+
},
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
const err = await model._handleError(testErr);
|
|
792
|
+
expect(err).toBeDefined();
|
|
793
|
+
expect(err.errorInformation).toBeDefined();
|
|
794
|
+
expect(err.errorInformation.errorCode).toEqual('3200');
|
|
795
|
+
// error message should be the default one, not custom.
|
|
796
|
+
// it is debatibale whether this is truly correct, to overwrite
|
|
797
|
+
// and custom error message; but it is the case for now.
|
|
798
|
+
expect(err.errorInformation.errorDescription).toEqual('Generic ID not found');
|
|
799
|
+
});
|
|
800
|
+
test('creates custom error body when backend returns custom error code', async () => {
|
|
801
|
+
const model = new Model({
|
|
802
|
+
...config,
|
|
803
|
+
cache,
|
|
804
|
+
logger,
|
|
805
|
+
});
|
|
806
|
+
const customMessage = 'some custom message';
|
|
807
|
+
const testErr = new HTTPResponseError({
|
|
808
|
+
msg: 'Request returned non-success status code 500',
|
|
809
|
+
res: {
|
|
810
|
+
data: {
|
|
811
|
+
statusCode: '3299',
|
|
812
|
+
message: customMessage,
|
|
813
|
+
},
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
const err = await model._handleError(testErr);
|
|
817
|
+
expect(err).toBeDefined();
|
|
818
|
+
expect(err.errorInformation).toBeDefined();
|
|
819
|
+
expect(err.errorInformation.errorCode).toEqual('3299');
|
|
820
|
+
expect(err.errorInformation.errorDescription).toEqual(customMessage);
|
|
821
|
+
});
|
|
822
|
+
test('creates custom error message when backend returns standard error code and message', async () => {
|
|
823
|
+
const model = new Model({
|
|
824
|
+
...config,
|
|
825
|
+
cache,
|
|
826
|
+
logger,
|
|
827
|
+
});
|
|
828
|
+
const customMessage = 'some custom message';
|
|
829
|
+
const testErr = new HTTPResponseError({
|
|
830
|
+
msg: 'Request returned non-success status code 500',
|
|
831
|
+
res: {
|
|
832
|
+
data: {
|
|
833
|
+
statusCode: '3200',
|
|
834
|
+
message: customMessage,
|
|
835
|
+
},
|
|
836
|
+
}
|
|
837
|
+
});
|
|
838
|
+
const err = await model._handleError(testErr);
|
|
839
|
+
expect(err).toBeDefined();
|
|
840
|
+
expect(err.errorInformation).toBeDefined();
|
|
841
|
+
expect(err.errorInformation.errorCode).toEqual('3200');
|
|
842
|
+
// error message should be custom
|
|
843
|
+
expect(err.errorInformation.errorDescription).toEqual(customMessage);
|
|
844
|
+
});
|
|
845
|
+
});
|
|
666
846
|
});
|
|
@@ -27,7 +27,7 @@ const payeeParty = require('./data/payeeParty');
|
|
|
27
27
|
const transactionRequestResponseTemplate = require('./data/transactionRequestResponse');
|
|
28
28
|
|
|
29
29
|
const genPartyId = (party) => {
|
|
30
|
-
const { partyIdType, partyIdentifier, partySubIdOrType } = party.party.partyIdInfo;
|
|
30
|
+
const { partyIdType, partyIdentifier, partySubIdOrType } = party.body.party.partyIdInfo;
|
|
31
31
|
return PartiesModel.channelName({
|
|
32
32
|
type: partyIdType,
|
|
33
33
|
id: partyIdentifier,
|
|
@@ -121,7 +121,7 @@ describe('outboundRequestToPayTransferModel', () => {
|
|
|
121
121
|
MojaloopRequests.__postTransfers = jest.fn((postTransfersBody, destFspId) => {
|
|
122
122
|
//ensure that the `MojaloopRequests.postTransfers` method has been called with the correct arguments
|
|
123
123
|
// set as the destination FSPID, picked up from the header's value `fspiop-source`
|
|
124
|
-
expect(model.data.quoteResponseSource).toBe(quoteResponse.headers['fspiop-source']);
|
|
124
|
+
expect(model.data.quoteResponseSource).toBe(quoteResponse.data.headers['fspiop-source']);
|
|
125
125
|
|
|
126
126
|
const extensionList = postTransfersBody.extensionList.extension;
|
|
127
127
|
expect(extensionList).toBeTruthy();
|
|
@@ -129,8 +129,8 @@ describe('outboundRequestToPayTransferModel', () => {
|
|
|
129
129
|
expect(extensionList[0]).toEqual({ key: 'tkey1', value: 'tvalue1' });
|
|
130
130
|
expect(extensionList[1]).toEqual({ key: 'tkey2', value: 'tvalue2' });
|
|
131
131
|
|
|
132
|
-
expect(destFspId).toBe(quoteResponse.headers['fspiop-source']);
|
|
133
|
-
expect(quoteResponse.headers['fspiop-source']).not.toBe(model.data.to.fspId);
|
|
132
|
+
expect(destFspId).toBe(quoteResponse.data.headers['fspiop-source']);
|
|
133
|
+
expect(quoteResponse.data.headers['fspiop-source']).not.toBe(model.data.to.fspId);
|
|
134
134
|
|
|
135
135
|
// simulate a callback with the transfer fulfilment
|
|
136
136
|
emitTransferFulfilCacheMessage(cache, postTransfersBody.transferId, transferFulfil);
|