@mojaloop/central-ledger 9.2.2 → 15.1.3-snapshot.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.
Files changed (256) hide show
  1. package/.circleci/config.yml +625 -204
  2. package/.env +3 -2
  3. package/.ncurc.yaml +9 -0
  4. package/.nvmrc +1 -1
  5. package/.versionrc +15 -0
  6. package/CHANGELOG.md +353 -0
  7. package/CODEOWNERS +31 -0
  8. package/Dockerfile +20 -14
  9. package/LICENSE.md +4 -4
  10. package/Onboarding.md +0 -7
  11. package/README.md +99 -22
  12. package/audit-resolve.json +136 -1
  13. package/config/default.json +82 -27
  14. package/docker/central-ledger/default.json +68 -21
  15. package/docker/ml-api-adapter/default.json +10 -1
  16. package/docker/wait-for/wait-for-objstore.sh +1 -6
  17. package/docker/wait-for/wait-for.env +1 -1
  18. package/docker-compose.integration.yml +8 -5
  19. package/docker-compose.yml +73 -16
  20. package/migrations/310203_transferParticipant-indexes.js +38 -0
  21. package/migrations/310503_participantLimit-indexes-composite.js +38 -0
  22. package/migrations/{400401_settlementWindowStateChange-indexes.js → 400401_settlmentWindowStateChange-indexes.js} +0 -0
  23. package/migrations/401301_settlementModel-indexes.js +2 -0
  24. package/migrations/500401_quote-hotfix-2719.js +58 -0
  25. package/migrations/500501_feature-fixSubIdRef.js +53 -0
  26. package/migrations/500601_party-2480.js +63 -0
  27. package/migrations/501001_quoteExtension-quote-service-174.js +46 -0
  28. package/migrations/501002_quoteExtension-2522.js +61 -0
  29. package/migrations/800101_feature-fixSubIdRef.js +90 -0
  30. package/migrations/910102_hotfix954.js +4 -1
  31. package/migrations/940100_quotePartyIdInfoExtension.js +52 -0
  32. package/migrations/940101_quotePartyIdInfoExtension-2522.js +64 -0
  33. package/migrations/950100_settlementContentAggregation-decimal184.js +40 -0
  34. package/migrations/950101_transferParticipantStateChange.js +46 -0
  35. package/migrations/950102_settlementModel-adjustPosition.js +42 -0
  36. package/migrations/950103_dropTransferParticipantStateChange.js +47 -0
  37. package/migrations/950104_settlementModel-settlementAccountTypeId.js +79 -0
  38. package/migrations/950106_unique_settlement_model_ledger_account_currency.js +38 -0
  39. package/migrations/950107_settlementWindowContent_foreign_settlementModel.js +60 -0
  40. package/package.json +99 -81
  41. package/scripts/_wait4_all.js +146 -0
  42. package/seeds/amountType.js +4 -6
  43. package/seeds/balanceOfPayments.js +4 -6
  44. package/seeds/bulkProcessingState.js +4 -6
  45. package/seeds/bulkTransferState.js +4 -6
  46. package/seeds/currency.js +4 -6
  47. package/seeds/endpointType.js +128 -7
  48. package/seeds/ledgerAccountType.js +11 -10
  49. package/seeds/ledgerEntryType.js +5 -7
  50. package/seeds/participant.js +4 -6
  51. package/seeds/participantLimitType.js +4 -6
  52. package/seeds/partyIdentifierType.js +4 -6
  53. package/seeds/partyType.js +4 -6
  54. package/seeds/settlementDelay.js +4 -6
  55. package/seeds/settlementGranularity.js +4 -6
  56. package/seeds/settlementInterchange.js +4 -6
  57. package/seeds/settlementState.js +4 -6
  58. package/seeds/settlementWindow1State.js +4 -6
  59. package/seeds/transactionInitiator.js +4 -6
  60. package/seeds/transactionInitiatorType.js +4 -6
  61. package/seeds/transactionScenario.js +4 -6
  62. package/seeds/transferParticipantRoleType.js +4 -6
  63. package/seeds/transferState.js +9 -6
  64. package/src/api/interface/swagger.json +728 -948
  65. package/src/api/ledgerAccountTypes/handler.js +55 -0
  66. package/src/api/ledgerAccountTypes/routes.js +63 -0
  67. package/src/api/metrics/handler.js +2 -2
  68. package/src/api/metrics/routes.js +1 -1
  69. package/src/api/participants/handler.js +43 -35
  70. package/src/api/participants/routes.js +13 -13
  71. package/src/api/root/routes.js +2 -2
  72. package/src/api/settlementModels/handler.js +9 -26
  73. package/src/api/settlementModels/routes.js +4 -4
  74. package/src/api/transactions/handler.js +46 -0
  75. package/src/api/transactions/routes.js +45 -0
  76. package/src/domain/bulkTransfer/index.js +6 -5
  77. package/src/domain/ledgerAccountTypes/index.js +58 -0
  78. package/src/domain/participant/index.js +55 -4
  79. package/src/domain/position/index.js +18 -2
  80. package/src/domain/settlement/index.js +39 -6
  81. package/src/domain/transactions/index.js +61 -0
  82. package/src/domain/transfer/index.js +22 -2
  83. package/src/domain/transfer/transform.js +3 -3
  84. package/src/handlers/admin/handler.js +24 -32
  85. package/src/handlers/bulk/fulfil/handler.js +89 -56
  86. package/src/handlers/bulk/get/handler.js +203 -0
  87. package/src/handlers/bulk/index.js +4 -1
  88. package/src/handlers/bulk/prepare/handler.js +30 -29
  89. package/src/handlers/bulk/processing/handler.js +61 -32
  90. package/src/handlers/bulk/shared/validator.js +13 -1
  91. package/src/handlers/index.js +18 -9
  92. package/src/handlers/positions/handler.js +67 -46
  93. package/src/handlers/register.js +7 -7
  94. package/src/handlers/timeouts/handler.js +9 -4
  95. package/src/handlers/transfers/handler.js +408 -182
  96. package/src/handlers/transfers/validator.js +41 -11
  97. package/src/lib/cache.js +8 -34
  98. package/src/lib/config.js +5 -7
  99. package/src/lib/enum.js +22 -22
  100. package/src/lib/enumCached.js +76 -0
  101. package/src/lib/healthCheck/subServiceHealth.js +2 -2
  102. package/src/lib/requestLogger.js +9 -9
  103. package/src/lib/urlParser.js +1 -1
  104. package/src/models/bulkTransfer/bulkTransfer.js +6 -6
  105. package/src/models/bulkTransfer/bulkTransferAssociation.js +3 -3
  106. package/src/models/bulkTransfer/bulkTransferDuplicateCheck.js +4 -4
  107. package/src/models/bulkTransfer/bulkTransferExtension.js +2 -2
  108. package/src/models/bulkTransfer/bulkTransferFulfilmentDuplicateCheck.js +4 -4
  109. package/src/models/bulkTransfer/bulkTransferStateChange.js +7 -7
  110. package/src/models/bulkTransfer/facade.js +54 -3
  111. package/src/models/bulkTransfer/individualTransfer.js +2 -2
  112. package/src/models/ilpPackets/ilpPacket.js +36 -0
  113. package/src/models/ledgerAccountType/ledgerAccountType.js +139 -3
  114. package/src/models/misc/migrationLock.js +1 -1
  115. package/src/models/misc/segment.js +1 -1
  116. package/src/models/participant/facade.js +226 -76
  117. package/src/models/participant/participant.js +6 -5
  118. package/src/models/participant/participantCached.js +8 -0
  119. package/src/models/participant/participantCurrency.js +7 -7
  120. package/src/models/participant/participantCurrencyCached.js +11 -1
  121. package/src/models/participant/participantLimit.js +13 -13
  122. package/src/models/participant/participantLimitCached.js +124 -0
  123. package/src/models/participant/participantPosition.js +34 -5
  124. package/src/models/participant/participantPositionChange.js +10 -2
  125. package/src/models/position/facade.js +112 -18
  126. package/src/models/position/participantPosition.js +6 -6
  127. package/src/models/settlement/settlementModel.js +92 -17
  128. package/src/models/settlement/settlementModelCached.js +139 -0
  129. package/src/models/transfer/facade.js +97 -32
  130. package/src/models/transfer/ilpPacket.js +4 -4
  131. package/src/models/transfer/transfer.js +7 -7
  132. package/src/models/transfer/transferDuplicateCheck.js +21 -5
  133. package/src/models/transfer/transferError.js +5 -5
  134. package/src/models/transfer/transferErrorDuplicateCheck.js +4 -4
  135. package/src/models/transfer/transferExtension.js +4 -4
  136. package/src/models/transfer/transferFulfilment.js +5 -5
  137. package/src/models/transfer/transferFulfilmentDuplicateCheck.js +4 -4
  138. package/src/models/transfer/transferParticipant.js +3 -3
  139. package/src/models/transfer/transferStateChange.js +11 -11
  140. package/src/models/transfer/transferTimeout.js +5 -5
  141. package/src/schema/bulkTransfer.js +4 -4
  142. package/src/shared/plugins.js +10 -9
  143. package/src/shared/setup.js +69 -46
  144. package/test/integration/domain/participant/index.test.js +2 -0
  145. package/test/integration/handlers/handlers.test.js +605 -166
  146. package/test/integration/handlers/root.test.js +1 -1
  147. package/test/integration/helpers/hubAccounts.js +62 -0
  148. package/test/integration/helpers/ilpPacket.js +1 -1
  149. package/test/integration/helpers/participantFundsInOut.js +80 -0
  150. package/test/integration/helpers/participantLimit.js +6 -1
  151. package/test/integration/helpers/settlementModels.js +88 -0
  152. package/test/integration/helpers/testConsumer.js +163 -0
  153. package/test/integration/helpers/testProducer.js +0 -1
  154. package/test/integration/helpers/transferDuplicateCheck.js +1 -1
  155. package/test/integration/helpers/transferStateChange.js +1 -1
  156. package/test/integration/helpers/transferTestHelper.js +3 -5
  157. package/test/integration/models/transfer/transferStateChange.test.js +6 -0
  158. package/test/unit/api/ledgerAccountTypes/handler.test.js +126 -0
  159. package/test/unit/api/ledgerAccountTypes/routes.test.js +52 -0
  160. package/test/unit/api/participants/handler.test.js +75 -33
  161. package/test/unit/api/root/handler.test.js +1 -1
  162. package/test/unit/api/root/routes.test.js +2 -2
  163. package/test/unit/api/settlementModels/handler.test.js +21 -57
  164. package/test/unit/api/transactions/handler.test.js +108 -0
  165. package/test/unit/api/transactions/routes.test.js +38 -0
  166. package/test/unit/domain/ledgerAccountTypes/index.test.js +197 -0
  167. package/test/unit/domain/participant/index.test.js +115 -2
  168. package/test/unit/domain/settlement/index.test.js +199 -29
  169. package/test/unit/domain/transactions/index.test.js +128 -0
  170. package/test/unit/domain/transfer/index.test.js +1 -0
  171. package/test/unit/domain/transfer/transform.test.js +2 -0
  172. package/test/unit/handlers/admin/handler.test.js +5 -5
  173. package/test/unit/handlers/bulk/get/handler.test.js +401 -0
  174. package/test/unit/handlers/index.test.js +6 -0
  175. package/test/unit/handlers/positions/handler.test.js +26 -0
  176. package/test/unit/handlers/register.test.js +2 -0
  177. package/test/unit/handlers/transfers/handler.test.js +491 -36
  178. package/test/unit/handlers/transfers/validator.test.js +31 -31
  179. package/test/unit/lib/cache.test.js +0 -64
  180. package/test/unit/lib/cachingOfEnums.test.js +121 -0
  181. package/test/unit/lib/config.test.js +34 -0
  182. package/test/unit/lib/enum.test.js +6 -1
  183. package/test/unit/lib/enumCached.test.js +82 -0
  184. package/test/unit/lib/healthCheck/subServiceHealth.test.js +2 -0
  185. package/test/unit/lib/requestLogger.test.js +14 -12
  186. package/test/unit/models/ilpPackets/ilpPacket.test.js +89 -0
  187. package/test/unit/models/ledgerAccountType/ledgerAccountType.test.js +255 -6
  188. package/test/unit/models/misc/migrationLock.test.js +4 -0
  189. package/test/unit/models/misc/segment.test.js +5 -0
  190. package/test/unit/models/participant/facade.test.js +493 -54
  191. package/test/unit/models/participant/participant.test.js +4 -0
  192. package/test/unit/models/participant/participantCurrency.test.js +22 -9
  193. package/test/unit/models/participant/participantCurrencyCached.test.js +1 -1
  194. package/test/unit/models/participant/participantLimit.test.js +97 -0
  195. package/test/unit/models/participant/participantLimitCached.test.js +232 -0
  196. package/test/unit/models/participant/participantPosition.test.js +217 -0
  197. package/test/unit/models/participant/participantPositionChange.test.js +3 -0
  198. package/test/unit/models/position/facade.test.js +318 -29
  199. package/test/unit/models/position/participantPosition.test.js +22 -7
  200. package/test/unit/models/settlement/settlementModel.test.js +18 -37
  201. package/test/unit/models/settlement/settlementModelCached.test.js +171 -0
  202. package/test/unit/models/transfer/facade.test.js +115 -0
  203. package/test/unit/models/transfer/ilpPacket.test.js +28 -11
  204. package/test/unit/models/transfer/transfer.test.js +13 -4
  205. package/test/unit/models/transfer/transferDuplicateCheck.test.js +4 -0
  206. package/test/unit/models/transfer/transferError.test.js +5 -1
  207. package/test/unit/models/transfer/transferErrorDuplicateCheck.test.js +4 -0
  208. package/test/unit/models/transfer/transferExtension.test.js +26 -9
  209. package/test/unit/models/transfer/transferFulfilment.test.js +14 -5
  210. package/test/unit/models/transfer/transferFulfilmentDuplicateCheck.test.js +4 -0
  211. package/test/unit/models/transfer/transferParticipant.test.js +14 -5
  212. package/test/unit/models/transfer/transferStateChange.test.js +3 -0
  213. package/test/unit/models/transfer/transferTimeout.test.js +6 -0
  214. package/test/unit/seeds/amountType.test.js +6 -22
  215. package/test/unit/seeds/balanceOfPayments.test.js +6 -22
  216. package/test/unit/seeds/bulkProcessingState.test.js +84 -0
  217. package/test/unit/{lib/sidecar/nullClient.test.js → seeds/bulkTransferState.test.js} +36 -39
  218. package/test/unit/seeds/currency.test.js +6 -22
  219. package/test/unit/seeds/endpointType.test.js +6 -22
  220. package/test/unit/seeds/ledgerAccountType.test.js +5 -22
  221. package/test/unit/seeds/ledgerEntryType.test.js +6 -22
  222. package/test/unit/seeds/participant.test.js +5 -22
  223. package/test/unit/seeds/participantLimitType.test.js +6 -22
  224. package/test/unit/seeds/partyIdentifierType.test.js +6 -22
  225. package/test/unit/seeds/partyType.test.js +6 -22
  226. package/test/unit/seeds/settlementState.test.js +5 -22
  227. package/test/unit/seeds/settlementWindow1State.test.js +5 -22
  228. package/test/unit/seeds/transactionInitiator.test.js +6 -22
  229. package/test/unit/seeds/transactionInitiatorType.test.js +6 -22
  230. package/test/unit/seeds/transactionScenario.test.js +6 -22
  231. package/test/unit/seeds/transferParticipantRoleType.test.js +6 -22
  232. package/test/unit/seeds/transferState.test.js +6 -22
  233. package/test/unit/shared/plugins.test.js +31 -1
  234. package/test/unit/shared/setup.test.js +25 -36
  235. package/test/util/helpers.js +37 -2
  236. package/test/util/randomTransfers.js +1 -1
  237. package/test/util/scripts/env.sh +6 -5
  238. package/test/util/scripts/populateTestData.sh +204 -181
  239. package/test/util/scripts/populateTestDataForLegacySimulator.sh +261 -0
  240. package/test/util/scripts/restartObjStore.sh +1 -1
  241. package/test-integration.Dockerfile +15 -15
  242. package/test.Dockerfile +12 -12
  243. package/.circleci/_set_up_deploy_envs.sh +0 -47
  244. package/.dockerignore +0 -17
  245. package/.ncurc.json +0 -3
  246. package/server.sh +0 -4
  247. package/sonar-project.properties +0 -17
  248. package/src/lib/sidecar/index.js +0 -47
  249. package/src/lib/sidecar/nullClient.js +0 -18
  250. package/test/.env +0 -8
  251. package/test/integration-config.json +0 -367
  252. package/test/integration-runner.env +0 -31
  253. package/test/integration-runner.sh +0 -264
  254. package/test/spec-runner.sh +0 -132
  255. package/test/unit/lib/sidecar/index.test.js +0 -148
  256. package/test/unit/models/position/participantLimit.test.js +0 -135
