@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.
Files changed (277) hide show
  1. package/.env.example +140 -0
  2. package/.eslintignore +2 -0
  3. package/.eslintrc.json +30 -0
  4. package/.nvmrc +1 -0
  5. package/.versionrc +15 -0
  6. package/CHANGELOG.md +118 -0
  7. package/InboundServer/api.yaml +3594 -0
  8. package/InboundServer/api_template.yaml +69 -0
  9. package/InboundServer/handlers.js +940 -0
  10. package/InboundServer/index.js +205 -0
  11. package/InboundServer/middlewares.js +426 -0
  12. package/OAuthTestServer/index.js +66 -0
  13. package/OAuthTestServer/model.js +70 -0
  14. package/OutboundServer/api.yaml +2732 -0
  15. package/OutboundServer/api_interfaces/index.d.ts +117 -0
  16. package/OutboundServer/api_interfaces/openapi.d.ts +1475 -0
  17. package/OutboundServer/api_template/components/parameters/bulkQuoteId.yaml +9 -0
  18. package/OutboundServer/api_template/components/parameters/bulkTransferId.yaml +9 -0
  19. package/OutboundServer/api_template/components/parameters/requestToPayTransactionId.yaml +9 -0
  20. package/OutboundServer/api_template/components/parameters/transferId.yaml +9 -0
  21. package/OutboundServer/api_template/components/responses/accountsCreationCompleted.yaml +5 -0
  22. package/OutboundServer/api_template/components/responses/accountsCreationError.yaml +5 -0
  23. package/OutboundServer/api_template/components/responses/accountsCreationTimeout.yaml +5 -0
  24. package/OutboundServer/api_template/components/responses/authorizationPostSuccess.yaml +5 -0
  25. package/OutboundServer/api_template/components/responses/authorizationsServerError.yaml +5 -0
  26. package/OutboundServer/api_template/components/responses/bulkQuoteBadRequest.yaml +5 -0
  27. package/OutboundServer/api_template/components/responses/bulkQuoteServerError.yaml +5 -0
  28. package/OutboundServer/api_template/components/responses/bulkQuoteSuccess.yaml +5 -0
  29. package/OutboundServer/api_template/components/responses/bulkQuoteTimeout.yaml +5 -0
  30. package/OutboundServer/api_template/components/responses/bulkTransferBadRequest.yaml +5 -0
  31. package/OutboundServer/api_template/components/responses/bulkTransferServerError.yaml +5 -0
  32. package/OutboundServer/api_template/components/responses/bulkTransferSuccess.yaml +5 -0
  33. package/OutboundServer/api_template/components/responses/bulkTransferTimeout.yaml +5 -0
  34. package/OutboundServer/api_template/components/responses/partiesByIdError404.yaml +9 -0
  35. package/OutboundServer/api_template/components/responses/partiesByIdSuccess.yaml +5 -0
  36. package/OutboundServer/api_template/components/responses/quotesPostSuccess.yaml +5 -0
  37. package/OutboundServer/api_template/components/responses/quotesServerError.yaml +5 -0
  38. package/OutboundServer/api_template/components/responses/requestToPaySuccess.yaml +5 -0
  39. package/OutboundServer/api_template/components/responses/requestToPayTransferBadRequest.yaml +5 -0
  40. package/OutboundServer/api_template/components/responses/requestToPayTransferSuccess.yaml +5 -0
  41. package/OutboundServer/api_template/components/responses/simpleTransfersPostSuccess.yaml +5 -0
  42. package/OutboundServer/api_template/components/responses/simpleTransfersServerError.yaml +5 -0
  43. package/OutboundServer/api_template/components/responses/transferBadRequest.yaml +5 -0
  44. package/OutboundServer/api_template/components/responses/transferServerError.yaml +5 -0
  45. package/OutboundServer/api_template/components/responses/transferSuccess.yaml +5 -0
  46. package/OutboundServer/api_template/components/responses/transferTimeout.yaml +5 -0
  47. package/OutboundServer/api_template/components/schemas/accountCreationStatus.yaml +18 -0
  48. package/OutboundServer/api_template/components/schemas/accountsCreationState.yaml +4 -0
  49. package/OutboundServer/api_template/components/schemas/accountsRequest.yaml +20 -0
  50. package/OutboundServer/api_template/components/schemas/accountsResponse.yaml +15 -0
  51. package/OutboundServer/api_template/components/schemas/async2SyncCurrentState.yaml +5 -0
  52. package/OutboundServer/api_template/components/schemas/authorizationsPostRequest.yaml +15 -0
  53. package/OutboundServer/api_template/components/schemas/authorizationsPostResponse.yaml +19 -0
  54. package/OutboundServer/api_template/components/schemas/bulkQuoteErrorResponse.yaml +8 -0
  55. package/OutboundServer/api_template/components/schemas/bulkQuoteRequest.yaml +26 -0
  56. package/OutboundServer/api_template/components/schemas/bulkQuoteResponse.yaml +21 -0
  57. package/OutboundServer/api_template/components/schemas/bulkQuoteStatus.yaml +4 -0
  58. package/OutboundServer/api_template/components/schemas/bulkQuoteStatusResponse.yaml +17 -0
  59. package/OutboundServer/api_template/components/schemas/bulkTransferErrorResponse.yaml +8 -0
  60. package/OutboundServer/api_template/components/schemas/bulkTransferRequest.yaml +26 -0
  61. package/OutboundServer/api_template/components/schemas/bulkTransferResponse.yaml +16 -0
  62. package/OutboundServer/api_template/components/schemas/bulkTransferStatus.yaml +4 -0
  63. package/OutboundServer/api_template/components/schemas/bulkTransferStatusResponse.yaml +17 -0
  64. package/OutboundServer/api_template/components/schemas/errorAccountsResponse.yaml +8 -0
  65. package/OutboundServer/api_template/components/schemas/errorAuthorizationsResponse.yaml +3 -0
  66. package/OutboundServer/api_template/components/schemas/errorQuotesResponse.yaml +9 -0
  67. package/OutboundServer/api_template/components/schemas/errorResponse.yaml +8 -0
  68. package/OutboundServer/api_template/components/schemas/errorSimpleTransfersResponse.yaml +3 -0
  69. package/OutboundServer/api_template/components/schemas/errorTransferResponse.yaml +8 -0
  70. package/OutboundServer/api_template/components/schemas/extensionListEmptiable.yaml +6 -0
  71. package/OutboundServer/api_template/components/schemas/individualQuote.yaml +32 -0
  72. package/OutboundServer/api_template/components/schemas/individualQuoteResult.yaml +28 -0
  73. package/OutboundServer/api_template/components/schemas/individualTransfer.yaml +32 -0
  74. package/OutboundServer/api_template/components/schemas/individualTransferFulfilment.yaml +13 -0
  75. package/OutboundServer/api_template/components/schemas/individualTransferResult.yaml +41 -0
  76. package/OutboundServer/api_template/components/schemas/mojaloopError.yaml +5 -0
  77. package/OutboundServer/api_template/components/schemas/mojaloopTransactionRequestState.yaml +2 -0
  78. package/OutboundServer/api_template/components/schemas/partiesByIdResponse.yaml +13 -0
  79. package/OutboundServer/api_template/components/schemas/quote.yaml +3 -0
  80. package/OutboundServer/api_template/components/schemas/quoteError.yaml +16 -0
  81. package/OutboundServer/api_template/components/schemas/quotesPostRequest.yaml +13 -0
  82. package/OutboundServer/api_template/components/schemas/quotesPostResponse.yaml +48 -0
  83. package/OutboundServer/api_template/components/schemas/requestToPayRequest.yaml +39 -0
  84. package/OutboundServer/api_template/components/schemas/requestToPayResponse.yaml +41 -0
  85. package/OutboundServer/api_template/components/schemas/requestToPayTransferRequest.yaml +42 -0
  86. package/OutboundServer/api_template/components/schemas/requestToPayTransferResponse.yaml +58 -0
  87. package/OutboundServer/api_template/components/schemas/simpleTransferServerError.yaml +5 -0
  88. package/OutboundServer/api_template/components/schemas/simpleTransfersPostRequest.yaml +12 -0
  89. package/OutboundServer/api_template/components/schemas/simpleTransfersPostResponse.yaml +11 -0
  90. package/OutboundServer/api_template/components/schemas/transactionType.yaml +4 -0
  91. package/OutboundServer/api_template/components/schemas/transferContinuationAcceptOTP.yaml +9 -0
  92. package/OutboundServer/api_template/components/schemas/transferContinuationAcceptParty.yaml +8 -0
  93. package/OutboundServer/api_template/components/schemas/transferContinuationAcceptQuote.yaml +9 -0
  94. package/OutboundServer/api_template/components/schemas/transferError.yaml +16 -0
  95. package/OutboundServer/api_template/components/schemas/transferFulfilment.yaml +3 -0
  96. package/OutboundServer/api_template/components/schemas/transferParty.yaml +40 -0
  97. package/OutboundServer/api_template/components/schemas/transferRequest.yaml +37 -0
  98. package/OutboundServer/api_template/components/schemas/transferResponse.yaml +58 -0
  99. package/OutboundServer/api_template/components/schemas/transferStatus.yaml +6 -0
  100. package/OutboundServer/api_template/components/schemas/transferStatusResponse.yaml +13 -0
  101. package/OutboundServer/api_template/health.yaml +12 -0
  102. package/OutboundServer/api_template/openapi.yaml +55 -0
  103. package/OutboundServer/api_template/paths/accounts.yaml +26 -0
  104. package/OutboundServer/api_template/paths/authorizations.yaml +19 -0
  105. package/OutboundServer/api_template/paths/bulkQuotes.yaml +23 -0
  106. package/OutboundServer/api_template/paths/bulkQuotes_bulkQuoteId.yaml +24 -0
  107. package/OutboundServer/api_template/paths/bulkTransfers.yaml +23 -0
  108. package/OutboundServer/api_template/paths/bulkTransfers_bulkTransferId.yaml +24 -0
  109. package/OutboundServer/api_template/paths/parties_Type_ID.yaml +20 -0
  110. package/OutboundServer/api_template/paths/parties_Type_ID_SubId.yaml +22 -0
  111. package/OutboundServer/api_template/paths/quotes.yaml +20 -0
  112. package/OutboundServer/api_template/paths/requestToPay.yaml +22 -0
  113. package/OutboundServer/api_template/paths/requestToPayTransfer.yaml +57 -0
  114. package/OutboundServer/api_template/paths/requestToPayTransfer_requestToPayTransactionId.yaml +34 -0
  115. package/OutboundServer/api_template/paths/simpleTransfers.yaml +19 -0
  116. package/OutboundServer/api_template/paths/transfers.yaml +55 -0
  117. package/OutboundServer/api_template/paths/transfers_transferId.yaml +58 -0
  118. package/OutboundServer/handlers.js +622 -0
  119. package/OutboundServer/index.js +137 -0
  120. package/OutboundServer/middlewares.js +67 -0
  121. package/TestServer/api.yaml +62 -0
  122. package/TestServer/handlers.js +63 -0
  123. package/TestServer/index.js +215 -0
  124. package/audit-resolve.json +65 -0
  125. package/babel.config.js +3 -0
  126. package/config.js +158 -0
  127. package/index.d.ts +1 -0
  128. package/index.js +149 -0
  129. package/jest.config.js +15 -0
  130. package/lib/api/index.js +12 -0
  131. package/lib/cache.js +352 -0
  132. package/lib/check.js +25 -0
  133. package/lib/model/AccountsModel.js +396 -0
  134. package/lib/model/Async2SyncModel.js +283 -0
  135. package/lib/model/AuthorizationsModel.js +86 -0
  136. package/lib/model/InboundTransfersModel.js +730 -0
  137. package/lib/model/OutboundBulkQuotesModel.js +485 -0
  138. package/lib/model/OutboundBulkTransfersModel.js +479 -0
  139. package/lib/model/OutboundRequestToPayModel.js +517 -0
  140. package/lib/model/OutboundRequestToPayTransferModel.js +893 -0
  141. package/lib/model/OutboundTransfersModel.js +823 -0
  142. package/lib/model/PartiesModel.js +70 -0
  143. package/lib/model/ProxyModel/MatchRules/Expression.js +48 -0
  144. package/lib/model/ProxyModel/MatchRules/Headers.js +65 -0
  145. package/lib/model/ProxyModel/MatchRules/MatchRule.js +27 -0
  146. package/lib/model/ProxyModel/MatchRules/Path.js +36 -0
  147. package/lib/model/ProxyModel/MatchRules/Query.js +65 -0
  148. package/lib/model/ProxyModel/MatchRules/index.js +19 -0
  149. package/lib/model/ProxyModel/Route.js +82 -0
  150. package/lib/model/ProxyModel/configSchema.json +118 -0
  151. package/lib/model/ProxyModel/index.js +138 -0
  152. package/lib/model/QuotesModel.js +94 -0
  153. package/lib/model/TransfersModel.js +81 -0
  154. package/lib/model/common/BackendError.js +26 -0
  155. package/lib/model/common/PersistentStateMachine.js +93 -0
  156. package/lib/model/common/index.js +18 -0
  157. package/lib/model/index.js +43 -0
  158. package/lib/model/lib/deferredJob.js +113 -0
  159. package/lib/model/lib/index.js +9 -0
  160. package/lib/model/lib/requests/backendRequests.js +227 -0
  161. package/lib/model/lib/requests/common.js +76 -0
  162. package/lib/model/lib/requests/index.js +19 -0
  163. package/lib/model/lib/shared.js +468 -0
  164. package/lib/randomphrase/index.js +21 -0
  165. package/lib/randomphrase/words.json +3397 -0
  166. package/lib/router.js +28 -0
  167. package/lib/validate.js +205 -0
  168. package/package.json +102 -0
  169. package/test/__mocks__/@mojaloop/sdk-standard-components.js +152 -0
  170. package/test/__mocks__/javascript-state-machine.js +21 -0
  171. package/test/__mocks__/redis.js +49 -0
  172. package/test/__mocks__/uuidv4.js +16 -0
  173. package/test/config/integration.env +136 -0
  174. package/test/integration/lib/Outbound/authorizations.test.js +58 -0
  175. package/test/integration/lib/Outbound/data/authorizationsPostRequest.json +43 -0
  176. package/test/integration/lib/Outbound/data/quotesPostRequest.json +52 -0
  177. package/test/integration/lib/Outbound/data/transfersPostRequest.json +24 -0
  178. package/test/integration/lib/Outbound/parties.test.js +28 -0
  179. package/test/integration/lib/Outbound/quotes.test.js +58 -0
  180. package/test/integration/lib/Outbound/simpleTransfers.test.js +67 -0
  181. package/test/integration/lib/cache.test.js +80 -0
  182. package/test/integration/testEnv.js +7 -0
  183. package/test/unit/InboundServer.test.js +443 -0
  184. package/test/unit/TestServer.test.js +394 -0
  185. package/test/unit/api/accounts/accounts.test.js +128 -0
  186. package/test/unit/api/accounts/data/postAccountsBody.json +7 -0
  187. package/test/unit/api/accounts/data/postAccountsErrorMojaloopResponse.json +25 -0
  188. package/test/unit/api/accounts/data/postAccountsErrorTimeoutResponse.json +19 -0
  189. package/test/unit/api/accounts/data/postAccountsSuccessResponse.json +17 -0
  190. package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError1.json +21 -0
  191. package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError2.json +21 -0
  192. package/test/unit/api/accounts/utils.js +65 -0
  193. package/test/unit/api/proxy/data/proxyConfig.yaml +82 -0
  194. package/test/unit/api/proxy/data/requestBody.json +22 -0
  195. package/test/unit/api/proxy/data/requestHeaders.json +5 -0
  196. package/test/unit/api/proxy/data/requestQuery.json +6 -0
  197. package/test/unit/api/proxy/data/responseBody.json +21 -0
  198. package/test/unit/api/proxy/data/responseHeaders.json +5 -0
  199. package/test/unit/api/proxy/proxy.test.js +220 -0
  200. package/test/unit/api/proxy/utils.js +79 -0
  201. package/test/unit/api/transfers/data/getTransfersCommittedResponse.json +21 -0
  202. package/test/unit/api/transfers/data/getTransfersErrorNotFound.json +17 -0
  203. package/test/unit/api/transfers/data/postQuotesBody.json +52 -0
  204. package/test/unit/api/transfers/data/postTransfersBadBody.json +17 -0
  205. package/test/unit/api/transfers/data/postTransfersBody.json +24 -0
  206. package/test/unit/api/transfers/data/postTransfersErrorMojaloopResponse.json +53 -0
  207. package/test/unit/api/transfers/data/postTransfersErrorTimeoutResponse.json +47 -0
  208. package/test/unit/api/transfers/data/postTransfersSimpleBody.json +26 -0
  209. package/test/unit/api/transfers/data/postTransfersSuccessResponse.json +101 -0
  210. package/test/unit/api/transfers/data/putPartiesBody.json +20 -0
  211. package/test/unit/api/transfers/data/putQuotesBody.json +37 -0
  212. package/test/unit/api/transfers/data/putTransfersBody.json +17 -0
  213. package/test/unit/api/transfers/transfers.test.js +191 -0
  214. package/test/unit/api/transfers/utils.js +183 -0
  215. package/test/unit/api/utils.js +75 -0
  216. package/test/unit/config.test.js +119 -0
  217. package/test/unit/data/commonHttpHeaders.json +6 -0
  218. package/test/unit/data/defaultConfig.json +58 -0
  219. package/test/unit/data/postQuotesBody.json +52 -0
  220. package/test/unit/data/putParticipantsBody.json +12 -0
  221. package/test/unit/data/putPartiesBody.json +20 -0
  222. package/test/unit/data/testFile.json +29 -0
  223. package/test/unit/data/testFile.yaml +14 -0
  224. package/test/unit/inboundApi/data/mockArguments.json +117 -0
  225. package/test/unit/inboundApi/data/mockTransactionRequest.json +42 -0
  226. package/test/unit/inboundApi/handlers.test.js +799 -0
  227. package/test/unit/index.test.js +55 -0
  228. package/test/unit/lib/cache.test.js +146 -0
  229. package/test/unit/lib/model/AccountsModel.test.js +121 -0
  230. package/test/unit/lib/model/AuthorizationsModel.test.js +460 -0
  231. package/test/unit/lib/model/InboundTransfersModel.test.js +628 -0
  232. package/test/unit/lib/model/OutboundBulkQuotesModel.test.js +249 -0
  233. package/test/unit/lib/model/OutboundBulkTransfersModel.test.js +244 -0
  234. package/test/unit/lib/model/OutboundRequestToPayModel.test.js +166 -0
  235. package/test/unit/lib/model/OutboundRequestToPayTransferModel.test.js +245 -0
  236. package/test/unit/lib/model/OutboundTransfersModel.test.js +836 -0
  237. package/test/unit/lib/model/PartiesModel.test.js +468 -0
  238. package/test/unit/lib/model/QuotesModel.test.js +470 -0
  239. package/test/unit/lib/model/TransfersModel.test.js +474 -0
  240. package/test/unit/lib/model/common/PersistentStateMachine.test.js +179 -0
  241. package/test/unit/lib/model/data/authorizationsResponse.json +13 -0
  242. package/test/unit/lib/model/data/bulkQuoteRequest.json +27 -0
  243. package/test/unit/lib/model/data/bulkQuoteResponse.json +35 -0
  244. package/test/unit/lib/model/data/bulkTransferFulfil.json +13 -0
  245. package/test/unit/lib/model/data/bulkTransferRequest.json +29 -0
  246. package/test/unit/lib/model/data/defaultConfig.json +47 -0
  247. package/test/unit/lib/model/data/getBulkTransfersBackendResponse.json +42 -0
  248. package/test/unit/lib/model/data/getBulkTransfersMojaloopResponse.json +22 -0
  249. package/test/unit/lib/model/data/getTransfersBackendResponse.json +34 -0
  250. package/test/unit/lib/model/data/getTransfersMojaloopResponse.json +17 -0
  251. package/test/unit/lib/model/data/mockArguments.json +131 -0
  252. package/test/unit/lib/model/data/mockTxnRequestsArguments.json +63 -0
  253. package/test/unit/lib/model/data/notificationToPayee.json +10 -0
  254. package/test/unit/lib/model/data/payeeParty.json +16 -0
  255. package/test/unit/lib/model/data/putAuthorizationsResponse.json +10 -0
  256. package/test/unit/lib/model/data/putQuotesResponse.json +33 -0
  257. package/test/unit/lib/model/data/putTransfersResponse.json +5 -0
  258. package/test/unit/lib/model/data/quoteResponse.json +31 -0
  259. package/test/unit/lib/model/data/requestToPayRequest.json +20 -0
  260. package/test/unit/lib/model/data/requestToPayTransferRequest.json +27 -0
  261. package/test/unit/lib/model/data/transactionRequestResponse.json +18 -0
  262. package/test/unit/lib/model/data/transferFulfil.json +8 -0
  263. package/test/unit/lib/model/data/transferRequest.json +26 -0
  264. package/test/unit/lib/model/mockedLibRequests.js +74 -0
  265. package/test/unit/mockLogger.js +39 -0
  266. package/test/unit/outboundApi/data/bulkQuoteRequest.json +28 -0
  267. package/test/unit/outboundApi/data/bulkTransferRequest.json +28 -0
  268. package/test/unit/outboundApi/data/mockBulkQuoteError.json +45 -0
  269. package/test/unit/outboundApi/data/mockBulkTransferError.json +48 -0
  270. package/test/unit/outboundApi/data/mockError.json +41 -0
  271. package/test/unit/outboundApi/data/mockGetPartiesError.json +4 -0
  272. package/test/unit/outboundApi/data/mockRequestToPayError.json +32 -0
  273. package/test/unit/outboundApi/data/mockRequestToPayTransferError.json +39 -0
  274. package/test/unit/outboundApi/data/requestToPay.json +21 -0
  275. package/test/unit/outboundApi/data/requestToPayTransferRequest.json +20 -0
  276. package/test/unit/outboundApi/data/transferRequest.json +21 -0
  277. package/test/unit/outboundApi/handlers.test.js +986 -0
