@mojaloop/sdk-scheme-adapter 11.18.8
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 +140 -0
- package/.eslintignore +2 -0
- package/.eslintrc.json +30 -0
- package/.nvmrc +1 -0
- package/.versionrc +15 -0
- package/CHANGELOG.md +118 -0
- package/InboundServer/api.yaml +3594 -0
- package/InboundServer/api_template.yaml +69 -0
- package/InboundServer/handlers.js +940 -0
- package/InboundServer/index.js +205 -0
- package/InboundServer/middlewares.js +426 -0
- package/OAuthTestServer/index.js +66 -0
- package/OAuthTestServer/model.js +70 -0
- package/OutboundServer/api.yaml +2732 -0
- package/OutboundServer/api_interfaces/index.d.ts +117 -0
- package/OutboundServer/api_interfaces/openapi.d.ts +1475 -0
- package/OutboundServer/api_template/components/parameters/bulkQuoteId.yaml +9 -0
- package/OutboundServer/api_template/components/parameters/bulkTransferId.yaml +9 -0
- package/OutboundServer/api_template/components/parameters/requestToPayTransactionId.yaml +9 -0
- package/OutboundServer/api_template/components/parameters/transferId.yaml +9 -0
- package/OutboundServer/api_template/components/responses/accountsCreationCompleted.yaml +5 -0
- package/OutboundServer/api_template/components/responses/accountsCreationError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/accountsCreationTimeout.yaml +5 -0
- package/OutboundServer/api_template/components/responses/authorizationPostSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/authorizationsServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkQuoteBadRequest.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkQuoteServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkQuoteSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkQuoteTimeout.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkTransferBadRequest.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkTransferServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkTransferSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkTransferTimeout.yaml +5 -0
- package/OutboundServer/api_template/components/responses/partiesByIdError404.yaml +9 -0
- package/OutboundServer/api_template/components/responses/partiesByIdSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/quotesPostSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/quotesServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/requestToPaySuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/requestToPayTransferBadRequest.yaml +5 -0
- package/OutboundServer/api_template/components/responses/requestToPayTransferSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/simpleTransfersPostSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/simpleTransfersServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/transferBadRequest.yaml +5 -0
- package/OutboundServer/api_template/components/responses/transferServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/transferSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/transferTimeout.yaml +5 -0
- package/OutboundServer/api_template/components/schemas/accountCreationStatus.yaml +18 -0
- package/OutboundServer/api_template/components/schemas/accountsCreationState.yaml +4 -0
- package/OutboundServer/api_template/components/schemas/accountsRequest.yaml +20 -0
- package/OutboundServer/api_template/components/schemas/accountsResponse.yaml +15 -0
- package/OutboundServer/api_template/components/schemas/async2SyncCurrentState.yaml +5 -0
- package/OutboundServer/api_template/components/schemas/authorizationsPostRequest.yaml +15 -0
- package/OutboundServer/api_template/components/schemas/authorizationsPostResponse.yaml +19 -0
- package/OutboundServer/api_template/components/schemas/bulkQuoteErrorResponse.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/bulkQuoteRequest.yaml +26 -0
- package/OutboundServer/api_template/components/schemas/bulkQuoteResponse.yaml +21 -0
- package/OutboundServer/api_template/components/schemas/bulkQuoteStatus.yaml +4 -0
- package/OutboundServer/api_template/components/schemas/bulkQuoteStatusResponse.yaml +17 -0
- package/OutboundServer/api_template/components/schemas/bulkTransferErrorResponse.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/bulkTransferRequest.yaml +26 -0
- package/OutboundServer/api_template/components/schemas/bulkTransferResponse.yaml +16 -0
- package/OutboundServer/api_template/components/schemas/bulkTransferStatus.yaml +4 -0
- package/OutboundServer/api_template/components/schemas/bulkTransferStatusResponse.yaml +17 -0
- package/OutboundServer/api_template/components/schemas/errorAccountsResponse.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/errorAuthorizationsResponse.yaml +3 -0
- package/OutboundServer/api_template/components/schemas/errorQuotesResponse.yaml +9 -0
- package/OutboundServer/api_template/components/schemas/errorResponse.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/errorSimpleTransfersResponse.yaml +3 -0
- package/OutboundServer/api_template/components/schemas/errorTransferResponse.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/extensionListEmptiable.yaml +6 -0
- package/OutboundServer/api_template/components/schemas/individualQuote.yaml +32 -0
- package/OutboundServer/api_template/components/schemas/individualQuoteResult.yaml +28 -0
- package/OutboundServer/api_template/components/schemas/individualTransfer.yaml +32 -0
- package/OutboundServer/api_template/components/schemas/individualTransferFulfilment.yaml +13 -0
- package/OutboundServer/api_template/components/schemas/individualTransferResult.yaml +41 -0
- package/OutboundServer/api_template/components/schemas/mojaloopError.yaml +5 -0
- package/OutboundServer/api_template/components/schemas/mojaloopTransactionRequestState.yaml +2 -0
- package/OutboundServer/api_template/components/schemas/partiesByIdResponse.yaml +13 -0
- package/OutboundServer/api_template/components/schemas/quote.yaml +3 -0
- package/OutboundServer/api_template/components/schemas/quoteError.yaml +16 -0
- package/OutboundServer/api_template/components/schemas/quotesPostRequest.yaml +13 -0
- package/OutboundServer/api_template/components/schemas/quotesPostResponse.yaml +48 -0
- package/OutboundServer/api_template/components/schemas/requestToPayRequest.yaml +39 -0
- package/OutboundServer/api_template/components/schemas/requestToPayResponse.yaml +41 -0
- package/OutboundServer/api_template/components/schemas/requestToPayTransferRequest.yaml +42 -0
- package/OutboundServer/api_template/components/schemas/requestToPayTransferResponse.yaml +58 -0
- package/OutboundServer/api_template/components/schemas/simpleTransferServerError.yaml +5 -0
- package/OutboundServer/api_template/components/schemas/simpleTransfersPostRequest.yaml +12 -0
- package/OutboundServer/api_template/components/schemas/simpleTransfersPostResponse.yaml +11 -0
- package/OutboundServer/api_template/components/schemas/transactionType.yaml +4 -0
- package/OutboundServer/api_template/components/schemas/transferContinuationAcceptOTP.yaml +9 -0
- package/OutboundServer/api_template/components/schemas/transferContinuationAcceptParty.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/transferContinuationAcceptQuote.yaml +9 -0
- package/OutboundServer/api_template/components/schemas/transferError.yaml +16 -0
- package/OutboundServer/api_template/components/schemas/transferFulfilment.yaml +3 -0
- package/OutboundServer/api_template/components/schemas/transferParty.yaml +40 -0
- package/OutboundServer/api_template/components/schemas/transferRequest.yaml +37 -0
- package/OutboundServer/api_template/components/schemas/transferResponse.yaml +58 -0
- package/OutboundServer/api_template/components/schemas/transferStatus.yaml +6 -0
- package/OutboundServer/api_template/components/schemas/transferStatusResponse.yaml +13 -0
- package/OutboundServer/api_template/health.yaml +12 -0
- package/OutboundServer/api_template/openapi.yaml +55 -0
- package/OutboundServer/api_template/paths/accounts.yaml +26 -0
- package/OutboundServer/api_template/paths/authorizations.yaml +19 -0
- package/OutboundServer/api_template/paths/bulkQuotes.yaml +23 -0
- package/OutboundServer/api_template/paths/bulkQuotes_bulkQuoteId.yaml +24 -0
- package/OutboundServer/api_template/paths/bulkTransfers.yaml +23 -0
- package/OutboundServer/api_template/paths/bulkTransfers_bulkTransferId.yaml +24 -0
- package/OutboundServer/api_template/paths/parties_Type_ID.yaml +20 -0
- package/OutboundServer/api_template/paths/parties_Type_ID_SubId.yaml +22 -0
- package/OutboundServer/api_template/paths/quotes.yaml +20 -0
- package/OutboundServer/api_template/paths/requestToPay.yaml +22 -0
- package/OutboundServer/api_template/paths/requestToPayTransfer.yaml +57 -0
- package/OutboundServer/api_template/paths/requestToPayTransfer_requestToPayTransactionId.yaml +34 -0
- package/OutboundServer/api_template/paths/simpleTransfers.yaml +19 -0
- package/OutboundServer/api_template/paths/transfers.yaml +55 -0
- package/OutboundServer/api_template/paths/transfers_transferId.yaml +58 -0
- package/OutboundServer/handlers.js +622 -0
- package/OutboundServer/index.js +137 -0
- package/OutboundServer/middlewares.js +67 -0
- package/TestServer/api.yaml +62 -0
- package/TestServer/handlers.js +63 -0
- package/TestServer/index.js +215 -0
- package/audit-resolve.json +65 -0
- package/babel.config.js +3 -0
- package/config.js +158 -0
- package/index.d.ts +1 -0
- package/index.js +149 -0
- package/jest.config.js +15 -0
- package/lib/api/index.js +12 -0
- package/lib/cache.js +352 -0
- package/lib/check.js +25 -0
- package/lib/model/AccountsModel.js +396 -0
- package/lib/model/Async2SyncModel.js +283 -0
- package/lib/model/AuthorizationsModel.js +86 -0
- package/lib/model/InboundTransfersModel.js +730 -0
- package/lib/model/OutboundBulkQuotesModel.js +485 -0
- package/lib/model/OutboundBulkTransfersModel.js +479 -0
- package/lib/model/OutboundRequestToPayModel.js +517 -0
- package/lib/model/OutboundRequestToPayTransferModel.js +893 -0
- package/lib/model/OutboundTransfersModel.js +823 -0
- package/lib/model/PartiesModel.js +70 -0
- package/lib/model/ProxyModel/MatchRules/Expression.js +48 -0
- package/lib/model/ProxyModel/MatchRules/Headers.js +65 -0
- package/lib/model/ProxyModel/MatchRules/MatchRule.js +27 -0
- package/lib/model/ProxyModel/MatchRules/Path.js +36 -0
- package/lib/model/ProxyModel/MatchRules/Query.js +65 -0
- package/lib/model/ProxyModel/MatchRules/index.js +19 -0
- package/lib/model/ProxyModel/Route.js +82 -0
- package/lib/model/ProxyModel/configSchema.json +118 -0
- package/lib/model/ProxyModel/index.js +138 -0
- package/lib/model/QuotesModel.js +94 -0
- package/lib/model/TransfersModel.js +81 -0
- package/lib/model/common/BackendError.js +26 -0
- package/lib/model/common/PersistentStateMachine.js +93 -0
- package/lib/model/common/index.js +18 -0
- package/lib/model/index.js +43 -0
- package/lib/model/lib/deferredJob.js +113 -0
- package/lib/model/lib/index.js +9 -0
- package/lib/model/lib/requests/backendRequests.js +227 -0
- package/lib/model/lib/requests/common.js +76 -0
- package/lib/model/lib/requests/index.js +19 -0
- package/lib/model/lib/shared.js +468 -0
- package/lib/randomphrase/index.js +21 -0
- package/lib/randomphrase/words.json +3397 -0
- package/lib/router.js +28 -0
- package/lib/validate.js +205 -0
- package/package.json +102 -0
- package/test/__mocks__/@mojaloop/sdk-standard-components.js +152 -0
- package/test/__mocks__/javascript-state-machine.js +21 -0
- package/test/__mocks__/redis.js +49 -0
- package/test/__mocks__/uuidv4.js +16 -0
- package/test/config/integration.env +136 -0
- package/test/integration/lib/Outbound/authorizations.test.js +58 -0
- package/test/integration/lib/Outbound/data/authorizationsPostRequest.json +43 -0
- package/test/integration/lib/Outbound/data/quotesPostRequest.json +52 -0
- package/test/integration/lib/Outbound/data/transfersPostRequest.json +24 -0
- package/test/integration/lib/Outbound/parties.test.js +28 -0
- package/test/integration/lib/Outbound/quotes.test.js +58 -0
- package/test/integration/lib/Outbound/simpleTransfers.test.js +67 -0
- package/test/integration/lib/cache.test.js +80 -0
- package/test/integration/testEnv.js +7 -0
- package/test/unit/InboundServer.test.js +443 -0
- package/test/unit/TestServer.test.js +394 -0
- package/test/unit/api/accounts/accounts.test.js +128 -0
- package/test/unit/api/accounts/data/postAccountsBody.json +7 -0
- package/test/unit/api/accounts/data/postAccountsErrorMojaloopResponse.json +25 -0
- package/test/unit/api/accounts/data/postAccountsErrorTimeoutResponse.json +19 -0
- package/test/unit/api/accounts/data/postAccountsSuccessResponse.json +17 -0
- package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError1.json +21 -0
- package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError2.json +21 -0
- package/test/unit/api/accounts/utils.js +65 -0
- package/test/unit/api/proxy/data/proxyConfig.yaml +82 -0
- package/test/unit/api/proxy/data/requestBody.json +22 -0
- package/test/unit/api/proxy/data/requestHeaders.json +5 -0
- package/test/unit/api/proxy/data/requestQuery.json +6 -0
- package/test/unit/api/proxy/data/responseBody.json +21 -0
- package/test/unit/api/proxy/data/responseHeaders.json +5 -0
- package/test/unit/api/proxy/proxy.test.js +220 -0
- package/test/unit/api/proxy/utils.js +79 -0
- package/test/unit/api/transfers/data/getTransfersCommittedResponse.json +21 -0
- package/test/unit/api/transfers/data/getTransfersErrorNotFound.json +17 -0
- package/test/unit/api/transfers/data/postQuotesBody.json +52 -0
- package/test/unit/api/transfers/data/postTransfersBadBody.json +17 -0
- package/test/unit/api/transfers/data/postTransfersBody.json +24 -0
- package/test/unit/api/transfers/data/postTransfersErrorMojaloopResponse.json +53 -0
- package/test/unit/api/transfers/data/postTransfersErrorTimeoutResponse.json +47 -0
- package/test/unit/api/transfers/data/postTransfersSimpleBody.json +26 -0
- package/test/unit/api/transfers/data/postTransfersSuccessResponse.json +101 -0
- package/test/unit/api/transfers/data/putPartiesBody.json +20 -0
- package/test/unit/api/transfers/data/putQuotesBody.json +37 -0
- package/test/unit/api/transfers/data/putTransfersBody.json +17 -0
- package/test/unit/api/transfers/transfers.test.js +191 -0
- package/test/unit/api/transfers/utils.js +183 -0
- package/test/unit/api/utils.js +75 -0
- package/test/unit/config.test.js +119 -0
- package/test/unit/data/commonHttpHeaders.json +6 -0
- package/test/unit/data/defaultConfig.json +58 -0
- package/test/unit/data/postQuotesBody.json +52 -0
- package/test/unit/data/putParticipantsBody.json +12 -0
- package/test/unit/data/putPartiesBody.json +20 -0
- package/test/unit/data/testFile.json +29 -0
- package/test/unit/data/testFile.yaml +14 -0
- package/test/unit/inboundApi/data/mockArguments.json +117 -0
- package/test/unit/inboundApi/data/mockTransactionRequest.json +42 -0
- package/test/unit/inboundApi/handlers.test.js +799 -0
- package/test/unit/index.test.js +55 -0
- package/test/unit/lib/cache.test.js +146 -0
- package/test/unit/lib/model/AccountsModel.test.js +121 -0
- package/test/unit/lib/model/AuthorizationsModel.test.js +460 -0
- package/test/unit/lib/model/InboundTransfersModel.test.js +628 -0
- package/test/unit/lib/model/OutboundBulkQuotesModel.test.js +249 -0
- package/test/unit/lib/model/OutboundBulkTransfersModel.test.js +244 -0
- package/test/unit/lib/model/OutboundRequestToPayModel.test.js +166 -0
- package/test/unit/lib/model/OutboundRequestToPayTransferModel.test.js +245 -0
- package/test/unit/lib/model/OutboundTransfersModel.test.js +836 -0
- package/test/unit/lib/model/PartiesModel.test.js +468 -0
- package/test/unit/lib/model/QuotesModel.test.js +470 -0
- package/test/unit/lib/model/TransfersModel.test.js +474 -0
- package/test/unit/lib/model/common/PersistentStateMachine.test.js +179 -0
- package/test/unit/lib/model/data/authorizationsResponse.json +13 -0
- package/test/unit/lib/model/data/bulkQuoteRequest.json +27 -0
- package/test/unit/lib/model/data/bulkQuoteResponse.json +35 -0
- package/test/unit/lib/model/data/bulkTransferFulfil.json +13 -0
- package/test/unit/lib/model/data/bulkTransferRequest.json +29 -0
- package/test/unit/lib/model/data/defaultConfig.json +47 -0
- package/test/unit/lib/model/data/getBulkTransfersBackendResponse.json +42 -0
- package/test/unit/lib/model/data/getBulkTransfersMojaloopResponse.json +22 -0
- package/test/unit/lib/model/data/getTransfersBackendResponse.json +34 -0
- package/test/unit/lib/model/data/getTransfersMojaloopResponse.json +17 -0
- package/test/unit/lib/model/data/mockArguments.json +131 -0
- package/test/unit/lib/model/data/mockTxnRequestsArguments.json +63 -0
- package/test/unit/lib/model/data/notificationToPayee.json +10 -0
- package/test/unit/lib/model/data/payeeParty.json +16 -0
- package/test/unit/lib/model/data/putAuthorizationsResponse.json +10 -0
- package/test/unit/lib/model/data/putQuotesResponse.json +33 -0
- package/test/unit/lib/model/data/putTransfersResponse.json +5 -0
- package/test/unit/lib/model/data/quoteResponse.json +31 -0
- package/test/unit/lib/model/data/requestToPayRequest.json +20 -0
- package/test/unit/lib/model/data/requestToPayTransferRequest.json +27 -0
- package/test/unit/lib/model/data/transactionRequestResponse.json +18 -0
- package/test/unit/lib/model/data/transferFulfil.json +8 -0
- package/test/unit/lib/model/data/transferRequest.json +26 -0
- package/test/unit/lib/model/mockedLibRequests.js +74 -0
- package/test/unit/mockLogger.js +39 -0
- package/test/unit/outboundApi/data/bulkQuoteRequest.json +28 -0
- package/test/unit/outboundApi/data/bulkTransferRequest.json +28 -0
- package/test/unit/outboundApi/data/mockBulkQuoteError.json +45 -0
- package/test/unit/outboundApi/data/mockBulkTransferError.json +48 -0
- package/test/unit/outboundApi/data/mockError.json +41 -0
- package/test/unit/outboundApi/data/mockGetPartiesError.json +4 -0
- package/test/unit/outboundApi/data/mockRequestToPayError.json +32 -0
- package/test/unit/outboundApi/data/mockRequestToPayTransferError.json +39 -0
- package/test/unit/outboundApi/data/requestToPay.json +21 -0
- package/test/unit/outboundApi/data/requestToPayTransferRequest.json +20 -0
- package/test/unit/outboundApi/data/transferRequest.json +21 -0
- package/test/unit/outboundApi/handlers.test.js +986 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**************************************************************************
|
|
2
|
+
* (C) Copyright ModusBox Inc. 2019 - All rights reserved. *
|
|
3
|
+
* *
|
|
4
|
+
* This file is made available under the terms of the license agreement *
|
|
5
|
+
* specified in the corresponding source code repository. *
|
|
6
|
+
* *
|
|
7
|
+
* ORIGINAL AUTHOR: *
|
|
8
|
+
* James Bush - james.bush@modusbox.com *
|
|
9
|
+
**************************************************************************/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const { Logger } = require('@mojaloop/sdk-standard-components');
|
|
14
|
+
const defaultConfig = require('./data/defaultConfig');
|
|
15
|
+
|
|
16
|
+
jest.mock('dotenv', () => ({
|
|
17
|
+
config: jest.fn()
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
process.env.PEER_ENDPOINT = '172.17.0.3:4000';
|
|
21
|
+
process.env.BACKEND_ENDPOINT = '172.17.0.5:4000';
|
|
22
|
+
process.env.CACHE_HOST = '172.17.0.2';
|
|
23
|
+
process.env.CACHE_PORT = '6379';
|
|
24
|
+
|
|
25
|
+
const index = require('../../index.js');
|
|
26
|
+
|
|
27
|
+
describe('index.js', () => {
|
|
28
|
+
test('WSO2 error events in OutboundServer propagate to top-level server', () => {
|
|
29
|
+
const logger = new Logger.Logger({ stringify: () => '' });
|
|
30
|
+
const svr = new index.Server(defaultConfig, logger);
|
|
31
|
+
const cb = jest.fn();
|
|
32
|
+
svr.on('error', cb);
|
|
33
|
+
svr.outboundServer._api._wso2.auth.emit('error', 'msg');
|
|
34
|
+
expect(cb).toHaveBeenCalledTimes(1);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('WSO2 error events in InboundServer propagate to top-level server', () => {
|
|
38
|
+
const logger = new Logger.Logger({ stringify: () => '' });
|
|
39
|
+
const svr = new index.Server(defaultConfig, logger);
|
|
40
|
+
const cb = jest.fn();
|
|
41
|
+
svr.on('error', cb);
|
|
42
|
+
svr.inboundServer._api._wso2.auth.emit('error', 'msg');
|
|
43
|
+
expect(cb).toHaveBeenCalledTimes(1);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('Exports expected modules', () => {
|
|
47
|
+
expect(typeof(index.Server)).toBe('function');
|
|
48
|
+
expect(typeof(index.InboundServerMiddleware)).toBe('object');
|
|
49
|
+
expect(typeof(index.OutboundServerMiddleware)).toBe('object');
|
|
50
|
+
expect(typeof(index.Router)).toBe('function');
|
|
51
|
+
expect(typeof(index.Validate)).toBe('function');
|
|
52
|
+
expect(typeof(index.RandomPhrase)).toBe('function');
|
|
53
|
+
expect(typeof(index.Cache)).toBe('function');
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**************************************************************************
|
|
2
|
+
* (C) Copyright ModusBox Inc. 2019 - All rights reserved. *
|
|
3
|
+
* *
|
|
4
|
+
* This file is made available under the terms of the license agreement *
|
|
5
|
+
* specified in the corresponding source code repository. *
|
|
6
|
+
* *
|
|
7
|
+
* ORIGINAL AUTHOR: *
|
|
8
|
+
* James Bush - james.bush@modusbox.com *
|
|
9
|
+
**************************************************************************/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
jest.mock('redis');
|
|
14
|
+
|
|
15
|
+
const Cache = require('../../../lib/cache');
|
|
16
|
+
const { Logger } = require('@mojaloop/sdk-standard-components');
|
|
17
|
+
|
|
18
|
+
const createCache = async() => {
|
|
19
|
+
const logger = new Logger.Logger({ context: { app: 'model-unit-tests-cache' }, stringify: () => '' });
|
|
20
|
+
const cache = new Cache({
|
|
21
|
+
host: 'dummyhost',
|
|
22
|
+
port: 1234,
|
|
23
|
+
logger,
|
|
24
|
+
});
|
|
25
|
+
await cache.connect();
|
|
26
|
+
return cache;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
describe('Cache', () => {
|
|
30
|
+
let dummyPubMessage;
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
dummyPubMessage = JSON.parse(JSON.stringify({
|
|
34
|
+
data: 12345,
|
|
35
|
+
test: '98765'
|
|
36
|
+
}));
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('Makes connections to redis server for cache operations', async () => {
|
|
40
|
+
const cache = await createCache();
|
|
41
|
+
expect(cache).not.toBeFalsy();
|
|
42
|
+
await cache.disconnect();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
test('Makes subscriber callbacks on the correct channels when messages arrive', async () => {
|
|
47
|
+
const cache = await createCache();
|
|
48
|
+
|
|
49
|
+
const msg1 = dummyPubMessage;
|
|
50
|
+
|
|
51
|
+
// make the messages different
|
|
52
|
+
const msg2 = JSON.parse(JSON.stringify(dummyPubMessage));
|
|
53
|
+
msg2.abc = 'xyz';
|
|
54
|
+
|
|
55
|
+
const chan1 = 'dummychannel1';
|
|
56
|
+
const chan2 = 'dummychannel2';
|
|
57
|
+
|
|
58
|
+
// create a promise that only gets resoled if the subscription gets the
|
|
59
|
+
// correct message
|
|
60
|
+
const cb1Promise = new Promise((resolve) => {
|
|
61
|
+
const mockCb1 = jest.fn((cn, msg) => {
|
|
62
|
+
expect(cn).toBe(chan1);
|
|
63
|
+
|
|
64
|
+
const value = JSON.parse(msg);
|
|
65
|
+
// check we got the expected message
|
|
66
|
+
expect(value).toEqual(msg1);
|
|
67
|
+
|
|
68
|
+
//resolve the promise
|
|
69
|
+
resolve();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// subscribe to a ficticious channel
|
|
73
|
+
return cache.subscribe(chan1, mockCb1).then(cbId1 => {
|
|
74
|
+
// we should be the first callback registered (zero index)
|
|
75
|
+
expect(cbId1).toBe(0);
|
|
76
|
+
|
|
77
|
+
// now we have subscribed, inject a message.
|
|
78
|
+
cache.publish(chan1, msg1);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// create a second promise that only gets resoled if the second subscription gets the
|
|
83
|
+
// correct message
|
|
84
|
+
const cb2Promise = new Promise((resolve) => {
|
|
85
|
+
const mockCb2 = jest.fn((cn, msg) => {
|
|
86
|
+
expect(cn).toBe(chan2);
|
|
87
|
+
|
|
88
|
+
// check we got the expected message
|
|
89
|
+
const value = JSON.parse(msg);
|
|
90
|
+
expect(value).toEqual(msg2);
|
|
91
|
+
|
|
92
|
+
//resolve the promise
|
|
93
|
+
resolve();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// subscribe to a ficticious channel
|
|
97
|
+
return cache.subscribe(chan2, mockCb2).then(cbId2 => {
|
|
98
|
+
// we should be the second callback registered (zero index)
|
|
99
|
+
expect(cbId2).toBe(1);
|
|
100
|
+
|
|
101
|
+
// now we have subscribed, inject a message.
|
|
102
|
+
cache.publish(chan2, msg2);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
await Promise.all([cb1Promise, cb2Promise]);
|
|
107
|
+
|
|
108
|
+
await cache.disconnect();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
test('Unsubscribed callbacks do not get called when messages arrive', async () => {
|
|
113
|
+
const cache = await createCache();
|
|
114
|
+
const msg1 = dummyPubMessage;
|
|
115
|
+
|
|
116
|
+
const chan = 'dummychannel1';
|
|
117
|
+
|
|
118
|
+
// create a promise that only gets resoled if the subscription gets the
|
|
119
|
+
// correct message
|
|
120
|
+
await new Promise((resolve, reject) => {
|
|
121
|
+
const mockCb1 = jest.fn((cn, msg) => { // eslint-disable-line no-unused-vars
|
|
122
|
+
//reject the outer promise if this func gets called!
|
|
123
|
+
reject();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// subscribe to a ficticious channel
|
|
127
|
+
return cache.subscribe(chan, mockCb1).then(cbId1 => {
|
|
128
|
+
// we should be the first callback registered (zero index)
|
|
129
|
+
expect(cbId1).toBe(0);
|
|
130
|
+
|
|
131
|
+
// now we have subscribed we unsubscribe
|
|
132
|
+
return cache.unsubscribe(chan, cbId1).then(() => {
|
|
133
|
+
// now we have unsubscribed, inject a message
|
|
134
|
+
cache.publish(chan, msg1);
|
|
135
|
+
|
|
136
|
+
// wait 3 seconds and if the callback has not been called we assume a pass
|
|
137
|
+
setTimeout(() => {
|
|
138
|
+
expect(mockCb1.mock.calls.length).toBe(0);
|
|
139
|
+
return resolve();
|
|
140
|
+
}, 3000);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
await cache.disconnect();
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**************************************************************************
|
|
2
|
+
* (C) Copyright ModusBox Inc. 2019 - All rights reserved. *
|
|
3
|
+
* *
|
|
4
|
+
* This file is made available under the terms of the license agreement *
|
|
5
|
+
* specified in the corresponding source code repository. *
|
|
6
|
+
* *
|
|
7
|
+
* ORIGINAL AUTHOR: *
|
|
8
|
+
* Yevhen Kyriukha - yevhen.kyriukha@modusbox.com *
|
|
9
|
+
**************************************************************************/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
// we use a mock standard components lib to intercept and mock certain funcs
|
|
14
|
+
jest.mock('@mojaloop/sdk-standard-components');
|
|
15
|
+
jest.mock('redis');
|
|
16
|
+
|
|
17
|
+
const Cache = require('../../../../lib/cache');
|
|
18
|
+
const { AccountsModel } = require('../../../../lib/model');
|
|
19
|
+
|
|
20
|
+
const StateMachine = require('javascript-state-machine');
|
|
21
|
+
const { MojaloopRequests, Logger } = require('@mojaloop/sdk-standard-components');
|
|
22
|
+
|
|
23
|
+
const defaultConfig = require('./data/defaultConfig');
|
|
24
|
+
const transferRequest = require('./data/transferRequest');
|
|
25
|
+
|
|
26
|
+
function generateAccounts(count, currencies) {
|
|
27
|
+
const accounts = [];
|
|
28
|
+
for (let currencyIndex = 0; currencyIndex < currencies.length; currencyIndex++) {
|
|
29
|
+
for (let i = 1; i <= count; i++) {
|
|
30
|
+
accounts.push({
|
|
31
|
+
idType: 'MSISDN',
|
|
32
|
+
idValue: String(i * (currencyIndex + 1)).padStart(9, '0'),
|
|
33
|
+
idSubValue: `Sub_${String(i * (currencyIndex + 1))}`.padStart(5, '0'),
|
|
34
|
+
currency: currencies[currencyIndex],
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return accounts;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
describe('AccountsModel', () => {
|
|
43
|
+
let logger;
|
|
44
|
+
let cache;
|
|
45
|
+
|
|
46
|
+
async function testCreateAccount(count, currencies) {
|
|
47
|
+
const MAX_ITEMS_PER_REQUEST = 10000; // As per API Spec 6.2.2.2 (partyList field)
|
|
48
|
+
|
|
49
|
+
MojaloopRequests.__postParticipants = jest.fn(request => {
|
|
50
|
+
// simulate a response from ALS
|
|
51
|
+
const response = {
|
|
52
|
+
type: 'accountsCreationSuccessfulResponse',
|
|
53
|
+
data: {
|
|
54
|
+
partyList: request.partyList.map(party => ({
|
|
55
|
+
partyId: party,
|
|
56
|
+
// errorInformation: null
|
|
57
|
+
})),
|
|
58
|
+
currency: request.currency,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
cache.publish(`ac_${request.requestId}`, JSON.stringify(response));
|
|
62
|
+
return Promise.resolve();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const model = new AccountsModel({
|
|
66
|
+
...defaultConfig,
|
|
67
|
+
cache,
|
|
68
|
+
logger,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const accounts = generateAccounts(count, currencies);
|
|
72
|
+
await model.initialize({ accounts });
|
|
73
|
+
|
|
74
|
+
expect(StateMachine.__instance.state).toBe('start');
|
|
75
|
+
|
|
76
|
+
// wait for the model to reach a terminal state
|
|
77
|
+
const result = await model.run();
|
|
78
|
+
|
|
79
|
+
const expectedRequestsCount = currencies.length *
|
|
80
|
+
(Math.floor(count / MAX_ITEMS_PER_REQUEST) + ((count % MAX_ITEMS_PER_REQUEST) ? 1 : 0));
|
|
81
|
+
expect(MojaloopRequests.__postParticipants).toHaveBeenCalledTimes(expectedRequestsCount);
|
|
82
|
+
|
|
83
|
+
expect(result.currentState).toBe('COMPLETED');
|
|
84
|
+
expect(StateMachine.__instance.state).toBe('succeeded');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
beforeAll(() => {
|
|
88
|
+
logger = new Logger.Logger({ context: { app: 'outbound-model-unit-tests-cache' }, stringify: () => '' });
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
beforeEach(async () => {
|
|
92
|
+
cache = new Cache({
|
|
93
|
+
host: 'dummycachehost',
|
|
94
|
+
port: 1234,
|
|
95
|
+
logger,
|
|
96
|
+
});
|
|
97
|
+
await cache.connect();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
afterEach(async () => {
|
|
101
|
+
await cache.disconnect();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('initializes to starting state', async () => {
|
|
105
|
+
const model = new AccountsModel({
|
|
106
|
+
...defaultConfig,
|
|
107
|
+
cache,
|
|
108
|
+
logger,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await model.initialize(JSON.parse(JSON.stringify(transferRequest)));
|
|
112
|
+
|
|
113
|
+
expect(StateMachine.__instance.state).toBe('start');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('create 100 accounts', () =>
|
|
117
|
+
testCreateAccount(100, ['USD', 'EUR', 'UAH']));
|
|
118
|
+
|
|
119
|
+
test('create 20000 accounts', () =>
|
|
120
|
+
testCreateAccount(20000, ['USD', 'EUR']));
|
|
121
|
+
});
|
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
/**************************************************************************
|
|
2
|
+
* (C) Copyright ModusBox Inc. 2021 - All rights reserved. *
|
|
3
|
+
* *
|
|
4
|
+
* This file is made available under the terms of the license agreement *
|
|
5
|
+
* specified in the corresponding source code repository. *
|
|
6
|
+
* *
|
|
7
|
+
* ORIGINAL AUTHOR: *
|
|
8
|
+
* Sridhar Voruganti - sridhar.voruganti@modusbox.com *
|
|
9
|
+
**************************************************************************/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
// we use a mock standard components lib to intercept and mock certain funcs
|
|
14
|
+
jest.mock('@mojaloop/sdk-standard-components');
|
|
15
|
+
|
|
16
|
+
const { uuid } = require('uuidv4');
|
|
17
|
+
const Model = require('../../../../lib/model').AuthorizationsModel;
|
|
18
|
+
const PSM = require('../../../../lib/model/common').PersistentStateMachine;
|
|
19
|
+
const { MojaloopRequests } = require('@mojaloop/sdk-standard-components');
|
|
20
|
+
const defaultConfig = require('./data/defaultConfig');
|
|
21
|
+
const mockLogger = require('../../mockLogger');
|
|
22
|
+
const deferredJob = require('../../../../lib/model/lib').deferredJob;
|
|
23
|
+
const pt = require('promise-timeout');
|
|
24
|
+
const authorizationsResponse = require('./data/putAuthorizationsResponse.json');
|
|
25
|
+
|
|
26
|
+
describe('AuthorizationsModel', () => {
|
|
27
|
+
let cacheKey;
|
|
28
|
+
let data;
|
|
29
|
+
let modelConfig;
|
|
30
|
+
|
|
31
|
+
const subId = uuid();
|
|
32
|
+
let handler = null;
|
|
33
|
+
beforeEach(async () => {
|
|
34
|
+
|
|
35
|
+
modelConfig = {
|
|
36
|
+
logger: mockLogger({ app: 'authorizationsModel-test' }),
|
|
37
|
+
|
|
38
|
+
// there is no need to mock redis but only Cache
|
|
39
|
+
cache: {
|
|
40
|
+
get: jest.fn(() => Promise.resolve(data)),
|
|
41
|
+
set: jest.fn(() => Promise.resolve),
|
|
42
|
+
|
|
43
|
+
// mock subscription and store handler
|
|
44
|
+
subscribe: jest.fn(async (channel, h) => {
|
|
45
|
+
handler = jest.fn(h);
|
|
46
|
+
return subId;
|
|
47
|
+
}),
|
|
48
|
+
|
|
49
|
+
// mock publish and call stored handler
|
|
50
|
+
publish: jest.fn(async (channel, message) => await handler(channel, message, subId)),
|
|
51
|
+
|
|
52
|
+
unsubscribe: jest.fn(() => Promise.resolve())
|
|
53
|
+
},
|
|
54
|
+
...defaultConfig
|
|
55
|
+
};
|
|
56
|
+
data = {
|
|
57
|
+
the: 'mocked data'
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
cacheKey = `key-authorizations-${uuid()}`;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('create', () => {
|
|
64
|
+
test('proper creation of model', async () => {
|
|
65
|
+
const model = await Model.create(data, cacheKey, modelConfig);
|
|
66
|
+
|
|
67
|
+
expect(model.state).toBe('start');
|
|
68
|
+
|
|
69
|
+
// model's methods layout
|
|
70
|
+
const methods = [
|
|
71
|
+
'run',
|
|
72
|
+
'getResponse',
|
|
73
|
+
'onRequestAction'
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
methods.forEach((method) => expect(typeof model[method]).toEqual('function'));
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('getResponse', () => {
|
|
81
|
+
|
|
82
|
+
it('should remap currentState', async () => {
|
|
83
|
+
const model = await Model.create(data, cacheKey, modelConfig);
|
|
84
|
+
const states = model.allStates();
|
|
85
|
+
// should remap for all states except 'init' and 'none'
|
|
86
|
+
states.filter((s) => s !== 'init' && s !== 'none').forEach((state) => {
|
|
87
|
+
model.context.data.currentState = state;
|
|
88
|
+
const result = model.getResponse();
|
|
89
|
+
expect(result.currentState).toEqual(Model.mapCurrentState[state]);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should handle unexpected state', async () => {
|
|
95
|
+
const model = await Model.create(data, cacheKey, modelConfig);
|
|
96
|
+
|
|
97
|
+
// simulate lack of state by undefined property
|
|
98
|
+
delete model.context.data.currentState;
|
|
99
|
+
|
|
100
|
+
const resp = model.getResponse();
|
|
101
|
+
expect(resp.currentState).toEqual(Model.mapCurrentState.errored);
|
|
102
|
+
|
|
103
|
+
// ensure that we log the problem properly
|
|
104
|
+
expect(modelConfig.logger.error).toHaveBeenCalledWith(`AuthorizationsModel model response being returned from an unexpected state: ${undefined}. Returning ERROR_OCCURRED state`);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('channelName', () => {
|
|
109
|
+
it('should validate input', () => {
|
|
110
|
+
expect(Model.channelName({})).toEqual('authorizations-undefined');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should generate proper channel name', () => {
|
|
114
|
+
const transactionRequestId = uuid();
|
|
115
|
+
expect(Model.channelName({ transactionRequestId })).toEqual(`authorizations-${transactionRequestId}`);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe('generateKey', () => {
|
|
121
|
+
it('should generate proper cache key', () => {
|
|
122
|
+
const transactionRequestId = uuid();
|
|
123
|
+
expect(Model.generateKey({ transactionRequestId })).toEqual(`key-${Model.channelName({ transactionRequestId })}`);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should handle lack of transactionRequestId param', () => {
|
|
127
|
+
expect(() => Model.generateKey({})).toThrowError(new Error('AuthorizationsModel args requires \'transactionRequestId\' is nonempty string and mandatory property'));
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('onRequestAction', () => {
|
|
133
|
+
|
|
134
|
+
it('should implement happy flow', async () => {
|
|
135
|
+
const transactionRequestId = uuid();
|
|
136
|
+
const fspId = uuid();
|
|
137
|
+
// our code takes care only about 'transactionRequestId' property
|
|
138
|
+
const authorization = { transactionRequestId };
|
|
139
|
+
const channel = Model.channelName({ transactionRequestId });
|
|
140
|
+
const model = await Model.create(data, cacheKey, modelConfig);
|
|
141
|
+
const { cache } = model.context;
|
|
142
|
+
// mock workflow execution which is tested in separate case
|
|
143
|
+
model.run = jest.fn(() => Promise.resolve());
|
|
144
|
+
|
|
145
|
+
const message = { ...authorizationsResponse };
|
|
146
|
+
|
|
147
|
+
const onRequestActionPromise = new Promise((resolve, reject) => {
|
|
148
|
+
// manually invoke transition handler
|
|
149
|
+
model.onRequestAction(model.fsm, { transactionRequestId, fspId, authorization })
|
|
150
|
+
.then(() => {
|
|
151
|
+
// subscribe should be called only once
|
|
152
|
+
expect(cache.subscribe).toBeCalledTimes(1);
|
|
153
|
+
|
|
154
|
+
// subscribe should be done to proper notificationChannel
|
|
155
|
+
expect(cache.subscribe.mock.calls[0][0]).toEqual(channel);
|
|
156
|
+
|
|
157
|
+
// check invocation of request.getParties
|
|
158
|
+
expect(MojaloopRequests.__postAuthorizations).toBeCalledWith(authorization, fspId);
|
|
159
|
+
|
|
160
|
+
// check that this.context.data is updated
|
|
161
|
+
expect(model.context.data).toEqual({
|
|
162
|
+
authorizations: { ...message },
|
|
163
|
+
currentState: 'start'
|
|
164
|
+
//current state will be updated by onAfterTransition which isn't called
|
|
165
|
+
//when manual invocation of transition handler happens
|
|
166
|
+
|
|
167
|
+
});
|
|
168
|
+
// handler should be called only once
|
|
169
|
+
expect(handler).toBeCalledTimes(1);
|
|
170
|
+
|
|
171
|
+
// handler should unsubscribe from notification channel
|
|
172
|
+
expect(cache.unsubscribe).toBeCalledTimes(1);
|
|
173
|
+
expect(cache.unsubscribe).toBeCalledWith(channel, subId);
|
|
174
|
+
resolve();
|
|
175
|
+
}).catch((err) => { reject(err); } );
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// ensure handler wasn't called before publishing the message
|
|
179
|
+
expect(handler).not.toBeCalled();
|
|
180
|
+
|
|
181
|
+
// ensure that cache.unsubscribe does not happened before fire the message
|
|
182
|
+
expect(cache.unsubscribe).not.toBeCalled();
|
|
183
|
+
|
|
184
|
+
// fire publication with given message
|
|
185
|
+
const df = deferredJob(cache, channel);
|
|
186
|
+
setImmediate(() => df.trigger(message));
|
|
187
|
+
|
|
188
|
+
// wait for onRequestAction
|
|
189
|
+
await onRequestActionPromise;
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should handle timeouts', async () => {
|
|
193
|
+
const transactionRequestId = uuid();
|
|
194
|
+
const fspId = uuid();
|
|
195
|
+
// our code takes care only about 'transactionRequestId' property
|
|
196
|
+
const authorization = { transactionRequestId };
|
|
197
|
+
const channel = Model.channelName({ transactionRequestId });
|
|
198
|
+
|
|
199
|
+
const model = await Model.create(data, cacheKey, modelConfig);
|
|
200
|
+
const { cache } = model.context;
|
|
201
|
+
// mock workflow execution which is tested in separate case
|
|
202
|
+
model.run = jest.fn(() => Promise.resolve());
|
|
203
|
+
|
|
204
|
+
const message = { ...authorizationsResponse };
|
|
205
|
+
|
|
206
|
+
const onRequestActionPromise = new Promise((resolve, reject) => {
|
|
207
|
+
// manually invoke transition handler
|
|
208
|
+
model.onRequestAction(model.fsm, { transactionRequestId, fspId, authorization })
|
|
209
|
+
.then(() => reject())
|
|
210
|
+
.catch((err) => {
|
|
211
|
+
// subscribe should be called only once
|
|
212
|
+
expect(err instanceof pt.TimeoutError).toBeTruthy();
|
|
213
|
+
|
|
214
|
+
// subscribe should be done to proper notificationChannel
|
|
215
|
+
expect(cache.subscribe.mock.calls[0][0]).toEqual(channel);
|
|
216
|
+
|
|
217
|
+
// check invocation of request.getParties
|
|
218
|
+
expect(MojaloopRequests.__postAuthorizations).toBeCalledWith(authorization, fspId);
|
|
219
|
+
|
|
220
|
+
// handler should be called only once
|
|
221
|
+
expect(handler).toBeCalledTimes(0);
|
|
222
|
+
|
|
223
|
+
// handler should unsubscribe from notification channel
|
|
224
|
+
expect(cache.unsubscribe).toBeCalledTimes(1);
|
|
225
|
+
expect(cache.unsubscribe).toBeCalledWith(channel, subId);
|
|
226
|
+
resolve();
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// ensure handler wasn't called before publishing the message
|
|
231
|
+
expect(handler).not.toBeCalled();
|
|
232
|
+
|
|
233
|
+
// ensure that cache.unsubscribe does not happened before fire the message
|
|
234
|
+
expect(cache.unsubscribe).not.toBeCalled();
|
|
235
|
+
|
|
236
|
+
// fire publication with given message
|
|
237
|
+
const df = deferredJob(cache, channel);
|
|
238
|
+
|
|
239
|
+
setTimeout(
|
|
240
|
+
() => { df.trigger(message); },
|
|
241
|
+
// ensure that publication will be far long after timeout should be auto triggered
|
|
242
|
+
(modelConfig.requestProcessingTimeoutSeconds + 1) * 1000
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
// wait for onRequestAction
|
|
246
|
+
await onRequestActionPromise;
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('should unsubscribe from cache in case when error happens in workflow run', async () => {
|
|
250
|
+
const transactionRequestId = uuid();
|
|
251
|
+
const fspId = uuid();
|
|
252
|
+
// our code takes care only about 'transactionRequestId' property
|
|
253
|
+
const authorization = { transactionRequestId };
|
|
254
|
+
const channel = Model.channelName({ transactionRequestId });
|
|
255
|
+
const model = await Model.create(data, cacheKey, modelConfig);
|
|
256
|
+
const { cache } = model.context;
|
|
257
|
+
|
|
258
|
+
// fire publication to channel with invalid message
|
|
259
|
+
// should throw the exception from JSON.parse
|
|
260
|
+
const df = deferredJob(cache, channel);
|
|
261
|
+
setImmediate(() => df.trigger(undefined));
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
await model.onRequestAction(model.fsm, { transactionRequestId, fspId, authorization });
|
|
265
|
+
throw new Error('this point should not be reached');
|
|
266
|
+
} catch(err) {
|
|
267
|
+
expect(err.message).toEqual('Unexpected token u in JSON at position 0');
|
|
268
|
+
expect(cache.unsubscribe).toBeCalledTimes(1);
|
|
269
|
+
expect(cache.unsubscribe).toBeCalledWith(channel, subId);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it('should unsubscribe from cache in case when error happens Mojaloop requests', async () => {
|
|
274
|
+
// simulate error
|
|
275
|
+
MojaloopRequests.__postAuthorizations = jest.fn(() => Promise.reject('postAuthorizations failed'));
|
|
276
|
+
const transactionRequestId = uuid();
|
|
277
|
+
const fspId = uuid();
|
|
278
|
+
// our code takes care only about 'transactionRequestId' property
|
|
279
|
+
const authorization = { transactionRequestId };
|
|
280
|
+
const channel = Model.channelName({ transactionRequestId });
|
|
281
|
+
const model = await Model.create(data, cacheKey, modelConfig);
|
|
282
|
+
const { cache } = model.context;
|
|
283
|
+
|
|
284
|
+
let theError = null;
|
|
285
|
+
// invoke transition handler
|
|
286
|
+
try {
|
|
287
|
+
await model.onRequestAction(model.fsm, { transactionRequestId, fspId, authorization });
|
|
288
|
+
throw new Error('this point should not be reached');
|
|
289
|
+
} catch (error) {
|
|
290
|
+
theError = error;
|
|
291
|
+
expect(theError).toEqual('postAuthorizations failed');
|
|
292
|
+
// handler should unsubscribe from notification channel
|
|
293
|
+
expect(cache.unsubscribe).toBeCalledTimes(1);
|
|
294
|
+
expect(cache.unsubscribe).toBeCalledWith(channel, subId);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
describe('run workflow', () => {
|
|
301
|
+
it('start', async () => {
|
|
302
|
+
const transactionRequestId = uuid();
|
|
303
|
+
const fspId = uuid();
|
|
304
|
+
// our code takes care only about 'transactionRequestId' property
|
|
305
|
+
const authorization = { transactionRequestId };
|
|
306
|
+
|
|
307
|
+
const model = await Model.create(data, cacheKey, modelConfig);
|
|
308
|
+
|
|
309
|
+
model.requestAction = jest.fn();
|
|
310
|
+
model.getResponse = jest.fn(() => Promise.resolve({ the: 'response' }));
|
|
311
|
+
|
|
312
|
+
model.context.data.currentState = 'start';
|
|
313
|
+
const result = await model.run({ transactionRequestId, fspId, authorization });
|
|
314
|
+
expect(result).toEqual({ the: 'response' });
|
|
315
|
+
expect(model.requestAction).toBeCalledTimes(1);
|
|
316
|
+
expect(model.getResponse).toBeCalledTimes(1);
|
|
317
|
+
expect(model.context.logger.log.mock.calls).toEqual([
|
|
318
|
+
['State machine transitioned \'init\': none -> start'],
|
|
319
|
+
['Action called successfully'],
|
|
320
|
+
[`Persisted model in cache: ${cacheKey}`],
|
|
321
|
+
]);
|
|
322
|
+
});
|
|
323
|
+
it('succeeded', async () => {
|
|
324
|
+
const transactionRequestId = uuid();
|
|
325
|
+
const fspId = uuid();
|
|
326
|
+
// our code takes care only about 'transactionRequestId' property
|
|
327
|
+
const authorization = { transactionRequestId };
|
|
328
|
+
|
|
329
|
+
const model = await Model.create(data, cacheKey, modelConfig);
|
|
330
|
+
|
|
331
|
+
model.getResponse = jest.fn(() => Promise.resolve({ the: 'response' }));
|
|
332
|
+
|
|
333
|
+
model.context.data.currentState = 'succeeded';
|
|
334
|
+
const result = await model.run({ transactionRequestId, fspId, authorization });
|
|
335
|
+
|
|
336
|
+
expect(result).toEqual({ the: 'response' });
|
|
337
|
+
expect(model.getResponse).toBeCalledTimes(1);
|
|
338
|
+
expect(model.context.logger.log).toBeCalledWith('Action called successfully');
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('errored', async () => {
|
|
342
|
+
const transactionRequestId = uuid();
|
|
343
|
+
const fspId = uuid();
|
|
344
|
+
// our code takes care only about 'transactionRequestId' property
|
|
345
|
+
const authorization = { transactionRequestId };
|
|
346
|
+
|
|
347
|
+
const model = await Model.create(data, cacheKey, modelConfig);
|
|
348
|
+
|
|
349
|
+
model.getResponse = jest.fn(() => Promise.resolve({ the: 'response' }));
|
|
350
|
+
|
|
351
|
+
model.context.data.currentState = 'errored';
|
|
352
|
+
const result = await model.run({ transactionRequestId, fspId, authorization });
|
|
353
|
+
|
|
354
|
+
expect(result).toBeFalsy();
|
|
355
|
+
expect(model.getResponse).not.toBeCalled();
|
|
356
|
+
expect(model.context.logger.log).toBeCalledWith('State machine in errored state');
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('handling errors', async () => {
|
|
360
|
+
const transactionRequestId = uuid();
|
|
361
|
+
const fspId = uuid();
|
|
362
|
+
// our code takes care only about 'transactionRequestId' property
|
|
363
|
+
const authorization = { transactionRequestId };
|
|
364
|
+
|
|
365
|
+
const model = await Model.create(data, cacheKey, modelConfig);
|
|
366
|
+
|
|
367
|
+
model.requestAction = jest.fn(() => { throw new Error('mocked error'); });
|
|
368
|
+
|
|
369
|
+
model.context.data.currentState = 'start';
|
|
370
|
+
|
|
371
|
+
try {
|
|
372
|
+
await model.run({ transactionRequestId, fspId, authorization });
|
|
373
|
+
throw new Error('this point should not be reached');
|
|
374
|
+
} catch (err) {
|
|
375
|
+
expect(model.context.data.currentState).toEqual('errored');
|
|
376
|
+
expect(err.requestActionState).toEqual({
|
|
377
|
+
...data,
|
|
378
|
+
currentState: 'ERROR_OCCURRED',
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('should handle errors', async () => {
|
|
384
|
+
const transactionRequestId = uuid();
|
|
385
|
+
const fspId = uuid();
|
|
386
|
+
// our code takes care only about 'transactionRequestId' property
|
|
387
|
+
const authorization = { transactionRequestId };
|
|
388
|
+
|
|
389
|
+
const model = await Model.create(data, cacheKey, modelConfig);
|
|
390
|
+
|
|
391
|
+
model.requestAction = jest.fn(() => {
|
|
392
|
+
const err = new Error('requestAction failed');
|
|
393
|
+
err.requestActionState = 'some';
|
|
394
|
+
return Promise.reject(err);
|
|
395
|
+
});
|
|
396
|
+
model.error = jest.fn();
|
|
397
|
+
model.context.data.currentState = 'start';
|
|
398
|
+
|
|
399
|
+
let theError = null;
|
|
400
|
+
try {
|
|
401
|
+
await model.run({ transactionRequestId, fspId, authorization });
|
|
402
|
+
throw new Error('this point should not be reached');
|
|
403
|
+
} catch (error) {
|
|
404
|
+
theError = error;
|
|
405
|
+
}
|
|
406
|
+
// check propagation of original error
|
|
407
|
+
expect(theError.message).toEqual('requestAction failed');
|
|
408
|
+
|
|
409
|
+
// ensure we start transition to errored state
|
|
410
|
+
expect(model.error).toBeCalledTimes(1);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it('should handle input validation for lack of transactionRequestId param', async () => {
|
|
414
|
+
const model = await Model.create(data, cacheKey, modelConfig);
|
|
415
|
+
|
|
416
|
+
expect(() => model.run({}))
|
|
417
|
+
.rejects.toEqual(
|
|
418
|
+
new Error('AuthorizationsModel args requires \'transactionRequestId\' is nonempty string and mandatory property')
|
|
419
|
+
);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('should handle input validation for fspId param', async () => {
|
|
423
|
+
const transactionRequestId = uuid();
|
|
424
|
+
const model = await Model.create(data, cacheKey, modelConfig);
|
|
425
|
+
|
|
426
|
+
expect(() => model.run({ transactionRequestId, fspId: '' }))
|
|
427
|
+
.rejects.toEqual(
|
|
428
|
+
new Error('AuthorizationsModel args requires \'fspId\' to be nonempty string')
|
|
429
|
+
);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
describe('loadFromCache', () => {
|
|
435
|
+
test('should use PSM.loadFromCache properly', async () => {
|
|
436
|
+
const spyLoadFromCache = jest.spyOn(PSM, 'loadFromCache');
|
|
437
|
+
const key = uuid();
|
|
438
|
+
|
|
439
|
+
// act
|
|
440
|
+
const model = await Model.loadFromCache(key, modelConfig);
|
|
441
|
+
|
|
442
|
+
// assert
|
|
443
|
+
// check does model is proper
|
|
444
|
+
expect(typeof model.requestAction).toEqual('function');
|
|
445
|
+
|
|
446
|
+
// check how cache.get has been called
|
|
447
|
+
expect(modelConfig.cache.get).toBeCalledWith(key);
|
|
448
|
+
|
|
449
|
+
// check how loadFromCache from parent PSM module was used
|
|
450
|
+
expect(spyLoadFromCache).toBeCalledTimes(1);
|
|
451
|
+
expect(spyLoadFromCache).toBeCalledWith(
|
|
452
|
+
modelConfig.cache,
|
|
453
|
+
key,
|
|
454
|
+
modelConfig.logger,
|
|
455
|
+
expect.anything(),
|
|
456
|
+
expect.anything()
|
|
457
|
+
);
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
});
|