@@ -30,7 +30,8 @@ const Uuid = require('uuid4')
30
30
  const retry = require('async-retry')
31
31
  const Logger = require('@mojaloop/central-services-logger')
32
32
  const Config = require('../../../src/lib/config')
33
- const sleep = require('@mojaloop/central-services-shared').Util.Time.sleep
33
+ const Time = require('@mojaloop/central-services-shared').Util.Time
34
+ const sleep = Time.sleep
34
35
  const Db = require('@mojaloop/central-services-database').Db
35
36
  const Cache = require('../../../src/lib/cache')
36
37
  const Consumer = require('@mojaloop/central-services-stream').Util.Consumer
@@ -39,13 +40,26 @@ const Utility = require('@mojaloop/central-services-shared').Util.Kafka
39
40
  const Enum = require('@mojaloop/central-services-shared').Enum
40
41
  const ParticipantHelper = require('../helpers/participant')
41
42
  const ParticipantLimitHelper = require('../helpers/participantLimit')
43
+ const ParticipantFundsInOutHelper = require('../helpers/participantFundsInOut')
42
44
  const ParticipantEndpointHelper = require('../helpers/participantEndpoint')
45
+ const SettlementHelper = require('../helpers/settlementModels')
46
+ const HubAccountsHelper = require('../helpers/hubAccounts')
43
47
  const TransferService = require('../../../src/domain/transfer')