@@ -0,0 +1,474 @@
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
+ * Kevin Leyow - kevin.leyow@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').TransfersModel;
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 putTransfersResponse = require('./data/putTransfersResponse.json');
25
+
26
+ describe('TransfersModel', () => {
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: 'TransfersModel-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-transfers-${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(`TransfersModel 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('transfers-undefined');
111
+ });
112
+
113
+ it('should generate proper channel name', () => {
114
+ const transferId = uuid();
115
+ expect(Model.channelName({ transferId })).toEqual(`transfers-${transferId}`);
116
+ });
117
+
118
+ });
119
+
120
+ describe('generateKey', () => {
121
+ it('should generate proper cache key', () => {
122
+ const transferId = uuid();
123
+ expect(Model.generateKey({ transferId })).toEqual(`key-${Model.channelName({ transferId })}`);
124
+ });
125
+
126
+ it('should handle lack of transferId param', () => {
127
+ expect(() => Model.generateKey({ })).toThrowError(new Error('TransfersModel args requires \'transferId\' is nonempty string and mandatory property'));
128
+ });
129
+
130
+ });
131
+
132
+ describe('onRequestAction', () => {
133
+
134
+ it('should implement happy flow', async () => {
135
+ const transferId = uuid();
136
+ const fspId = uuid();
137
+ // our code takes care only about 'transferId' property
138
+ const transfer = { transferId };
139
+ const channel = Model.channelName({ transferId });
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 = { ...putTransfersResponse };
146
+
147
+ const onRequestActionPromise = new Promise((resolve, reject) => {
148
+ // manually invoke transition handler
149
+ model.onRequestAction(model.fsm, { transferId, fspId, transfer })
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.__postTransfers).toBeCalledWith(transfer, fspId);
159
+
160
+ // check that this.context.data is updated
161
+ expect(model.context.data).toEqual({
162
+ transfer: { ...message },
163
+ // current state will be updated by onAfterTransition which isn't called
164
+ // when manual invocation of transition handler happens
165
+ currentState: 'start'
166
+ });
167
+ // handler should be called only once
168
+ expect(handler).toBeCalledTimes(1);
169
+
170
+ // handler should unsubscribe from notification channel
171
+ expect(cache.unsubscribe).toBeCalledTimes(1);
172
+ expect(cache.unsubscribe).toBeCalledWith(channel, subId);
173
+ resolve();
174
+ }).catch((err) => { reject(err); } );
175
+ });
176
+
177
+ // ensure handler wasn't called before publishing the message
178
+ expect(handler).not.toBeCalled();
179
+
180
+ // ensure that cache.unsubscribe does not happened before fire the message
181
+ expect(cache.unsubscribe).not.toBeCalled();
182
+
183
+ // fire publication with given message
184
+ const df = deferredJob(cache, channel);
185
+ setImmediate(() => df.trigger(message));
186
+
187
+ // wait for onRequestAction
188
+ await onRequestActionPromise;
189
+ });
190
+
191
+ it('should handle timeouts', async () => {
192
+ const transferId = uuid();
193
+ const fspId = uuid();
194
+ // our code takes care only about 'transferId' property
195
+ const transfer = { transferId };
196
+ const channel = Model.channelName({ transferId });
197
+
198
+ const model = await Model.create(data, cacheKey, modelConfig);
199
+ const { cache } = model.context;
200
+ // mock workflow execution which is tested in separate case
201
+ model.run = jest.fn(() => Promise.resolve());
202
+
203
+ const message = { ...putTransfersResponse };
204
+
205
+ const onRequestActionPromise = new Promise((resolve, reject) => {
206
+ // manually invoke transition handler
207
+ model.onRequestAction(model.fsm, { transferId, fspId, transfer })
208
+ .then(() => reject())
209
+ .catch((err) => {
210
+ // subscribe should be called only once
211
+ expect(err instanceof pt.TimeoutError).toBeTruthy();
212
+
213
+ // subscribe should be done to proper notificationChannel
214
+ expect(cache.subscribe.mock.calls[0][0]).toEqual(channel);
215
+
216
+ // check invocation of request.getParties
217
+ expect(MojaloopRequests.__postTransfers).toBeCalledWith(transfer, fspId);
218
+
219
+ // handler should be called only once
220
+ expect(handler).toBeCalledTimes(0);
221
+
222
+ // handler should unsubscribe from notification channel
223
+ expect(cache.unsubscribe).toBeCalledTimes(1);
224
+ expect(cache.unsubscribe).toBeCalledWith(channel, subId);
225
+ resolve();
226
+ });
227
+ });
228
+
229
+ // ensure handler wasn't called before publishing the message
230
+ expect(handler).not.toBeCalled();
231
+
232
+ // ensure that cache.unsubscribe does not happened before fire the message
233
+ expect(cache.unsubscribe).not.toBeCalled();
234
+
235
+ // fire publication with given message
236
+ const df = deferredJob(cache, channel);
237
+
238
+ setTimeout(
239
+ () => { df.trigger(message); },
240
+ // ensure that publication will be far long after timeout should be auto triggered
241
+ (modelConfig.requestProcessingTimeoutSeconds+1)*1000
242
+ );
243
+
244
+ // wait for onRequestAction
245
+ await onRequestActionPromise;
246
+ });
247
+
248
+ it('should unsubscribe from cache in case when error happens in workflow run', async () => {
249
+ const transferId = uuid();
250
+ const fspId = uuid();
251
+ // our code takes care only about 'transferId' property
252
+ const transfer = { transferId };
253
+ const channel = Model.channelName({ transferId });
254
+ const model = await Model.create(data, cacheKey, modelConfig);
255
+ const { cache } = model.context;
256
+
257
+ const onRequestActionPromise = new Promise((resolve, reject) => {
258
+ // invoke transition handler
259
+ model.onRequestAction(model.fsm, { transferId, fspId, transfer })
260
+ .then(() => reject())
261
+ .catch((err) => {
262
+ expect(err.message).toEqual('Unexpected token u in JSON at position 0');
263
+ expect(cache.unsubscribe).toBeCalledTimes(1);
264
+ expect(cache.unsubscribe).toBeCalledWith(channel, subId);
265
+ resolve();
266
+ });
267
+ });
268
+
269
+ // fire publication to channel with invalid message
270
+ // should throw the exception from JSON.parse
271
+ const df = deferredJob(cache, channel);
272
+ setImmediate(() => df.trigger(undefined));
273
+
274
+ // wait for onRequestAction
275
+ await onRequestActionPromise;
276
+ });
277
+
278
+ it('should unsubscribe from cache in case when error happens Mojaloop requests', async () => {
279
+ // simulate error
280
+ MojaloopRequests.__postTransfers = jest.fn(() => Promise.reject('postTransfers failed'));
281
+ const transferId = uuid();
282
+ const fspId = uuid();
283
+ // our code takes care only about 'transferId' property
284
+ const transfer = { transferId };
285
+ const channel = Model.channelName({ transferId });
286
+ const model = await Model.create(data, cacheKey, modelConfig);
287
+ const { cache } = model.context;
288
+
289
+ let theError = null;
290
+ // invoke transition handler
291
+ try {
292
+ await model.onRequestAction(model.fsm, { transferId, fspId, transfer });
293
+ throw new Error('this point should not be reached');
294
+ } catch (error) {
295
+ theError = error;
296
+ expect(theError).toEqual('postTransfers failed');
297
+ // handler should unsubscribe from notification channel
298
+ expect(cache.unsubscribe).toBeCalledTimes(1);
299
+ expect(cache.unsubscribe).toBeCalledWith(channel, subId);
300
+ }
301
+ });
302
+
303
+ });
304
+
305
+ describe('run workflow', () => {
306
+ it('start', async () => {
307
+ const transferId = uuid();
308
+ const fspId = uuid();
309
+ // our code takes care only about 'transferId' property
310
+ const transfer = { transferId };
311
+
312
+ const model = await Model.create(data, cacheKey, modelConfig);
313
+
314
+ model.requestAction = jest.fn();
315
+ model.getResponse = jest.fn(() => Promise.resolve({the: 'response'}));
316
+
317
+ model.context.data.currentState = 'start';
318
+ const result = await model.run({ transferId, fspId, transfer });
319
+ expect(result).toEqual({the: 'response'});
320
+ expect(model.requestAction).toBeCalledTimes(1);
321
+ expect(model.getResponse).toBeCalledTimes(1);
322
+ expect(model.context.logger.log.mock.calls).toEqual([
323
+ ['State machine transitioned \'init\': none -> start'],
324
+ ['Action called successfully'],
325
+ [`Persisted model in cache: ${cacheKey}`],
326
+ ]);
327
+ });
328
+ it('succeeded', async () => {
329
+ const transferId = uuid();
330
+ const fspId = uuid();
331
+ // our code takes care only about 'transferId' property
332
+ const transfer = { transferId };
333
+
334
+ const model = await Model.create(data, cacheKey, modelConfig);
335
+
336
+ model.getResponse = jest.fn(() => Promise.resolve({the: 'response'}));
337
+
338
+ model.context.data.currentState = 'succeeded';
339
+ const result = await model.run({ transferId, fspId, transfer });
340
+
341
+ expect(result).toEqual({the: 'response'});
342
+ expect(model.getResponse).toBeCalledTimes(1);
343
+ expect(model.context.logger.log).toBeCalledWith('Action called successfully');
344
+ });
345
+
346
+ it('errored', async () => {
347
+ const transferId = uuid();
348
+ const fspId = uuid();
349
+ // our code takes care only about 'transferId' property
350
+ const transfer = { transferId };
351
+
352
+ const model = await Model.create(data, cacheKey, modelConfig);
353
+
354
+ model.getResponse = jest.fn(() => Promise.resolve({the: 'response'}));
355
+
356
+ model.context.data.currentState = 'errored';
357
+ const result = await model.run({ transferId, fspId, transfer});
358
+
359
+ expect(result).toBeFalsy();
360
+ expect(model.getResponse).not.toBeCalled();
361
+ expect(model.context.logger.log).toBeCalledWith('State machine in errored state');
362
+ });
363
+
364
+ it('handling errors', async () => {
365
+ const transferId = uuid();
366
+ const fspId = uuid();
367
+ // our code takes care only about 'transferId' property
368
+ const transfer = { transferId };
369
+
370
+ const model = await Model.create(data, cacheKey, modelConfig);
371
+
372
+ model.requestAction = jest.fn(() => { throw new Error('mocked error'); });
373
+
374
+ model.context.data.currentState = 'start';
375
+
376
+ try {
377
+ await model.run({ transferId, fspId, transfer });
378
+ throw new Error('this point should not be reached');
379
+ } catch (err) {
380
+ expect(model.context.data.currentState).toEqual('errored');
381
+ expect(err.requestActionState).toEqual( {
382
+ ...data,
383
+ currentState: 'ERROR_OCCURRED',
384
+ });
385
+ }
386
+ });
387
+ it('should handle errors', async () => {
388
+ const transferId = uuid();
389
+ const fspId = uuid();
390
+ // our code takes care only about 'transferId' property
391
+ const transfer = { transferId };
392
+
393
+ const model = await Model.create(data, cacheKey, modelConfig);
394
+
395
+ model.requestAction = jest.fn(() => {
396
+ const err = new Error('requestAction failed');
397
+ err.requestActionState = 'some';
398
+ return Promise.reject(err);
399
+ });
400
+ model.error = jest.fn();
401
+ model.context.data.currentState = 'start';
402
+
403
+ let theError = null;
404
+ try {
405
+ await model.run({ transferId, fspId, transfer });
406
+ throw new Error('this point should not be reached');
407
+ } catch(error) {
408
+ theError = error;
409
+ }
410
+ // check propagation of original error
411
+ expect(theError.message).toEqual('requestAction failed');
412
+
413
+ // ensure we start transition to errored state
414
+ expect(model.error).toBeCalledTimes(1);
415
+ });
416
+
417
+ it('should handle input validation for lack of transferId param', async () => {
418
+ const model = await Model.create(data, cacheKey, modelConfig);
419
+
420
+ expect(() => model.run({}))
421
+ .rejects.toEqual(
422
+ new Error('TransfersModel args requires \'transferId\' is nonempty string and mandatory property')
423
+ );
424
+ });
425
+
426
+ it('should handle input validation for not transferId & transfer.transferId be in sync', async () => {
427
+ const transferId = uuid();
428
+ const model = await Model.create(data, cacheKey, modelConfig);
429
+
430
+ expect(() => model.run({transferId, transfer: { transferId: uuid()}}))
431
+ .rejects.toEqual(
432
+ new Error('TransfersModel args requires properties \'transfer.transferId\' and \'transferId\' to be the equal in value')
433
+ );
434
+ });
435
+
436
+ it('should handle input validation for fspId param', async () => {
437
+ const transferId = uuid();
438
+ const model = await Model.create(data, cacheKey, modelConfig);
439
+
440
+ expect(() => model.run({transferId, fspId:'' }))
441
+ .rejects.toEqual(
442
+ new Error('TransfersModel args requires \'fspId\' to be nonempty string')
443
+ );
444
+ });
445
+
446
+ });
447
+
448
+ describe('loadFromCache', () => {
449
+ test('should use PSM.loadFromCache properly', async () => {
450
+ const spyLoadFromCache = jest.spyOn(PSM, 'loadFromCache');
451
+ const key = uuid();
452
+
453
+ // act
454
+ const model = await Model.loadFromCache(key, modelConfig);
455
+
456
+ // assert
457
+ // check does model is proper
458
+ expect(typeof model.requestAction).toEqual('function');
459
+
460
+ // check how cache.get has been called
461
+ expect(modelConfig.cache.get).toBeCalledWith(key);
462
+
463
+ // check how loadFromCache from parent PSM module was used
464
+ expect(spyLoadFromCache).toBeCalledTimes(1);
465
+ expect(spyLoadFromCache).toBeCalledWith(
466
+ modelConfig.cache,
467
+ key,
468
+ modelConfig.logger,
469
+ expect.anything(),
470
+ expect.anything()
471
+ );
472
+ });
473
+ });
474
+ });