44
48
  const ParticipantService = require('../../../src/domain/participant')
45
49
  const TransferExtensionModel = require('../../../src/models/transfer/transferExtension')
46
50
  const Util = require('@mojaloop/central-services-shared').Util
47
51
  const ErrorHandler = require('@mojaloop/central-services-error-handling')
48
- const { sleepPromise } = require('../../util/helpers')
52
+ const {
53
+ wrapWithRetries,
54
+ getMessagePayloadOrThrow,
55
+ sleepPromise
56
+ } = require('../../util/helpers')
57
+ const TestConsumer = require('../helpers/testConsumer')
58
+
59
+ const ParticipantCached = require('../../../src/models/participant/participantCached')
60
+ const ParticipantCurrencyCached = require('../../../src/models/participant/participantCurrencyCached')
61
+ const ParticipantLimitCached = require('../../../src/models/participant/participantLimitCached')
62
+ const SettlementModelCached = require('../../../src/models/settlement/settlementModelCached')
49
63
 
50
64
  const Handlers = {
51
65
  index: require('../../../src/handlers/register'),
@@ -90,167 +104,238 @@ const testData = {
90
104
  expiration: new Date((new Date()).getTime() + (24 * 60 * 60 * 1000)) // tomorrow
91
105
  }
92
106
 
93
- const prepareTestData = async (dataObj) => {
94
- const payer = await ParticipantHelper.prepareData(dataObj.payer.name, dataObj.amount.currency)
95
- const payee = await ParticipantHelper.prepareData(dataObj.payee.name, dataObj.amount.currency)
96
-
97
- const kafkacat = 'GROUP=abc; T=topic; TR=transfer; kafkacat -b localhost -G $GROUP $T-$TR-prepare $T-$TR-position $T-$TR-fulfil $T-$TR-get $T-admin-$TR $T-notification-event $T-bulk-prepare'
98
- if (debug) console.error(kafkacat)
107
+ const testDataZAR = {
108
+ amount: {
109
+ currency: 'ZAR',
110
+ amount: 110
111
+ },
112
+ payer: {
113
+ name: 'payerFsp',
114
+ limit: 500
115
+ },
116
+ payee: {
117
+ name: 'payeeFsp',
118
+ limit: 300
119
+ },
120
+ endpoint: {
121
+ base: 'http://localhost:1080',
122
+ email: 'test@example.com'
123
+ },
124
+ now: new Date(),
125
+ expiration: new Date((new Date()).getTime() + (24 * 60 * 60 * 1000)) // tomorrow
126
+ }
99
127
 
100
- const payerLimitAndInitialPosition = await ParticipantLimitHelper.prepareLimitAndInitialPosition(payer.participant.name, {
101
- currency: dataObj.amount.currency,
102
- limit: { value: dataObj.payer.limit }
103
- })
104
- const payeeLimitAndInitialPosition = await ParticipantLimitHelper.prepareLimitAndInitialPosition(payee.participant.name, {
105
- currency: dataObj.amount.currency,
106
- limit: { value: dataObj.payee.limit }
107
- })
128
+ const prepareTestData = async (dataObj) => {
129
+ try {
130
+ const payer = await ParticipantHelper.prepareData(dataObj.payer.name, dataObj.amount.currency)
131
+ const payee = await ParticipantHelper.prepareData(dataObj.payee.name, dataObj.amount.currency)
108
132
 
109
- for (const name of [payer.participant.name, payee.participant.name]) {
110
- await ParticipantEndpointHelper.prepareData(name, 'FSPIOP_CALLBACK_URL_TRANSFER_POST', `${dataObj.endpoint.base}/transfers`)
111
- await ParticipantEndpointHelper.prepareData(name, 'FSPIOP_CALLBACK_URL_TRANSFER_PUT', `${dataObj.endpoint.base}/transfers/{{transferId}}`)
112
- await ParticipantEndpointHelper.prepareData(name, 'FSPIOP_CALLBACK_URL_TRANSFER_ERROR', `${dataObj.endpoint.base}/transfers/{{transferId}}/error`)
113
- await ParticipantEndpointHelper.prepareData(name, 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_POST', `${dataObj.endpoint.base}/bulkTransfers`)
114
- await ParticipantEndpointHelper.prepareData(name, 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_PUT', `${dataObj.endpoint.base}/bulkTransfers/{{id}}`)
115
- await ParticipantEndpointHelper.prepareData(name, 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_ERROR', `${dataObj.endpoint.base}/bulkTransfers/{{id}}/error`)
116
- await ParticipantEndpointHelper.prepareData(name, 'FSPIOP_CALLBACK_URL_QUOTES', `${dataObj.endpoint.base}`)
117
- }
133
+ const kafkacat = 'GROUP=abc; T=topic; TR=transfer; kafkacat -b localhost -G $GROUP $T-$TR-prepare $T-$TR-position $T-$TR-fulfil $T-$TR-get $T-admin-$TR $T-notification-event $T-bulk-prepare'
134
+ if (debug) console.error(kafkacat)
118
135
 
119
- const transferPayload = {
120
- transferId: Uuid(),
121
- payerFsp: payer.participant.name,
122
- payeeFsp: payee.participant.name,
123
- amount: {
136
+ const payerLimitAndInitialPosition = await ParticipantLimitHelper.prepareLimitAndInitialPosition(payer.participant.name, {
124
137
  currency: dataObj.amount.currency,
125
- amount: dataObj.amount.amount
126
- },
127
- ilpPacket: 'AYIBgQAAAAAAAASwNGxldmVsb25lLmRmc3AxLm1lci45T2RTOF81MDdqUUZERmZlakgyOVc4bXFmNEpLMHlGTFGCAUBQU0svMS4wCk5vbmNlOiB1SXlweUYzY3pYSXBFdzVVc05TYWh3CkVuY3J5cHRpb246IG5vbmUKUGF5bWVudC1JZDogMTMyMzZhM2ItOGZhOC00MTYzLTg0NDctNGMzZWQzZGE5OGE3CgpDb250ZW50LUxlbmd0aDogMTM1CkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbgpTZW5kZXItSWRlbnRpZmllcjogOTI4MDYzOTEKCiJ7XCJmZWVcIjowLFwidHJhbnNmZXJDb2RlXCI6XCJpbnZvaWNlXCIsXCJkZWJpdE5hbWVcIjpcImFsaWNlIGNvb3BlclwiLFwiY3JlZGl0TmFtZVwiOlwibWVyIGNoYW50XCIsXCJkZWJpdElkZW50aWZpZXJcIjpcIjkyODA2MzkxXCJ9IgA',
128
- condition: 'GRzLaTP7DJ9t4P-a_BA0WA9wzzlsugf00-Tn6kESAfM',
129
- expiration: dataObj.expiration,
130
- extensionList: {
131
- extension: [
132
- {
133
- key: 'key1',
134
- value: 'value1'
135
- },
136
- {
137
- key: 'key2',
138
- value: 'value2'
139
- }
140
- ]
141
- }
142
- }
138
+ limit: { value: dataObj.payer.limit }
139
+ })
140
+ const payeeLimitAndInitialPosition = await ParticipantLimitHelper.prepareLimitAndInitialPosition(payee.participant.name, {
141
+ currency: dataObj.amount.currency,
142
+ limit: { value: dataObj.payee.limit }
143
+ })
144
+ await ParticipantFundsInOutHelper.recordFundsIn(payer.participant.name, payer.participantCurrencyId2, {
145
+ currency: dataObj.amount.currency,
146
+ amount: 10000
147
+ })
143
148
 
144
- const prepareHeaders = {
145
- 'fspiop-source': payer.participant.name,
146
- 'fspiop-destination': payee.participant.name
147
- }
148
- const fulfilAbortRejectHeaders = {
149
- 'fspiop-source': payee.participant.name,
150
- 'fspiop-destination': payer.participant.name
151
- }
149
+ for (const name of [payer.participant.name, payee.participant.name]) {
150
+ await ParticipantEndpointHelper.prepareData(name, 'FSPIOP_CALLBACK_URL_TRANSFER_POST', `${dataObj.endpoint.base}/transfers`)
151
+ await ParticipantEndpointHelper.prepareData(name, 'FSPIOP_CALLBACK_URL_TRANSFER_PUT', `${dataObj.endpoint.base}/transfers/{{transferId}}`)
152
+ await ParticipantEndpointHelper.prepareData(name, 'FSPIOP_CALLBACK_URL_TRANSFER_ERROR', `${dataObj.endpoint.base}/transfers/{{transferId}}/error`)
153
+ await ParticipantEndpointHelper.prepareData(name, 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_POST', `${dataObj.endpoint.base}/bulkTransfers`)
154
+ await ParticipantEndpointHelper.prepareData(name, 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_PUT', `${dataObj.endpoint.base}/bulkTransfers/{{id}}`)
155
+ await ParticipantEndpointHelper.prepareData(name, 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_ERROR', `${dataObj.endpoint.base}/bulkTransfers/{{id}}/error`)
156
+ await ParticipantEndpointHelper.prepareData(name, 'FSPIOP_CALLBACK_URL_QUOTES', `${dataObj.endpoint.base}`)
157
+ }
152
158
 
153
- const fulfilPayload = {
154
- fulfilment: 'UNlJ98hZTY_dsw0cAqw4i_UN3v4utt7CZFB4yfLbVFA',
155
- completedTimestamp: dataObj.now,
156
- transferState: 'COMMITTED',
157
- extensionList: {
158
- extension: [
159
- {
160
- key: 'key1',
161
- value: 'value1'
162
- },
163
- {
164
- key: 'key2',
165
- value: 'value2'
166
- }
167
- ]
159
+ const transferPayload = {
160
+ transferId: Uuid(),
161
+ payerFsp: payer.participant.name,
162
+ payeeFsp: payee.participant.name,
163
+ amount: {
164
+ currency: dataObj.amount.currency,
165
+ amount: dataObj.amount.amount
166
+ },
167
+ ilpPacket: 'AYIBgQAAAAAAAASwNGxldmVsb25lLmRmc3AxLm1lci45T2RTOF81MDdqUUZERmZlakgyOVc4bXFmNEpLMHlGTFGCAUBQU0svMS4wCk5vbmNlOiB1SXlweUYzY3pYSXBFdzVVc05TYWh3CkVuY3J5cHRpb246IG5vbmUKUGF5bWVudC1JZDogMTMyMzZhM2ItOGZhOC00MTYzLTg0NDctNGMzZWQzZGE5OGE3CgpDb250ZW50LUxlbmd0aDogMTM1CkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbgpTZW5kZXItSWRlbnRpZmllcjogOTI4MDYzOTEKCiJ7XCJmZWVcIjowLFwidHJhbnNmZXJDb2RlXCI6XCJpbnZvaWNlXCIsXCJkZWJpdE5hbWVcIjpcImFsaWNlIGNvb3BlclwiLFwiY3JlZGl0TmFtZVwiOlwibWVyIGNoYW50XCIsXCJkZWJpdElkZW50aWZpZXJcIjpcIjkyODA2MzkxXCJ9IgA',
168
+ condition: 'GRzLaTP7DJ9t4P-a_BA0WA9wzzlsugf00-Tn6kESAfM',
169
+ expiration: dataObj.expiration,
170
+ extensionList: {
171
+ extension: [
172
+ {
173
+ key: 'key1',
174
+ value: 'value1'
175
+ },
176
+ {
177
+ key: 'key2',
178
+ value: 'value2'
179
+ }
180
+ ]
181
+ }
168
182
  }
169
- }
170
183
 
171
- const rejectPayload = Object.assign({}, fulfilPayload, { transferState: TransferInternalState.ABORTED_REJECTED })
184
+ const prepareHeaders = {
185
+ 'fspiop-source': payer.participant.name,
186
+ 'fspiop-destination': payee.participant.name,
187
+ 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1'
188
+ }
189
+ const fulfilAbortRejectHeaders = {
190
+ 'fspiop-source': payee.participant.name,
191
+ 'fspiop-destination': payer.participant.name,
192
+ 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1'
193
+ }
172
194
 
173
- const errorPayload = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PAYEE_FSP_REJECTED_TXN).toApiErrorObject()
174
- errorPayload.errorInformation.extensionList = { extension: [{ key: 'errorDetail', value: 'This is an abort extension' }] }
195
+ const fulfilPayload = {
196
+ fulfilment: 'UNlJ98hZTY_dsw0cAqw4i_UN3v4utt7CZFB4yfLbVFA',
197
+ completedTimestamp: dataObj.now,
198
+ transferState: 'COMMITTED',
199
+ extensionList: {
200
+ extension: [
201
+ {
202
+ key: 'key1',
203
+ value: 'value1'
204
+ },
205
+ {
206
+ key: 'key2',
207
+ value: 'value2'
208
+ }
209
+ ]
210
+ }
211
+ }
175
212
 
176
- const messageProtocolPrepare = {
177
- id: Uuid(),
178
- from: transferPayload.payerFsp,
179
- to: transferPayload.payeeFsp,
180
- type: 'application/json',
181
- content: {
182
- headers: prepareHeaders,
183
- payload: transferPayload
184
- },
185
- metadata: {
186
- event: {
187
- id: Uuid(),
188
- type: TransferEventAction.PREPARE,
189
- action: TransferEventType.PREPARE,
190
- createdAt: dataObj.now,
191
- state: {
192
- status: 'success',
193
- code: 0
213
+ const rejectPayload = Object.assign({}, fulfilPayload, { transferState: TransferInternalState.ABORTED_REJECTED })
214
+
215
+ const errorPayload = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PAYEE_FSP_REJECTED_TXN).toApiErrorObject()
216
+ errorPayload.errorInformation.extensionList = { extension: [{ key: 'errorDetail', value: 'This is an abort extension' }] }
217
+
218
+ const messageProtocolPrepare = {
219
+ id: Uuid(),
220
+ from: transferPayload.payerFsp,
221
+ to: transferPayload.payeeFsp,
222
+ type: 'application/json',
223
+ content: {
224
+ headers: prepareHeaders,
225
+ payload: transferPayload
226
+ },
227
+ metadata: {
228
+ event: {
229
+ id: Uuid(),
230
+ type: TransferEventAction.PREPARE,
231
+ action: TransferEventType.PREPARE,
232
+ createdAt: dataObj.now,
233
+ state: {
234
+ status: 'success',
235
+ code: 0
236
+ }
194
237
  }
195
238
  }
196
239
  }
197
- }
198
240
 
199
- const messageProtocolFulfil = Util.clone(messageProtocolPrepare)
200
- messageProtocolFulfil.id = Uuid()
201
- messageProtocolFulfil.from = transferPayload.payeeFsp
202
- messageProtocolFulfil.to = transferPayload.payerFsp
203
- messageProtocolFulfil.content.headers = fulfilAbortRejectHeaders
204
- messageProtocolFulfil.content.uriParams = { id: transferPayload.transferId }
205
- messageProtocolFulfil.content.payload = fulfilPayload
206
- messageProtocolFulfil.metadata.event.id = Uuid()
207
- messageProtocolFulfil.metadata.event.type = TransferEventType.FULFIL
208
- messageProtocolFulfil.metadata.event.action = TransferEventAction.COMMIT
209
-
210
- const messageProtocolReject = Util.clone(messageProtocolFulfil)
211
- messageProtocolReject.id = Uuid()
212
- messageProtocolFulfil.content.uriParams = { id: transferPayload.transferId }
213
- messageProtocolReject.content.payload = rejectPayload
214
- messageProtocolReject.metadata.event.action = TransferEventAction.REJECT
215
-
216
- const messageProtocolError = Util.clone(messageProtocolFulfil)
217
- messageProtocolError.id = Uuid()
218
- messageProtocolFulfil.content.uriParams = { id: transferPayload.transferId }
219
- messageProtocolError.content.payload = errorPayload
220
- messageProtocolError.metadata.event.action = TransferEventAction.ABORT
221
-
222
- const topicConfTransferPrepare = Utility.createGeneralTopicConf(Config.KAFKA_CONFIG.TOPIC_TEMPLATES.GENERAL_TOPIC_TEMPLATE.TEMPLATE, TransferEventType.TRANSFER, TransferEventType.PREPARE)
223
- const topicConfTransferFulfil = Utility.createGeneralTopicConf(Config.KAFKA_CONFIG.TOPIC_TEMPLATES.GENERAL_TOPIC_TEMPLATE.TEMPLATE, TransferEventType.TRANSFER, TransferEventType.FULFIL)
224
-
225
- return {
226
- transferPayload,
227
- fulfilPayload,
228
- rejectPayload,
229
- errorPayload,
230
- messageProtocolPrepare,
231
- messageProtocolFulfil,
232
- messageProtocolReject,
233
- messageProtocolError,
234
- topicConfTransferPrepare,
235
- topicConfTransferFulfil,
236
- payer,
237
- payerLimitAndInitialPosition,
238
- payee,
239
- payeeLimitAndInitialPosition
241
+ const messageProtocolFulfil = Util.clone(messageProtocolPrepare)
242
+ messageProtocolFulfil.id = Uuid()
243
+ messageProtocolFulfil.from = transferPayload.payeeFsp
244
+ messageProtocolFulfil.to = transferPayload.payerFsp
245
+ messageProtocolFulfil.content.headers = fulfilAbortRejectHeaders
246
+ messageProtocolFulfil.content.uriParams = { id: transferPayload.transferId }
247
+ messageProtocolFulfil.content.payload = fulfilPayload
248
+ messageProtocolFulfil.metadata.event.id = Uuid()
249
+ messageProtocolFulfil.metadata.event.type = TransferEventType.FULFIL
250
+ messageProtocolFulfil.metadata.event.action = TransferEventAction.COMMIT
251
+
252
+ const messageProtocolReject = Util.clone(messageProtocolFulfil)
253
+ messageProtocolReject.id = Uuid()
254
+ messageProtocolFulfil.content.uriParams = { id: transferPayload.transferId }
255
+ messageProtocolReject.content.payload = rejectPayload
256
+ messageProtocolReject.metadata.event.action = TransferEventAction.REJECT
257
+
258
+ const messageProtocolError = Util.clone(messageProtocolFulfil)
259
+ messageProtocolError.id = Uuid()
260
+ messageProtocolFulfil.content.uriParams = { id: transferPayload.transferId }
261
+ messageProtocolError.content.payload = errorPayload
262
+ messageProtocolError.metadata.event.action = TransferEventAction.ABORT
263
+
264
+ const topicConfTransferPrepare = Utility.createGeneralTopicConf(Config.KAFKA_CONFIG.TOPIC_TEMPLATES.GENERAL_TOPIC_TEMPLATE.TEMPLATE, TransferEventType.TRANSFER, TransferEventType.PREPARE)
265
+ const topicConfTransferFulfil = Utility.createGeneralTopicConf(Config.KAFKA_CONFIG.TOPIC_TEMPLATES.GENERAL_TOPIC_TEMPLATE.TEMPLATE, TransferEventType.TRANSFER, TransferEventType.FULFIL)
266
+
267
+ return {
268
+ transferPayload,
269
+ fulfilPayload,
270
+ rejectPayload,
271
+ errorPayload,
272
+ messageProtocolPrepare,
273
+ messageProtocolFulfil,
274
+ messageProtocolReject,
275
+ messageProtocolError,
276
+ topicConfTransferPrepare,
277
+ topicConfTransferFulfil,
278
+ payer,
279
+ payerLimitAndInitialPosition,
280
+ payee,
281
+ payeeLimitAndInitialPosition
282
+ }
283
+ } catch (err) {
284
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
240
285
  }
241
286
  }
242
287
 
243
288
  Test('Handlers test', async handlersTest => {
244
289
  const startTime = new Date()
290
+ await Db.connect(Config.DATABASE)
291
+ await ParticipantCached.initialize()
292
+ await ParticipantCurrencyCached.initialize()
293
+ await ParticipantLimitCached.initialize()
294
+ await SettlementModelCached.initialize()
295
+ await Cache.initCache()
296
+ await SettlementHelper.prepareData()
297
+ await HubAccountsHelper.prepareData()
298
+
299
+ // Start a testConsumer to monitor events that our handlers emit
300
+ const testConsumer = new TestConsumer([
301
+ {
302
+ topicName: Utility.transformGeneralTopicName(
303
+ Config.KAFKA_CONFIG.TOPIC_TEMPLATES.GENERAL_TOPIC_TEMPLATE.TEMPLATE,
304
+ Enum.Events.Event.Type.TRANSFER,
305
+ Enum.Events.Event.Action.FULFIL
306
+ ),
307
+ config: Utility.getKafkaConfig(
308
+ Config.KAFKA_CONFIG,
309
+ Enum.Kafka.Config.CONSUMER,
310
+ Enum.Events.Event.Type.TRANSFER.toUpperCase(),
311
+ Enum.Events.Event.Action.FULFIL.toUpperCase()
312
+ )
313
+ },
314
+ {
315
+ topicName: Utility.transformGeneralTopicName(
316
+ Config.KAFKA_CONFIG.TOPIC_TEMPLATES.GENERAL_TOPIC_TEMPLATE.TEMPLATE,
317
+ Enum.Events.Event.Type.NOTIFICATION,
318
+ Enum.Events.Event.Action.EVENT
319
+ ),
320
+ config: Utility.getKafkaConfig(
321
+ Config.KAFKA_CONFIG,
322
+ Enum.Kafka.Config.CONSUMER,
323
+ Enum.Events.Event.Type.NOTIFICATION.toUpperCase(),
324
+ Enum.Events.Event.Action.EVENT.toUpperCase()
325
+ )
326
+ }
327
+ ])
328
+
245
329
  await handlersTest.test('registerAllHandlers should', async registerAllHandlers => {
246
330
  await registerAllHandlers.test('setup handlers', async (test) => {
247
- await Db.connect(Config.DATABASE)
248
- await Cache.initCache()
249
331
  await Handlers.transfers.registerPrepareHandler()
250
332
  await Handlers.positions.registerPositionHandler()
251
333
  await Handlers.transfers.registerFulfilHandler()
252
334
  await Handlers.timeouts.registerTimeoutHandler()
253
335
 
336
+ // Set up the testConsumer here
337
+ await testConsumer.startListening()
338
+
254
339
  sleep(rebalanceDelay, debug, 'registerAllHandlers', 'awaiting registration of common handlers')
255
340
 
256
341
  test.pass('done')
@@ -260,6 +345,312 @@ Test('Handlers test', async handlersTest => {
260
345
  await registerAllHandlers.end()
261
346
  })
262
347
 
348
+ await handlersTest.test('transferFulfilReserve should', async transferFulfilReserve => {
349
+ await transferFulfilReserve.test('Does not send a RESERVED_ABORTED notification when the Payee aborts the transfer', async (test) => {
350
+ // Arrange
351
+ const td = await prepareTestData(testData)
352
+
353
+ // 1. send a PREPARE request (from Payer)
354
+ const prepareConfig = Utility.getKafkaConfig(
355
+ Config.KAFKA_CONFIG,
356
+ Enum.Kafka.Config.PRODUCER,
357
+ TransferEventType.TRANSFER.toUpperCase(),
358
+ TransferEventType.PREPARE.toUpperCase())
359
+ prepareConfig.logger = Logger
360
+ await Producer.produceMessage(td.messageProtocolPrepare, td.topicConfTransferPrepare, prepareConfig)
361
+ const transfer = await wrapWithRetries(async () => {
362
+ // lets fetch the transfer
363
+ const transfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId)
364
+ // lets check its status, and if its what we expect return the result
365
+ if (transfer.transferState === 'RESERVED') return transfer
366
+ // otherwise lets return nothing
367
+ return null
368
+ })
369
+
370
+ test.equal(transfer.transferState, 'RESERVED', 'Transfer is in reserved state')
371
+
372
+ // 2. send an ABORTED request from Payee
373
+ td.messageProtocolFulfil.metadata.event.action = TransferEventAction.RESERVE
374
+ const completedTimestamp = Time.getUTCString(new Date())
375
+ td.messageProtocolFulfil.content.payload = {
376
+ ...td.messageProtocolFulfil.content.payload,
377
+ completedTimestamp,
378
+ transferState: 'ABORTED'
379
+ }
380
+ const fulfilConfig = Utility.getKafkaConfig(
381
+ Config.KAFKA_CONFIG,
382
+ Enum.Kafka.Config.PRODUCER,
383
+ TransferEventType.TRANSFER.toUpperCase(),
384
+ TransferEventType.FULFIL.toUpperCase())
385
+ fulfilConfig.logger = Logger
386
+ await Producer.produceMessage(td.messageProtocolFulfil, td.topicConfTransferFulfil, fulfilConfig)
387
+
388
+ // Assert
389
+ // 3. Check that we didn't sent a notification for the Payee
390
+ try {
391
+ await wrapWithRetries(() => testConsumer.getEventsForFilter({
392
+ topicFilter: 'topic-notification-event',
393
+ action: 'reserved-aborted'
394
+ }))
395
+ test.notOk('Should not be executed')
396
+ } catch (err) {
397
+ console.log(err)
398
+ test.ok('No payee abort notification sent')
399
+ }
400
+ console.log(JSON.stringify(testConsumer.getAllEvents()))
401
+
402
+ // TODO: I can't seem to find the payer abort notification in the log
403
+ // is there something I'm missing here? Does it go to a different handler?
404
+
405
+ // 4. Check that we sent 1 notification for the payer
406
+ // const payerAbortNotification = (await wrapWithRetries(() => testConsumer.getEventsForFilter({
407
+ // topicFilter: 'topic-notification-event',
408
+ // action: 'abort'
409
+ // })))[0]
410
+ // test.ok(payerAbortNotification, 'Payer Abort notification sent')
411
+
412
+ // Cleanup
413
+ testConsumer.clearEvents()
414
+ test.end()
415
+ })
416
+
417
+ await transferFulfilReserve.test('send a RESERVED_ABORTED notification if the transfer is expired', async (test) => {
418
+ // Arrange
419
+ const customTestData = {
420
+ ...testData,
421
+ expiration: new Date((new Date()).getTime() + (2 * 1000)) // 2 seconds
422
+ }
423
+ const td = await prepareTestData(customTestData)
424
+
425
+ // 1. send a PREPARE request (from Payer)
426
+ const prepareConfig = Utility.getKafkaConfig(
427
+ Config.KAFKA_CONFIG,
428
+ Enum.Kafka.Config.PRODUCER,
429
+ TransferEventType.TRANSFER.toUpperCase(),
430
+ TransferEventType.PREPARE.toUpperCase())
431
+ prepareConfig.logger = Logger
432
+ await Producer.produceMessage(td.messageProtocolPrepare, td.topicConfTransferPrepare, prepareConfig)
433
+ const transfer = await wrapWithRetries(async () => {
434
+ // lets fetch the transfer
435
+ const transfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId)
436
+ // lets check its status, and if its what we expect return the result
437
+ if (transfer.transferState === 'RESERVED') return transfer
438
+ // otherwise lets return nothing
439
+ return null
440
+ })
441
+ test.equal(transfer.transferState, 'RESERVED', 'Transfer is in reserved state')
442
+
443
+ // 2. sleep so that the RESERVED transfer expires
444
+ await sleepPromise(2)
445
+
446
+ // 3. send a RESERVED request from Payee
447
+ td.messageProtocolFulfil.metadata.event.action = TransferEventAction.RESERVE
448
+ const completedTimestamp = Time.getUTCString(new Date())
449
+ td.messageProtocolFulfil.content.payload = {
450
+ ...td.messageProtocolFulfil.content.payload,
451
+ completedTimestamp,
452
+ transferState: 'RESERVED'
453
+ }
454
+ const fulfilConfig = Utility.getKafkaConfig(
455
+ Config.KAFKA_CONFIG,
456
+ Enum.Kafka.Config.PRODUCER,
457
+ TransferEventType.TRANSFER.toUpperCase(),
458
+ TransferEventType.FULFIL.toUpperCase())
459
+ fulfilConfig.logger = Logger
460
+ await Producer.produceMessage(td.messageProtocolFulfil, td.topicConfTransferFulfil, fulfilConfig)
461
+
462
+ // 4. Get the updated transfer since the completedTimestamp may have changed
463
+ const updatedTransfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId)
464
+ const expectedAbortNotificationPayload = {
465
+ completedTimestamp: Time.getUTCString(new Date(updatedTransfer.completedTimestamp)),
466
+ transferState: 'ABORTED'
467
+ }
468
+
469
+ // Assert
470
+ // 5. Check that we sent 2 notifications to kafka - one for the Payee, one for the Payer
471
+ const payerAbortNotification = (await wrapWithRetries(
472
+ () => testConsumer.getEventsForFilter({ topicFilter: 'topic-notification-event', action: 'commit' }))
473
+ )[0]
474
+ const payeeAbortNotification = (await wrapWithRetries(
475
+ () => testConsumer.getEventsForFilter({ topicFilter: 'topic-notification-event', action: 'reserved-aborted' }))
476
+ )[0]
477
+ test.ok(payerAbortNotification, 'Payer Abort notification sent')
478
+ test.ok(payeeAbortNotification, 'Payee Abort notification sent')
479
+
480
+ test.deepEqual(
481
+ getMessagePayloadOrThrow(payeeAbortNotification),
482
+ expectedAbortNotificationPayload,
483
+ 'Abort notification should be sent with the correct values'
484
+ )
485
+
486
+ // Cleanup
487
+ testConsumer.clearEvents()
488
+ test.end()
489
+ })
490
+
491
+ await transferFulfilReserve.test('send a RESERVED_ABORTED notification when the transfer is not in a RESERVED state', async (test) => {
492
+ // Arrange
493
+ const td = await prepareTestData(testData)
494
+
495
+ // 1. send a PREPARE request (from Payer)
496
+ const prepareConfig = Utility.getKafkaConfig(
497
+ Config.KAFKA_CONFIG,
498
+ Enum.Kafka.Config.PRODUCER,
499
+ TransferEventType.TRANSFER.toUpperCase(),
500
+ TransferEventType.PREPARE.toUpperCase())
501
+ prepareConfig.logger = Logger
502
+ await Producer.produceMessage(td.messageProtocolPrepare, td.topicConfTransferPrepare, prepareConfig)
503
+ const transfer = await wrapWithRetries(async () => {
504
+ // lets fetch the transfer
505
+ const transfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId)
506
+ // lets check its status, and if its what we expect return the result
507
+ if (transfer.transferState === 'RESERVED') return transfer
508
+ // otherwise lets return nothing
509
+ return null
510
+ })
511
+ test.equal(transfer.transferState, 'RESERVED', 'Transfer is in reserved state')
512
+
513
+ // 2. Modify the transfer in the DB
514
+ await TransferService.saveTransferStateChange({
515
+ transferId: transfer.transferId,
516
+ transferStateId: 'INVALID'
517
+ })
518
+
519
+ // 3. send a RESERVED request from Payee
520
+ td.messageProtocolFulfil.metadata.event.action = TransferEventAction.RESERVE
521
+ const completedTimestamp = Time.getUTCString(new Date())
522
+ td.messageProtocolFulfil.content.payload = {
523
+ ...td.messageProtocolFulfil.content.payload,
524
+ completedTimestamp,
525
+ transferState: 'RESERVED'
526
+ }
527
+ const fulfilConfig = Utility.getKafkaConfig(
528
+ Config.KAFKA_CONFIG,
529
+ Enum.Kafka.Config.PRODUCER,
530
+ TransferEventType.TRANSFER.toUpperCase(),
531
+ TransferEventType.FULFIL.toUpperCase())
532
+ fulfilConfig.logger = Logger
533
+ await Producer.produceMessage(td.messageProtocolFulfil, td.topicConfTransferFulfil, fulfilConfig)
534
+
535
+ // 4. Get the updated transfer since the completedTimestamp may have changed
536
+ const updatedTransfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId)
537
+ const expectedAbortNotificationPayload = {
538
+ completedTimestamp: Time.getUTCString(new Date(updatedTransfer.completedTimestamp)),
539
+ transferState: 'ABORTED'
540
+ }
541
+
542
+ // Assert
543
+ // 5. Check that we sent 2 notifications to kafka - one for the Payee, one for the Payer
544
+ try {
545
+ const payerAbortNotification = (await wrapWithRetries(
546
+ () => testConsumer.getEventsForFilter({ topicFilter: 'topic-notification-event', action: 'commit' }))
547
+ )[0]
548
+ test.ok(payerAbortNotification, 'Payer Abort notification sent')
549
+ } catch (err) {
550
+ test.notOk('No payerAbortNotification was sent')
551
+ }
552
+ try {
553
+ const payeeAbortNotification = (await wrapWithRetries(
554
+ () => testConsumer.getEventsForFilter({ topicFilter: 'topic-notification-event', action: 'reserved-aborted' }))
555
+ )[0]
556
+ test.ok(payeeAbortNotification, 'Payee Abort notification sent')
557
+ test.deepEqual(
558
+ getMessagePayloadOrThrow(payeeAbortNotification),
559
+ expectedAbortNotificationPayload,
560
+ 'Abort notification should be sent with the correct values'
561
+ )
562
+ } catch (err) {
563
+ test.notOk('No payeeAbortNotification was sent')
564
+ }
565
+
566
+ // Cleanup
567
+ testConsumer.clearEvents()
568
+ test.end()
569
+ })
570
+
571
+ await transferFulfilReserve.test('send a RESERVED_ABORTED notification when the validation fails', async (test) => {
572
+ // Arrange
573
+ const td = await prepareTestData(testData)
574
+
575
+ // 1. send a PREPARE request (from Payer)
576
+ const prepareConfig = Utility.getKafkaConfig(
577
+ Config.KAFKA_CONFIG,
578
+ Enum.Kafka.Config.PRODUCER,
579
+ TransferEventType.TRANSFER.toUpperCase(),
580
+ TransferEventType.PREPARE.toUpperCase())
581
+ prepareConfig.logger = Logger
582
+ await Producer.produceMessage(td.messageProtocolPrepare, td.topicConfTransferPrepare, prepareConfig)
583
+ const transfer = await wrapWithRetries(async () => {
584
+ // lets fetch the transfer
585
+ const transfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId)
586
+ // lets check its status, and if its what we expect return the result
587
+ if (transfer.transferState === 'RESERVED') return transfer
588
+ // otherwise lets return nothing
589
+ return null
590
+ })
591
+ test.equal(transfer.transferState, 'RESERVED', 'Transfer is in reserved state')
592
+
593
+ // 2. send a RESERVED request with an invalid validation(from Payee)
594
+ td.messageProtocolFulfil.metadata.event.action = TransferEventAction.RESERVE
595
+ const completedTimestamp = Time.getUTCString(new Date())
596
+ td.messageProtocolFulfil.content.payload = {
597
+ fulfilment: 'INVALIDZTY_dsw0cAqw4i_UN3v4utt7CZFB4yfLbVFA',
598
+ completedTimestamp,
599
+ transferState: 'RESERVED'
600
+ }
601
+ const fulfilConfig = Utility.getKafkaConfig(
602
+ Config.KAFKA_CONFIG,
603
+ Enum.Kafka.Config.PRODUCER,
604
+ TransferEventType.TRANSFER.toUpperCase(),
605
+ TransferEventType.FULFIL.toUpperCase())
606
+ fulfilConfig.logger = Logger
607
+ await Producer.produceMessage(td.messageProtocolFulfil, td.topicConfTransferFulfil, fulfilConfig)
608
+
609
+ await wrapWithRetries(async () => {
610
+ const transfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId)
611
+ return transfer.transferState === 'ABORTED_ERROR'
612
+ })
613
+ const updatedTransfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId)
614
+ test.equal(updatedTransfer.transferState, 'ABORTED_ERROR', 'Transfer is in ABORTED_ERROR state')
615
+ const expectedAbortNotificationPayload = {
616
+ completedTimestamp: (new Date(Date.parse(updatedTransfer.completedTimestamp))).toISOString(),
617
+ transferState: 'ABORTED'
618
+ }
619
+
620
+ // Assert
621
+ // 3. Check that we sent 2 notifications to kafka - one for the Payee, one for the Payer
622
+ const payerAbortNotificationEvent = (await wrapWithRetries(
623
+ () => testConsumer.getEventsForFilter({ topicFilter: 'topic-notification-event', action: 'abort-validation' }))
624
+ )[0]
625
+ const payeeAbortNotificationEvent = (await wrapWithRetries(
626
+ () => testConsumer.getEventsForFilter({ topicFilter: 'topic-notification-event', action: 'reserved-aborted' }))
627
+ )[0]
628
+ test.ok(payerAbortNotificationEvent, 'Payer Abort notification sent')
629
+ test.ok(payeeAbortNotificationEvent, 'Payee Abort notification sent')
630
+
631
+ // grab kafka message
632
+ const payerAbortNotificationPayload = getMessagePayloadOrThrow(payeeAbortNotificationEvent)
633
+
634
+ test.equal(
635
+ payerAbortNotificationPayload.transferState,
636
+ expectedAbortNotificationPayload.transferState,
637
+ 'Abort notification should be sent with the correct transferState'
638
+ )
639
+
640
+ test.equal(
641
+ payerAbortNotificationPayload.completedTimestamp,
642
+ expectedAbortNotificationPayload.completedTimestamp,
643
+ 'Abort notification should be sent with the correct completedTimestamp'
644
+ )
645
+
646
+ // Cleanup
647
+ testConsumer.clearEvents()
648
+ test.end()
649
+ })
650
+
651
+ transferFulfilReserve.end()
652
+ })
653
+
263
654
  await handlersTest.test('transferFulfilCommit should', async transferFulfilCommit => {
264
655
  const td = await prepareTestData(testData)
265
656
 
@@ -291,7 +682,7 @@ Test('Handlers test', async handlersTest => {
291
682
  const transfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId) || {}
292
683
  if (transfer.transferState !== TransferState.RESERVED) {
293
684
  if (debug) console.log(`retrying in ${retryDelay / 1000}s..`)
294
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`)
685
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `#1 Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`)
295
686
  }
296
687
  return tests()
297
688
  }, retryOpts)
@@ -331,7 +722,7 @@ Test('Handlers test', async handlersTest => {
331
722
  const transfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId) || {}
332
723
  if (transfer.transferState !== TransferState.COMMITTED) {
333
724
  if (debug) console.log(`retrying in ${retryDelay / 1000}s..`)
334
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`)
725
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `#2 Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`)
335
726
  }
336
727
  return tests()
337
728
  }, retryOpts)
@@ -345,11 +736,9 @@ Test('Handlers test', async handlersTest => {
345
736
  transferFulfilCommit.end()
346
737
  })
347
738
 
348
- await handlersTest.test('transferFulfilReject should', async transferFulfilReject => {
349
- testData.amount.amount = 15
350
- const td = await prepareTestData(testData)
351
-
352
- await transferFulfilReject.test('update transfer state to RESERVED by PREPARE request', async (test) => {
739
+ await handlersTest.test('tranferFulfilCommit with default settlement model should', async transferFulfilCommit => {
740
+ const td = await prepareTestData(testDataZAR)
741
+ await transferFulfilCommit.test('update transfer state to RESERVED by PREPARE request', async (test) => {
353
742
  const config = Utility.getKafkaConfig(
354
743
  Config.KAFKA_CONFIG,
355
744
  Enum.Kafka.Config.PRODUCER,
@@ -361,8 +750,15 @@ Test('Handlers test', async handlersTest => {
361
750
 
362
751
  const tests = async () => {
363
752
  const transfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId) || {}
753
+ const payerCurrentPosition = await ParticipantService.getPositionByParticipantCurrencyId(td.payer.participantCurrencyId) || {}
754
+ const payerInitialPosition = td.payerLimitAndInitialPosition.participantPosition.value
755
+ const payerExpectedPosition = payerInitialPosition + td.transferPayload.amount.amount
756
+ const payerPositionChange = await ParticipantService.getPositionChangeByParticipantPositionId(payerCurrentPosition.participantPositionId) || {}
364
757
  test.equal(producerResponse, true, 'Producer for prepare published message')
365
758
  test.equal(transfer.transferState, TransferState.RESERVED, `Transfer state changed to ${TransferState.RESERVED}`)
759
+ test.equal(payerCurrentPosition.value, payerExpectedPosition, 'Payer position incremented by transfer amount and updated in participantPosition')
760
+ test.equal(payerPositionChange.value, payerCurrentPosition.value, 'Payer position change value inserted and matches the updated participantPosition value')
761
+ test.equal(payerPositionChange.transferStateChangeId, transfer.transferStateChangeId, 'Payer position change record is bound to the corresponding transfer state change')
366
762
  }
367
763
 
368
764
  try {
@@ -370,7 +766,7 @@ Test('Handlers test', async handlersTest => {
370
766
  const transfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId) || {}
371
767
  if (transfer.transferState !== TransferState.RESERVED) {
372
768
  if (debug) console.log(`retrying in ${retryDelay / 1000}s..`)
373
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`)
769
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `#1 Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`)
374
770
  }
375
771
  return tests()
376
772
  }, retryOpts)
@@ -380,8 +776,7 @@ Test('Handlers test', async handlersTest => {
380
776
  }
381
777
  test.end()
382
778
  })
383
-
384
- await transferFulfilReject.test('update transfer state to ABORTED_REJECTED by ABORT request', async (test) => {
779
+ await transferFulfilCommit.test('update transfer state to COMMITTED by FULFIL request', async (test) => {
385
780
  const config = Utility.getKafkaConfig(
386
781
  Config.KAFKA_CONFIG,
387
782
  Enum.Kafka.Config.PRODUCER,
@@ -389,27 +784,27 @@ Test('Handlers test', async handlersTest => {
389
784
  TransferEventType.FULFIL.toUpperCase())
390
785
  config.logger = Logger
391
786
 
392
- const producerResponse = await Producer.produceMessage(td.messageProtocolReject, td.topicConfTransferFulfil, config)
393
-
787
+ const producerResponse = await Producer.produceMessage(td.messageProtocolFulfil, td.topicConfTransferFulfil, config)
394
788
  const tests = async () => {
395
789
  const transfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId) || {}
396
- const payerCurrentPosition = await ParticipantService.getPositionByParticipantCurrencyId(td.payer.participantCurrencyId) || {}
397
- const payerExpectedPosition = testData.amount.amount - td.transferPayload.amount.amount
398
- const payerPositionChange = await ParticipantService.getPositionChangeByParticipantPositionId(payerCurrentPosition.participantPositionId) || {}
790
+ const payeeCurrentPosition = await ParticipantService.getPositionByParticipantCurrencyId(td.payee.participantCurrencyId) || {}
791
+ const payeeInitialPosition = td.payeeLimitAndInitialPosition.participantPosition.value
792
+ const payeeExpectedPosition = payeeInitialPosition - td.transferPayload.amount.amount
793
+ const payeePositionChange = await ParticipantService.getPositionChangeByParticipantPositionId(payeeCurrentPosition.participantPositionId) || {}
399
794
  test.equal(producerResponse, true, 'Producer for fulfil published message')
400
- test.equal(transfer.transferState, TransferInternalState.ABORTED_REJECTED, `Transfer state changed to ${TransferInternalState.ABORTED_REJECTED}`)
401
- test.equal(transfer.fulfilment, td.fulfilPayload.fulfilment, 'Reject ilpFulfilment saved')
402
- test.equal(payerCurrentPosition.value, payerExpectedPosition, 'Payer position decremented by transfer amount and updated in participantPosition')
403
- test.equal(payerPositionChange.value, payerCurrentPosition.value, 'Payer position change value inserted and matches the updated participantPosition value')
404
- test.equal(payerPositionChange.transferStateChangeId, transfer.transferStateChangeId, 'Payer position change record is bound to the corresponding transfer state change')
795
+ test.equal(transfer.transferState, TransferState.COMMITTED, `Transfer state changed to ${TransferState.COMMITTED}`)
796
+ test.equal(transfer.fulfilment, td.fulfilPayload.fulfilment, 'Commit ilpFulfilment saved')
797
+ test.equal(payeeCurrentPosition.value, payeeExpectedPosition, 'Payee position decremented by transfer amount and updated in participantPosition')
798
+ test.equal(payeePositionChange.value, payeeCurrentPosition.value, 'Payee position change value inserted and matches the updated participantPosition value')
799
+ test.equal(payeePositionChange.transferStateChangeId, transfer.transferStateChangeId, 'Payee position change record is bound to the corresponding transfer state change')
405
800
  }
406
801
 
407
802
  try {
408
803
  await retry(async () => { // use bail(new Error('to break before max retries'))
409
804
  const transfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId) || {}
410
- if (transfer.transferState !== TransferInternalState.ABORTED_REJECTED) {
805
+ if (transfer.transferState !== TransferState.COMMITTED) {
411
806
  if (debug) console.log(`retrying in ${retryDelay / 1000}s..`)
412
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`)
807
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `#2 Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`)
413
808
  }
414
809
  return tests()
415
810
  }, retryOpts)
@@ -419,8 +814,44 @@ Test('Handlers test', async handlersTest => {
419
814
  }
420
815
  test.end()
421
816
  })
817
+ transferFulfilCommit.end()
818
+ })
819
+
820
+ await handlersTest.test('transferFulfilReject should', async transferFulfilReject => {
821
+ testData.amount.amount = 15
822
+ const td = await prepareTestData(testData)
823
+
824
+ await transferFulfilReject.test('update transfer state to RESERVED by PREPARE request', async (test) => {
825
+ const config = Utility.getKafkaConfig(
826
+ Config.KAFKA_CONFIG,
827
+ Enum.Kafka.Config.PRODUCER,
828
+ TransferEventType.TRANSFER.toUpperCase(),
829
+ TransferEventType.PREPARE.toUpperCase())
830
+ config.logger = Logger
831
+
832
+ const producerResponse = await Producer.produceMessage(td.messageProtocolPrepare, td.topicConfTransferPrepare, config)
422
833
 
423
- transferFulfilReject.end()
834
+ const tests = async () => {
835
+ const transfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId) || {}
836
+ test.equal(producerResponse, true, 'Producer for prepare published message')
837
+ test.equal(transfer.transferState, TransferState.RESERVED, `Transfer state changed to ${TransferState.RESERVED}`)
838
+ }
839
+
840
+ try {
841
+ await retry(async () => { // use bail(new Error('to break before max retries'))
842
+ const transfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId) || {}
843
+ if (transfer.transferState !== TransferState.RESERVED) {
844
+ if (debug) console.log(`retrying in ${retryDelay / 1000}s..`)
845
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `#3 Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`)
846
+ }
847
+ return tests()
848
+ }, retryOpts)
849
+ } catch (err) {
850
+ Logger.error(err)
851
+ test.fail(err.message)
852
+ }
853
+ test.end()
854
+ })
424
855
  })
425
856
 
426
857
  await handlersTest.test('transferPrepareExceedLimit should', async transferPrepareExceedLimit => {
@@ -448,7 +879,7 @@ Test('Handlers test', async handlersTest => {
448
879
  const transfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId) || {}
449
880
  if (transfer.transferState !== TransferInternalState.ABORTED_REJECTED) {
450
881
  if (debug) console.log(`retrying in ${retryDelay / 1000}s..`)
451
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`)
882
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `#4 Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`)
452
883
  }
453
884
  return tests()
454
885
  }, retryOpts)
@@ -487,7 +918,7 @@ Test('Handlers test', async handlersTest => {
487
918
  const transfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId) || {}
488
919
  if (transfer.transferState !== TransferState.RESERVED) {
489
920
  if (debug) console.log(`retrying in ${retryDelay / 1000}s..`)
490
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`)
921
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `#5 Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`)
491
922
  }
492
923
  return tests()
493
924
  }, retryOpts)
@@ -534,7 +965,7 @@ Test('Handlers test', async handlersTest => {
534
965
  const transfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId) || {}
535
966
  if (transfer.transferState !== TransferInternalState.ABORTED_ERROR) {
536
967
  if (debug) console.log(`retrying in ${retryDelay / 1000}s..`)
537
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`)
968
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `#6 Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`)
538
969
  }
539
970
  return tests()
540
971
  }, retryOpts)
@@ -580,7 +1011,7 @@ Test('Handlers test', async handlersTest => {
580
1011
  const transfer = await TransferService.getById(td.messageProtocolPrepare.content.payload.transferId) || {}
581
1012
  if (transfer.transferState !== TransferState.RESERVED) {
582
1013
  if (debug) console.log(`retrying in ${retryDelay / 1000}s..`)
583
- throw new Error(`Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`)
1014
+ throw new Error(`#7 Max retry count ${retryCount} reached after ${retryCount * retryDelay / 1000}s. Tests fail`)
584
1015
  }
585
1016
  return tests()
586
1017
  }, retryOpts)
@@ -595,11 +1026,17 @@ Test('Handlers test', async handlersTest => {
595
1026
  await timeoutTest.test('position resets after a timeout', async (test) => {
596
1027
  // Arrange
597
1028
  const payerInitialPosition = td.payerLimitAndInitialPosition.participantPosition.value
1029
+
598
1030
  // Act
599
- await sleepPromise(15) // give the timeout handler some time to expire the request
1031
+ const payerPositionDidReset = async () => {
1032
+ const payerCurrentPosition = await ParticipantService.getPositionByParticipantCurrencyId(td.payer.participantCurrencyId)
1033
+ return payerCurrentPosition.value === payerInitialPosition
1034
+ }
1035
+ // wait until we know the position reset, or throw after 5 tries
1036
+ await wrapWithRetries(payerPositionDidReset, 10, 4)
600
1037
  const payerCurrentPosition = await ParticipantService.getPositionByParticipantCurrencyId(td.payer.participantCurrencyId) || {}
601
1038
 
602
- // Assert // TODO: ggrg (20191108) not always valid!? (docker restart fixed it)
1039
+ // Assert
603
1040
  test.equal(payerCurrentPosition.value, payerInitialPosition, 'Position resets after a timeout')
604
1041
  test.end()
605
1042
  })
@@ -613,6 +1050,7 @@ Test('Handlers test', async handlersTest => {
613
1050
  await Cache.destroyCache()
614
1051
  await Db.disconnect()
615
1052
  assert.pass('database connection closed')
1053
+ await testConsumer.destroy()
616
1054
 
617
1055
  const topics = [
618
1056
  'topic-transfer-prepare',
@@ -641,6 +1079,7 @@ Test('Handlers test', async handlersTest => {
641
1079
  const elapsedTime = Math.round(((new Date()) - startTime) / 100) / 10
642
1080
  console.log(`handlers.test.js finished in (${elapsedTime}s)`)
643
1081
  }
1082
+
644
1083
  assert.end()
645
1084
  } catch (err) {
646
1085
  Logger.error(`teardown failed with error - ${err}`)