account-lookup-service 15.5.0-iso.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (209) hide show
  1. package/.circleci/config.yml +11 -0
  2. package/.ncurc.yaml +6 -0
  3. package/.nvmrc +1 -0
  4. package/.nycrc.yml +20 -0
  5. package/.versionrc +15 -0
  6. package/CHANGELOG.md +330 -0
  7. package/CODEOWNERS +38 -0
  8. package/Dockerfile +45 -0
  9. package/LICENSE.md +10 -0
  10. package/README.md +252 -0
  11. package/audit-ci.jsonc +32 -0
  12. package/audit-resolve.json +161 -0
  13. package/config/default.json +109 -0
  14. package/config/knexfile.js +21 -0
  15. package/docker/account-lookup-service/default.json +106 -0
  16. package/docker/account-lookup-service/make-default-json.sh +5 -0
  17. package/docker/account-lookup-service/override.json +15 -0
  18. package/docker/central-ledger/default.json +458 -0
  19. package/docker/config-modifier/account-lookup-service.js +31 -0
  20. package/docker/kafka/consumer.properties +26 -0
  21. package/docker/kafka/producer.properties +45 -0
  22. package/docker/kafka/server.properties +143 -0
  23. package/docker/kafka/tools-log4j.properties +21 -0
  24. package/docker/mock-proxy/Dockerfile +15 -0
  25. package/docker/mock-proxy/package-lock.json +4986 -0
  26. package/docker/mock-proxy/package.json +24 -0
  27. package/docker/mock-proxy/src/config.ts +14 -0
  28. package/docker/mock-proxy/src/server.ts +94 -0
  29. package/docker/mock-proxy/src/utils.ts +29 -0
  30. package/docker/mock-proxy/tsconfig.json +24 -0
  31. package/docker/sql-init/01_permissions.sql +2 -0
  32. package/docker/sql-init-central-ledger/01_permissions.sql +2 -0
  33. package/docker/wait-for/wait-for-account-lookup-service.sh +10 -0
  34. package/docker/wait-for/wait-for-central-ledger.sh +11 -0
  35. package/docker/wait-for/wait-for-kafka.sh +7 -0
  36. package/docker/wait-for/wait-for-ml-api-adapter.sh +9 -0
  37. package/docker/wait-for/wait-for-mockserver.sh +20 -0
  38. package/docker/wait-for/wait-for-mysql-als.sh +14 -0
  39. package/docker/wait-for/wait-for-mysql-central-ledger.sh +11 -0
  40. package/docker/wait-for/wait-for-mysql.sh +11 -0
  41. package/docker/wait-for/wait-for-objstore.sh +12 -0
  42. package/docker/wait-for/wait-for.env +18 -0
  43. package/docker/wait-for/wait-for.sh +81 -0
  44. package/docker-compose.integration.yml +29 -0
  45. package/docker-compose.yml +243 -0
  46. package/jest-int.config.js +8 -0
  47. package/jest.config.js +16 -0
  48. package/jsdoc.json +38 -0
  49. package/migrations/01_currency.js +42 -0
  50. package/migrations/02_endpointType.js +43 -0
  51. package/migrations/03_endpointType-indexes.js +37 -0
  52. package/migrations/04_partyIdType.js +43 -0
  53. package/migrations/05_partyIdType-indexes.js +38 -0
  54. package/migrations/08_oracleEndpoint.js +51 -0
  55. package/migrations/09_oracleEndpoint-indexes.js +41 -0
  56. package/migrations/10_oracleEndpoint-remove-constraints.js +38 -0
  57. package/package.json +180 -0
  58. package/scripts/_wait4_all.js +143 -0
  59. package/scripts/test-functional.sh +76 -0
  60. package/secrets/jwsSigningKey.key +27 -0
  61. package/seeds/currency.js +765 -0
  62. package/seeds/endpointType.js +65 -0
  63. package/seeds/partyIdType.js +79 -0
  64. package/src/api/endpointcache.js +67 -0
  65. package/src/api/health.js +66 -0
  66. package/src/api/index.js +85 -0
  67. package/src/api/oracles/{ID}.js +100 -0
  68. package/src/api/oracles.js +96 -0
  69. package/src/api/participants/{ID}/error.js +44 -0
  70. package/src/api/participants/{ID}.js +44 -0
  71. package/src/api/participants/{Type}/{ID}/error.js +74 -0
  72. package/src/api/participants/{Type}/{ID}/{SubId}/error.js +68 -0
  73. package/src/api/participants/{Type}/{ID}/{SubId}.js +113 -0
  74. package/src/api/participants/{Type}/{ID}.js +133 -0
  75. package/src/api/participants.js +63 -0
  76. package/src/api/parties/{Type}/{ID}/error.js +66 -0
  77. package/src/api/parties/{Type}/{ID}/{SubId}/error.js +56 -0
  78. package/src/api/parties/{Type}/{ID}/{SubId}.js +77 -0
  79. package/src/api/parties/{Type}/{ID}.js +98 -0
  80. package/src/api/routes.js +294 -0
  81. package/src/constants.js +16 -0
  82. package/src/domain/oracle/index.js +33 -0
  83. package/src/domain/oracle/oracle.js +234 -0
  84. package/src/domain/participants/index.js +35 -0
  85. package/src/domain/participants/participants.js +560 -0
  86. package/src/domain/parties/getPartiesByTypeAndID.js +239 -0
  87. package/src/domain/parties/index.js +32 -0
  88. package/src/domain/parties/parties.js +215 -0
  89. package/src/domain/parties/utils.js +84 -0
  90. package/src/domain/timeout/dto.js +48 -0
  91. package/src/domain/timeout/index.js +104 -0
  92. package/src/handlers/TimeoutHandler.js +94 -0
  93. package/src/handlers/index.js +70 -0
  94. package/src/handlers/monitoring/index.js +51 -0
  95. package/src/handlers/monitoring/plugins/health.js +61 -0
  96. package/src/handlers/monitoring/plugins/metrics.js +48 -0
  97. package/src/handlers/register.js +102 -0
  98. package/src/index.js +66 -0
  99. package/src/interface/admin-swagger.yaml +804 -0
  100. package/src/interface/admin_swagger.json +959 -0
  101. package/src/interface/api-swagger-iso20022-parties.yaml +1734 -0
  102. package/src/interface/api-swagger.yaml +1733 -0
  103. package/src/interface/api_swagger.json +3046 -0
  104. package/src/interface/fspiop-rest-v2.0-ISO20022_parties.yaml +2256 -0
  105. package/src/interface/thirdparty/admin-swagger.yaml +808 -0
  106. package/src/interface/thirdparty/admin_swagger.json +961 -0
  107. package/src/interface/thirdparty/api-swagger.yaml +1739 -0
  108. package/src/interface/thirdparty/api_swagger.json +3142 -0
  109. package/src/lib/argv.js +39 -0
  110. package/src/lib/cache.js +126 -0
  111. package/src/lib/config.js +183 -0
  112. package/src/lib/db.js +26 -0
  113. package/src/lib/headers.js +53 -0
  114. package/src/lib/healthCheck/subServiceHealth.js +84 -0
  115. package/src/lib/index.js +11 -0
  116. package/src/lib/migrator.js +17 -0
  117. package/src/lib/requestLogger.js +54 -0
  118. package/src/lib/util.js +66 -0
  119. package/src/metrics/handler.js +33 -0
  120. package/src/metrics/plugin.js +52 -0
  121. package/src/metrics/routes.js +43 -0
  122. package/src/models/currency/currency.js +48 -0
  123. package/src/models/currency/index.js +32 -0
  124. package/src/models/endpointType/endpointType.js +48 -0
  125. package/src/models/endpointType/index.js +32 -0
  126. package/src/models/misc/migrationLock.js +49 -0
  127. package/src/models/oracle/facade.js +341 -0
  128. package/src/models/oracle/index.js +41 -0
  129. package/src/models/oracle/oracleEndpoint.js +192 -0
  130. package/src/models/oracle/oracleEndpointCached.js +108 -0
  131. package/src/models/participantEndpoint/facade.js +238 -0
  132. package/src/models/partyIdType/index.js +32 -0
  133. package/src/models/partyIdType/partyIdType.js +41 -0
  134. package/src/plugins.js +139 -0
  135. package/src/server.js +199 -0
  136. package/test/fixtures/index.js +131 -0
  137. package/test/fixtures/iso.js +110 -0
  138. package/test/integration/.env +8 -0
  139. package/test/integration/api/parties.test.js +137 -0
  140. package/test/integration/constants.js +20 -0
  141. package/test/integration/domain/oracle/index.test.js +324 -0
  142. package/test/integration/domain/timeout/index.test.js +75 -0
  143. package/test/integration/env.sh +15 -0
  144. package/test/integration/example.test.js +12 -0
  145. package/test/integration/models/currency/currency.test.js +68 -0
  146. package/test/integration/plugins.test.js +62 -0
  147. package/test/integration/prepareTestParticipants.js +30 -0
  148. package/test/integration/setup.js +5 -0
  149. package/test/integration-config.json +81 -0
  150. package/test/integration-runner.sh +108 -0
  151. package/test/unit/api/health.test.js +142 -0
  152. package/test/unit/api/oracles/{ID}.test.js +264 -0
  153. package/test/unit/api/oracles.test.js +173 -0
  154. package/test/unit/api/participants/participants.test.js +117 -0
  155. package/test/unit/api/participants/{Type}/{ID}/error.test.js +155 -0
  156. package/test/unit/api/participants/{Type}/{ID}/{SubId}/error.test.js +131 -0
  157. package/test/unit/api/participants/{Type}/{ID}/{SubId}.test.js +377 -0
  158. package/test/unit/api/participants/{Type}/{ID}.test.js +383 -0
  159. package/test/unit/api/participants.test.js +108 -0
  160. package/test/unit/api/parties/endpointcache.test.js +83 -0
  161. package/test/unit/api/parties/parties.test.js +102 -0
  162. package/test/unit/api/parties/{Type}/{ID}/error.test.js +145 -0
  163. package/test/unit/api/parties/{Type}/{ID}/{SubId}/error.test.js +141 -0
  164. package/test/unit/api/parties/{Type}/{ID}/{SubId}.test.js +241 -0
  165. package/test/unit/api/parties/{Type}/{ID}.test.js +240 -0
  166. package/test/unit/domain/oracle/oracle.test.js +505 -0
  167. package/test/unit/domain/participants/participants.test.js +1724 -0
  168. package/test/unit/domain/parties/parties.test.js +940 -0
  169. package/test/unit/domain/timeout/dto.test.js +28 -0
  170. package/test/unit/domain/timeout/index.test.js +81 -0
  171. package/test/unit/handlers/TimeoutHandler.test.js +125 -0
  172. package/test/unit/handlers/index.test.js +56 -0
  173. package/test/unit/handlers/register.test.js +90 -0
  174. package/test/unit/index.test.js +139 -0
  175. package/test/unit/iso20022/partiesValidation.test.js +129 -0
  176. package/test/unit/lib/TransformFacades.test.js +18 -0
  177. package/test/unit/lib/argv.test.js +40 -0
  178. package/test/unit/lib/cache.test.js +172 -0
  179. package/test/unit/lib/config.test.js +108 -0
  180. package/test/unit/lib/healthCheck/subServiceHealth.test.js +89 -0
  181. package/test/unit/lib/migrator.test.js +52 -0
  182. package/test/unit/lib/requestLogger.test.js +115 -0
  183. package/test/unit/lib/util.test.js +68 -0
  184. package/test/unit/mocks.js +66 -0
  185. package/test/unit/models/currency/currency.test.js +91 -0
  186. package/test/unit/models/endpointType/endpointType.test.js +69 -0
  187. package/test/unit/models/misc/migrationLock.test.js +96 -0
  188. package/test/unit/models/oracle/facade.test.js +546 -0
  189. package/test/unit/models/oracle/oracleEndpoint.test.js +409 -0
  190. package/test/unit/models/oracle/oracleEndpointCached.test.js +153 -0
  191. package/test/unit/models/participantEndpoint/facade.test.js +295 -0
  192. package/test/unit/models/partyIdType/partyIdType.test.js +88 -0
  193. package/test/unit/plugins.test.js +89 -0
  194. package/test/unit/setup.js +7 -0
  195. package/test/util/apiClients/AlsApiClient.js +44 -0
  196. package/test/util/apiClients/BasicApiClient.js +34 -0
  197. package/test/util/apiClients/ProxyApiClient.js +25 -0
  198. package/test/util/apiClients/index.js +7 -0
  199. package/test/util/helper.js +332 -0
  200. package/test/util/index.js +11 -0
  201. package/test/util/mockgen.js +43 -0
  202. package/test/util/onboarding.js +132 -0
  203. package/test/util/scripts/addAlsDb.sh +33 -0
  204. package/test/util/scripts/configureMockServer.sh +35 -0
  205. package/test/util/scripts/env.sh +19 -0
  206. package/test/util/scripts/populateTestData.sh +62 -0
  207. package/test/util/scripts/startMockCentralServer.sh +45 -0
  208. package/test/util/scripts/startMockOracleServer.sh +45 -0
  209. package/test/util/testConfig.js +44 -0
@@ -0,0 +1,1724 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2017 Bill & Melinda Gates Foundation
5
+ The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
8
+ Contributors
9
+ --------------
10
+ This is the official list of the Mojaloop project contributors for this file.
11
+ Names of the original copyright holders (individuals or organizations)
12
+ should be listed with a '*' in the first column. People who have
13
+ contributed from an organization can be listed under the organization
14
+ that actually holds the copyright for their contributions (see the
15
+ Gates Foundation organization for an example). Those individuals should have
16
+ their names indented and be marked with a '-'. Email address can be added
17
+ optionally within square brackets <email>.
18
+ * Gates Foundation
19
+ - Name Surname <name.surname@gatesfoundation.com>
20
+
21
+ * ModusBox
22
+ - Rajiv Mothilal <rajiv.mothilal@modusbox.com>
23
+ - Steven Oderayi <steven.oderayi@modusbox>
24
+
25
+ * Crosslake
26
+ - Lewis Daly <lewisd@crosslaketech.com>
27
+
28
+ --------------
29
+ ******/
30
+
31
+ 'use strict'
32
+
33
+ const Sinon = require('sinon')
34
+ const Enums = require('@mojaloop/central-services-shared').Enum
35
+ const Logger = require('@mojaloop/central-services-logger')
36
+ const ErrorHandler = require('@mojaloop/central-services-error-handling')
37
+
38
+ const participantsDomain = require('../../../../src/domain/participants/participants')
39
+ const participant = require('../../../../src/models/participantEndpoint/facade')
40
+ const oracle = require('../../../../src/models/oracle/facade')
41
+ const Helper = require('../../../util/helper')
42
+ const Config = require('../../../../src/lib/config')
43
+
44
+ Logger.isDebugEnabled = jest.fn(() => true)
45
+ Logger.isErrorEnabled = jest.fn(() => true)
46
+ Logger.isInfoEnabled = jest.fn(() => true)
47
+
48
+ describe('participant Tests', () => {
49
+ describe('getParticipantsByTypeAndID', () => {
50
+ let sandbox
51
+
52
+ beforeEach(() => {
53
+ sandbox = Sinon.createSandbox()
54
+ sandbox.stub(participant)
55
+ sandbox.stub(oracle)
56
+ })
57
+
58
+ afterEach(() => {
59
+ sandbox.restore()
60
+ })
61
+
62
+ it('gets participants and sends callback', async () => {
63
+ expect.hasAssertions()
64
+ // Arrange
65
+ participant.validateParticipant = sandbox.stub().resolves({})
66
+ oracle.oracleRequest = sandbox.stub().resolves({
67
+ data: {
68
+ partyList: [
69
+ { fspId: 'fsp1' }
70
+ ]
71
+ }
72
+ })
73
+ participant.sendRequest = sandbox.stub()
74
+ const args = [
75
+ Helper.getByTypeIdCurrencyRequest.headers,
76
+ Helper.getByTypeIdCurrencyRequest.params,
77
+ Helper.getByTypeIdCurrencyRequest.method,
78
+ Helper.getByTypeIdCurrencyRequest.query,
79
+ Helper.mockSpan()
80
+ ]
81
+
82
+ // Act
83
+ await participantsDomain.getParticipantsByTypeAndID(...args)
84
+
85
+ // Assert
86
+ expect(participant.sendRequest.callCount).toBe(1)
87
+ const firstCallArgs = participant.sendRequest.getCall(0).args
88
+ expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe('payeefsp')
89
+ })
90
+
91
+ it('gets participants and sends callback when SubId is supplied', async () => {
92
+ expect.hasAssertions()
93
+ // Arrange
94
+ participant.validateParticipant = sandbox.stub().resolves({})
95
+ oracle.oracleRequest = sandbox.stub().resolves({
96
+ data: {
97
+ partyList: [
98
+ { fspId: 'fsp1' }
99
+ ]
100
+ }
101
+ })
102
+ participant.sendRequest = sandbox.stub()
103
+ const params = { ...Helper.getByTypeIdCurrencyRequest.params, SubId: 'subId' }
104
+ const args = [
105
+ Helper.getByTypeIdCurrencyRequest.headers,
106
+ params,
107
+ Helper.getByTypeIdCurrencyRequest.method,
108
+ Helper.getByTypeIdCurrencyRequest.query
109
+ ]
110
+ const expectedCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT
111
+
112
+ // Act
113
+ await participantsDomain.getParticipantsByTypeAndID(...args)
114
+
115
+ // Assert
116
+ expect(participant.sendRequest.callCount).toBe(1)
117
+ const firstCallArgs = participant.sendRequest.getCall(0).args
118
+ expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe('payeefsp')
119
+ expect(firstCallArgs[2]).toBe(expectedCallbackEndpointType)
120
+ expect(firstCallArgs[5].partySubIdOrType).toBe('subId')
121
+ })
122
+
123
+ it('fails to get participants and sends error callback with appropriate endpoint type when SubId is specified', async () => {
124
+ expect.hasAssertions()
125
+ // Arrange
126
+ participant.validateParticipant = sandbox.stub().resolves({})
127
+ participant.sendErrorToParticipant = sandbox.stub().resolves({})
128
+ oracle.oracleRequest = sandbox.stub().resolves(null)
129
+ participant.sendRequest = sandbox.stub()
130
+ const params = { ...Helper.getByTypeIdCurrencyRequest.params, SubId: 'subId' }
131
+ const args = [
132
+ Helper.getByTypeIdCurrencyRequest.headers,
133
+ params,
134
+ Helper.getByTypeIdCurrencyRequest.method,
135
+ Helper.getByTypeIdCurrencyRequest.query,
136
+ Helper.mockSpan()
137
+ ]
138
+ const expectedErrorCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
139
+
140
+ // Act
141
+ await participantsDomain.getParticipantsByTypeAndID(...args)
142
+
143
+ // Assert
144
+ expect(participant.sendRequest.callCount).toBe(0)
145
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
146
+ expect(firstCallArgs[1]).toBe(expectedErrorCallbackEndpointType)
147
+ })
148
+
149
+ it('gets participants and sends callback when `fspiop-dest` is not set', async () => {
150
+ expect.hasAssertions()
151
+ // Arrange
152
+ participant.validateParticipant = sandbox.stub().resolves({})
153
+ oracle.oracleRequest = sandbox.stub().resolves({
154
+ data: {
155
+ partyList: [
156
+ { fspId: 'fsp1' }
157
+ ]
158
+ }
159
+ })
160
+ participant.sendRequest = sandbox.stub()
161
+ const headers = {
162
+ accept: 'application/vnd.interoperability.participants+json;version=1',
163
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
164
+ date: '2019-05-24 08:52:19',
165
+ 'fspiop-source': 'payerfsp'
166
+ }
167
+ const args = [
168
+ headers,
169
+ Helper.getByTypeIdCurrencyRequest.params,
170
+ Helper.getByTypeIdCurrencyRequest.method,
171
+ Helper.getByTypeIdCurrencyRequest.query,
172
+ Helper.mockSpan()
173
+ ]
174
+
175
+ // Act
176
+ await participantsDomain.getParticipantsByTypeAndID(...args)
177
+
178
+ // Assert
179
+ expect(participant.sendRequest.callCount).toBe(1)
180
+ const firstCallArgs = participant.sendRequest.getCall(0).args
181
+ expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe(headers['fspiop-source'])
182
+ })
183
+
184
+ it('fails with `Requester FSP not found` if `validateParticipant` fails', async () => {
185
+ expect.hasAssertions()
186
+ // Arrange
187
+ participant.validateParticipant = sandbox.stub().resolves(null)
188
+ const logErrorStub = sandbox.stub(Logger, 'error')
189
+
190
+ participant.sendRequest = sandbox.stub()
191
+ const args = [
192
+ Helper.getByTypeIdCurrencyRequest.headers,
193
+ Helper.getByTypeIdCurrencyRequest.params,
194
+ Helper.getByTypeIdCurrencyRequest.method,
195
+ Helper.getByTypeIdCurrencyRequest.query
196
+ ]
197
+
198
+ // Act
199
+ await participantsDomain.getParticipantsByTypeAndID(...args)
200
+
201
+ // Assert
202
+ const firstCallArgs = logErrorStub.getCall(0).args
203
+ expect(firstCallArgs[0]).toBe('Requester FSP not found')
204
+ })
205
+
206
+ it('fails when `oracleRequest` response is empty', async () => {
207
+ expect.hasAssertions()
208
+ // Arrange
209
+ participant.validateParticipant = sandbox.stub().resolves({})
210
+ oracle.oracleRequest = sandbox.stub().resolves(null)
211
+ participant.sendErrorToParticipant = sandbox.stub()
212
+
213
+ const args = [
214
+ Helper.getByTypeIdCurrencyRequest.headers,
215
+ Helper.getByTypeIdCurrencyRequest.params,
216
+ Helper.getByTypeIdCurrencyRequest.method,
217
+ Helper.getByTypeIdCurrencyRequest.query
218
+ ]
219
+
220
+ // Act
221
+ await participantsDomain.getParticipantsByTypeAndID(...args)
222
+
223
+ // Assert
224
+ expect(participant.sendErrorToParticipant.callCount).toBe(1)
225
+ })
226
+
227
+ it('handles error when `sendRequest` and sendErrorToParticipant` fails', async () => {
228
+ expect.hasAssertions()
229
+ // Arrange
230
+ participant.validateParticipant = sandbox.stub().resolves({})
231
+ oracle.oracleRequest = sandbox.stub().resolves({
232
+ data: {
233
+ partyList: [
234
+ { fspId: 'fsp1' }
235
+ ]
236
+ }
237
+ })
238
+ participant.sendRequest = sandbox.stub().throws(new Error('sendRequest error'))
239
+ participant.sendErrorToParticipant = sandbox.stub().throws(new Error('sendErrorToParticipant error'))
240
+
241
+ const args = [
242
+ Helper.getByTypeIdCurrencyRequest.headers,
243
+ Helper.getByTypeIdCurrencyRequest.params,
244
+ Helper.getByTypeIdCurrencyRequest.method,
245
+ Helper.getByTypeIdCurrencyRequest.query
246
+ ]
247
+
248
+ // Act
249
+ await participantsDomain.getParticipantsByTypeAndID(...args)
250
+
251
+ // Assert
252
+ expect(participant.sendRequest.callCount).toBe(1)
253
+ expect(participant.sendErrorToParticipant.callCount).toBe(1)
254
+ })
255
+
256
+ it('handles error when `sendRequest` and sendErrorToParticipant` fails, but sends callback with a specific endpoint type when SubId is present', async () => {
257
+ expect.hasAssertions()
258
+ // Arrange
259
+ participant.validateParticipant = sandbox.stub().resolves({})
260
+ oracle.oracleRequest = sandbox.stub().resolves({
261
+ data: {
262
+ partyList: [
263
+ { fspId: 'fsp1' }
264
+ ]
265
+ }
266
+ })
267
+ participant.sendRequest = sandbox.stub().throws(new Error('sendRequest error'))
268
+ participant.sendErrorToParticipant = sandbox.stub().throws(new Error('sendErrorToParticipant error'))
269
+
270
+ const params = { ...Helper.getByTypeIdCurrencyRequest.params, SubId: 'subId' }
271
+ const args = [
272
+ Helper.getByTypeIdCurrencyRequest.headers,
273
+ params,
274
+ Helper.getByTypeIdCurrencyRequest.method,
275
+ Helper.getByTypeIdCurrencyRequest.query
276
+ ]
277
+ const expectedErrorCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
278
+
279
+ // Act
280
+ await participantsDomain.getParticipantsByTypeAndID(...args)
281
+
282
+ // Assert
283
+ expect(participant.sendRequest.callCount).toBe(1)
284
+ expect(participant.sendErrorToParticipant.callCount).toBe(1)
285
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
286
+ expect(firstCallArgs[1]).toBe(expectedErrorCallbackEndpointType)
287
+ })
288
+ })
289
+
290
+ describe('putParticipantsByTypeAndID', () => {
291
+ let sandbox
292
+
293
+ beforeEach(() => {
294
+ sandbox = Sinon.createSandbox()
295
+ })
296
+
297
+ afterEach(() => {
298
+ sandbox.restore()
299
+ })
300
+
301
+ it('sends put request to the participant', async () => {
302
+ expect.hasAssertions()
303
+ // Arrange
304
+ participant.validateParticipant = sandbox.stub().resolves({})
305
+ oracle.oracleRequest = sandbox.stub().resolves({
306
+ data: {
307
+ partyList: [
308
+ { fspId: 'fsp1' }
309
+ ]
310
+ }
311
+ })
312
+ participant.sendRequest = sandbox.stub()
313
+ const headers = {
314
+ accept: 'application/vnd.interoperability.participants+json;version=1',
315
+ 'fspiop-destination': Config.HUB_NAME,
316
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
317
+ date: '2019-05-24 08:52:19',
318
+ 'fspiop-source': 'fsp1'
319
+ }
320
+ const params = {
321
+ ID: '123456',
322
+ Type: 'MSISDN'
323
+ }
324
+ const method = 'put'
325
+ const payload = {
326
+ fspId: 'fsp1',
327
+ currency: 'USD'
328
+ }
329
+
330
+ // Act
331
+ await participantsDomain.putParticipantsByTypeAndID(headers, params, method, payload)
332
+
333
+ // Assert
334
+ expect(participant.sendRequest.callCount).toBe(1)
335
+ const firstCallArgs = participant.sendRequest.getCall(0).args
336
+ expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe(Config.HUB_NAME)
337
+ })
338
+
339
+ it('sends put request to the participant with SubId', async () => {
340
+ expect.hasAssertions()
341
+ // Arrange
342
+ participant.validateParticipant = sandbox.stub().resolves({})
343
+ oracle.oracleRequest = sandbox.stub().resolves({
344
+ data: {
345
+ partyList: [
346
+ { fspId: 'fsp1' }
347
+ ]
348
+ }
349
+ })
350
+ participant.sendRequest = sandbox.stub()
351
+ const expectedCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT
352
+ const headers = {
353
+ accept: 'application/vnd.interoperability.participants+json;version=1',
354
+ 'fspiop-destination': Config.HUB_NAME,
355
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
356
+ date: '2019-05-24 08:52:19',
357
+ 'fspiop-source': 'fsp1'
358
+ }
359
+ const params = {
360
+ ID: '123456',
361
+ Type: 'MSISDN',
362
+ SubId: 'subId'
363
+ }
364
+ const method = 'put'
365
+ const payload = {
366
+ fspId: 'fsp1',
367
+ currency: 'USD'
368
+ }
369
+
370
+ // Act
371
+ await participantsDomain.putParticipantsByTypeAndID(headers, params, method, payload)
372
+
373
+ // Assert
374
+ expect(participant.sendRequest.callCount).toBe(1)
375
+ const firstCallArgs = participant.sendRequest.getCall(0).args
376
+ expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe(Config.HUB_NAME)
377
+ expect(firstCallArgs[2]).toBe(expectedCallbackEndpointType)
378
+ expect(firstCallArgs[4].partyList[0].partySubIdOrType).toBe('subId')
379
+ })
380
+
381
+ it('handles put request without fspiop-dest header', async () => {
382
+ expect.hasAssertions()
383
+ // Arrange
384
+ participant.validateParticipant = sandbox.stub().resolves({})
385
+ oracle.oracleRequest = sandbox.stub().resolves({
386
+ data: {
387
+ partyList: [
388
+ { fspId: 'fsp2' }
389
+ ]
390
+ }
391
+ })
392
+ participant.sendRequest = sandbox.stub()
393
+ const headers = {
394
+ accept: 'application/vnd.interoperability.participants+json;version=1',
395
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
396
+ date: '2019-05-24 08:52:19',
397
+ 'fspiop-source': 'fsp1'
398
+ }
399
+ const params = {
400
+ ID: '123456',
401
+ Type: 'MSISDN'
402
+ }
403
+ const method = 'put'
404
+ const payload = {
405
+ fspId: 'fsp2',
406
+ currency: 'USD'
407
+ }
408
+
409
+ // Act
410
+ await participantsDomain.putParticipantsByTypeAndID(headers, params, method, payload)
411
+
412
+ // Assert
413
+ expect(participant.sendRequest.callCount).toBe(1)
414
+ const firstCallArgs = participant.sendRequest.getCall(0).args
415
+ expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe('fsp2')
416
+ })
417
+
418
+ it('handles the case where `oracleRequest` returns has no response.data', async () => {
419
+ expect.hasAssertions()
420
+ // Arrange
421
+ participant.validateParticipant = sandbox.stub().resolves({})
422
+ oracle.oracleRequest = sandbox.stub().resolves({})
423
+ participant.sendErrorToParticipant = sandbox.stub()
424
+ const headers = {
425
+ accept: 'application/vnd.interoperability.participants+json;version=1',
426
+ 'fspiop-destination': Config.HUB_NAME,
427
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
428
+ date: '2019-05-24 08:52:19',
429
+ 'fspiop-source': 'fsp1'
430
+ }
431
+ const params = {
432
+ ID: '123456',
433
+ Type: 'MSISDN'
434
+ }
435
+ const method = 'put'
436
+ const payload = {
437
+ fspId: 'fsp1',
438
+ currency: 'USD'
439
+ }
440
+
441
+ // Act
442
+ await participantsDomain.putParticipantsByTypeAndID(headers, params, method, payload)
443
+
444
+ // Assert
445
+ expect(participant.sendErrorToParticipant.callCount).toBe(1)
446
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
447
+ expect(firstCallArgs[0]).toBe('fsp1')
448
+ })
449
+
450
+ it('handles the case where SubId is supplied but `oracleRequest` returns has no response.data', async () => {
451
+ expect.hasAssertions()
452
+ // Arrange
453
+ participant.validateParticipant = sandbox.stub().resolves({})
454
+ oracle.oracleRequest = sandbox.stub().resolves({})
455
+ participant.sendErrorToParticipant = sandbox.stub()
456
+ const expectedErrorCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
457
+ const headers = {
458
+ accept: 'application/vnd.interoperability.participants+json;version=1',
459
+ 'fspiop-destination': Config.HUB_NAME,
460
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
461
+ date: '2019-05-24 08:52:19',
462
+ 'fspiop-source': 'fsp1'
463
+ }
464
+ const params = {
465
+ ID: '123456',
466
+ Type: 'MSISDN',
467
+ SubId: 'subId'
468
+ }
469
+ const method = 'put'
470
+ const payload = {
471
+ fspId: 'fsp1',
472
+ currency: 'USD'
473
+ }
474
+
475
+ // Act
476
+ await participantsDomain.putParticipantsByTypeAndID(headers, params, method, payload)
477
+
478
+ // Assert
479
+ expect(participant.sendErrorToParticipant.callCount).toBe(1)
480
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
481
+ expect(firstCallArgs[0]).toBe('fsp1')
482
+ expect(firstCallArgs[1]).toBe(expectedErrorCallbackEndpointType)
483
+ })
484
+
485
+ it('handles the case where `validateParticipant` returns null', async () => {
486
+ expect.hasAssertions()
487
+ // Arrange
488
+ participant.validateParticipant = sandbox.stub().resolves(null)
489
+ sandbox.stub(Logger)
490
+ Logger.error = sandbox.stub()
491
+ participant.sendErrorToParticipant = sandbox.stub()
492
+ const headers = {
493
+ accept: 'application/vnd.interoperability.participants+json;version=1',
494
+ 'fspiop-destination': Config.HUB_NAME,
495
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
496
+ date: '2019-05-24 08:52:19',
497
+ 'fspiop-source': 'fsp1'
498
+ }
499
+ const params = {
500
+ ID: '123456',
501
+ Type: 'MSISDN'
502
+ }
503
+ const method = 'put'
504
+ const payload = {
505
+ fspId: 'fsp1',
506
+ currency: 'USD'
507
+ }
508
+
509
+ // Act
510
+ await participantsDomain.putParticipantsByTypeAndID(headers, params, method, payload)
511
+
512
+ // Assert
513
+ const loggerFirstCallArgs = Logger.error.getCall(0).args
514
+ expect(loggerFirstCallArgs[0]).toBe('Requester FSP not found')
515
+ })
516
+
517
+ it('handles case where type is not in `PartyAccountTypes`', async () => {
518
+ expect.hasAssertions()
519
+ // Arrange
520
+ sandbox.stub(Logger)
521
+ Logger.error = sandbox.stub()
522
+ participant.sendErrorToParticipant = sandbox.stub()
523
+
524
+ const headers = {
525
+ accept: 'application/vnd.interoperability.participants+json;version=1',
526
+ 'fspiop-destination': Config.HUB_NAME,
527
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
528
+ date: '2019-05-24 08:52:19',
529
+ 'fspiop-source': 'fsp1'
530
+ }
531
+ const params = {
532
+ ID: '123456',
533
+ Type: 'UNKNOWN_TYPE'
534
+ }
535
+ const method = 'put'
536
+ const payload = {
537
+ fspId: 'fsp1',
538
+ currency: 'USD'
539
+ }
540
+
541
+ // Act
542
+ await participantsDomain.putParticipantsByTypeAndID(headers, params, method, payload)
543
+
544
+ // Assert
545
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
546
+ expect(firstCallArgs[0]).toBe('fsp1')
547
+ })
548
+
549
+ it('handles case where type is not in `PartyAccountTypes` and `sendErrorToParticipant` fails', async () => {
550
+ expect.hasAssertions()
551
+ // Arrange
552
+ sandbox.stub(Logger)
553
+ Logger.error = sandbox.stub()
554
+ participant.sendErrorToParticipant = sandbox.stub().throws(new Error('Error sending error to participant'))
555
+
556
+ const headers = {
557
+ accept: 'application/vnd.interoperability.participants+json;version=1',
558
+ 'fspiop-destination': Config.HUB_NAME,
559
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
560
+ date: '2019-05-24 08:52:19',
561
+ 'fspiop-source': 'fsp1'
562
+ }
563
+ const params = {
564
+ ID: '123456',
565
+ Type: 'UNKNOWN_TYPE'
566
+ }
567
+ const method = 'put'
568
+ const payload = {
569
+ fspId: 'fsp1',
570
+ currency: 'USD'
571
+ }
572
+
573
+ // Act
574
+ await participantsDomain.putParticipantsByTypeAndID(headers, params, method, payload)
575
+
576
+ // Assert
577
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
578
+ expect(firstCallArgs[0]).toBe('fsp1')
579
+ })
580
+
581
+ it('handles case where SubId is supplied but validation fails and an error is thrown while sending error callback', async () => {
582
+ expect.hasAssertions()
583
+ // Arrange
584
+ sandbox.stub(Logger)
585
+ Logger.error = sandbox.stub()
586
+ participant.sendErrorToParticipant = sandbox.stub().throws(new Error('Error sending error to participant'))
587
+ const expectedErrorCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
588
+ const headers = {
589
+ accept: 'application/vnd.interoperability.participants+json;version=1',
590
+ 'fspiop-destination': Config.HUB_NAME,
591
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
592
+ date: '2019-05-24 08:52:19',
593
+ 'fspiop-source': 'fsp1'
594
+ }
595
+ const params = {
596
+ ID: '123456',
597
+ Type: 'UNKNOWN_TYPE',
598
+ SubId: 'subId'
599
+ }
600
+ const method = 'put'
601
+ const payload = {
602
+ fspId: 'fsp1',
603
+ currency: 'USD'
604
+ }
605
+
606
+ // Act
607
+ await participantsDomain.putParticipantsByTypeAndID(headers, params, method, payload)
608
+
609
+ // Assert
610
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
611
+ expect(firstCallArgs[0]).toBe('fsp1')
612
+ expect(firstCallArgs[1]).toBe(expectedErrorCallbackEndpointType)
613
+ })
614
+ })
615
+
616
+ describe('putParticipantsErrorByTypeAndID', () => {
617
+ let sandbox
618
+
619
+ beforeEach(() => {
620
+ sandbox = Sinon.createSandbox()
621
+ })
622
+
623
+ afterEach(() => {
624
+ sandbox.restore()
625
+ })
626
+
627
+ it('handles PUT /error', async () => {
628
+ expect.hasAssertions()
629
+ // Arrange
630
+ sandbox.stub(Logger)
631
+ Logger.info = sandbox.stub()
632
+ Logger.error = sandbox.stub()
633
+ participant.validateParticipant = sandbox.stub().resolves({})
634
+ oracle.oracleRequest = sandbox.stub().resolves(null)
635
+ participant.sendErrorToParticipant = sandbox.stub()
636
+ const headers = {
637
+ accept: 'application/vnd.interoperability.participants+json;version=1',
638
+ 'fspiop-destination': 'payerfsp',
639
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
640
+ date: '2019-05-24 08:52:19',
641
+ 'fspiop-source': Config.HUB_NAME
642
+ }
643
+ const params = {
644
+ ID: '123456',
645
+ Type: 'MSISDN'
646
+ }
647
+ const payload = {
648
+ fspId: 'payerfsp',
649
+ currency: 'USD'
650
+ }
651
+ const dataUri = ''
652
+
653
+ // Act
654
+ await participantsDomain.putParticipantsErrorByTypeAndID(headers, params, payload, dataUri)
655
+
656
+ expect(participant.sendErrorToParticipant.callCount).toBe(1)
657
+
658
+ // Assert
659
+ expect(Logger.info.callCount).toBe(0)
660
+ expect(Logger.error.callCount).toBe(0)
661
+ })
662
+
663
+ it('handles PUT /error when SubId is supplied', async () => {
664
+ expect.hasAssertions()
665
+ // Arrange
666
+ sandbox.stub(Logger)
667
+ Logger.info = sandbox.stub()
668
+ Logger.error = sandbox.stub()
669
+ participant.validateParticipant = sandbox.stub().resolves({})
670
+ oracle.oracleRequest = sandbox.stub().resolves(null)
671
+ participant.sendErrorToParticipant = sandbox.stub()
672
+ const expectedCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
673
+ const headers = {
674
+ accept: 'application/vnd.interoperability.participants+json;version=1',
675
+ 'fspiop-destination': 'payerfsp',
676
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
677
+ date: '2019-05-24 08:52:19',
678
+ 'fspiop-source': Config.HUB_NAME
679
+ }
680
+ const params = {
681
+ ID: '123456',
682
+ Type: 'MSISDN',
683
+ SubId: 'SubId'
684
+ }
685
+ const payload = {
686
+ fspId: 'payerfsp',
687
+ currency: 'USD'
688
+ }
689
+ const dataUri = ''
690
+
691
+ // Act
692
+ await participantsDomain.putParticipantsErrorByTypeAndID(headers, params, payload, dataUri)
693
+
694
+ // Assert
695
+ expect(participant.sendErrorToParticipant.callCount).toBe(1)
696
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
697
+ expect(firstCallArgs[1]).toBe(expectedCallbackEndpointType)
698
+ expect(Logger.error.callCount).toBe(0)
699
+ })
700
+
701
+ it('handles PUT /error when SubId supplied but validateParticipant fails to return participant', async () => {
702
+ expect.hasAssertions()
703
+ // Arrange
704
+ sandbox.stub(Logger)
705
+ Logger.info = sandbox.stub()
706
+ Logger.error = sandbox.stub()
707
+ participant.validateParticipant = sandbox.stub().resolves(null)
708
+ oracle.oracleRequest = sandbox.stub().resolves(null)
709
+ participant.sendErrorToParticipant = sandbox.stub()
710
+ const expectedCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
711
+ const headers = {
712
+ accept: 'application/vnd.interoperability.participants+json;version=1',
713
+ 'fspiop-destination': 'payerfsp',
714
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
715
+ date: '2019-05-24 08:52:19',
716
+ 'fspiop-source': Config.HUB_NAME
717
+ }
718
+ const params = {
719
+ ID: '123456',
720
+ Type: 'MSISDN',
721
+ SubId: 'SubId'
722
+ }
723
+ const payload = {
724
+ fspId: 'payerfsp',
725
+ currency: 'USD'
726
+ }
727
+ const dataUri = ''
728
+
729
+ // Act
730
+ await participantsDomain.putParticipantsErrorByTypeAndID(headers, params, payload, dataUri)
731
+
732
+ // Assert
733
+ expect(participant.sendErrorToParticipant.callCount).toBe(1)
734
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
735
+ expect(firstCallArgs[1]).toBe(expectedCallbackEndpointType)
736
+ expect(Logger.error.callCount).toBe(0)
737
+ })
738
+
739
+ it('handles PUT /error when SubId supplied but validateParticipant throws error', async () => {
740
+ expect.hasAssertions()
741
+ // Arrange
742
+ sandbox.stub(Logger)
743
+ Logger.info = sandbox.stub()
744
+ Logger.error = sandbox.stub()
745
+ participant.validateParticipant = sandbox.stub().throws(new Error('Validation failed'))
746
+ oracle.oracleRequest = sandbox.stub().resolves(null)
747
+ participant.sendErrorToParticipant = sandbox.stub()
748
+ const expectedCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
749
+ const headers = {
750
+ accept: 'application/vnd.interoperability.participants+json;version=1',
751
+ 'fspiop-destination': 'payerfsp',
752
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
753
+ date: '2019-05-24 08:52:19',
754
+ 'fspiop-source': Config.HUB_NAME
755
+ }
756
+ const params = {
757
+ ID: '123456',
758
+ Type: 'MSISDN',
759
+ SubId: 'SubId'
760
+ }
761
+ const payload = {
762
+ fspId: 'payerfsp',
763
+ currency: 'USD'
764
+ }
765
+ const dataUri = ''
766
+
767
+ // Act
768
+ await participantsDomain.putParticipantsErrorByTypeAndID(headers, params, payload, dataUri)
769
+
770
+ // Assert
771
+ expect(participant.sendErrorToParticipant.callCount).toBe(1)
772
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
773
+ expect(firstCallArgs[1]).toBe(expectedCallbackEndpointType)
774
+ expect(Logger.error.callCount).toBe(1)
775
+ })
776
+
777
+ it('handles PUT /error when `sendErrorToParticipant` throws error', async () => {
778
+ expect.hasAssertions()
779
+ // Arrange
780
+ sandbox.stub(Logger)
781
+ Logger.info = sandbox.stub()
782
+ Logger.error = sandbox.stub()
783
+ participant.validateParticipant = sandbox.stub().resolves({})
784
+ participant.sendErrorToParticipant = sandbox.stub().throws(new Error('sendErrorToParticipant failed'))
785
+ const expectedCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
786
+ const headers = {
787
+ accept: 'application/vnd.interoperability.participants+json;version=1',
788
+ 'fspiop-destination': 'payerfsp',
789
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
790
+ date: '2019-05-24 08:52:19',
791
+ 'fspiop-source': Config.HUB_NAME
792
+ }
793
+ const params = {
794
+ ID: '123456',
795
+ Type: 'MSISDN'
796
+ }
797
+ const payload = {
798
+ fspId: 'payerfsp',
799
+ currency: 'USD'
800
+ }
801
+ const dataUri = ''
802
+
803
+ // Act
804
+ await participantsDomain.putParticipantsErrorByTypeAndID(headers, params, payload, dataUri)
805
+
806
+ // Assert
807
+ expect(participant.sendErrorToParticipant.callCount).toBe(2)
808
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
809
+ const secondCallArgs = participant.sendErrorToParticipant.getCall(1).args
810
+ expect(firstCallArgs[1]).toBe(expectedCallbackEndpointType)
811
+ expect(secondCallArgs[0]).toBe(Config.HUB_NAME)
812
+ expect(Logger.error.callCount).toBe(2)
813
+ })
814
+
815
+ it('handles PUT /error when SubId is supplied and `sendErrorToParticipant` throws error', async () => {
816
+ expect.hasAssertions()
817
+ // Arrange
818
+ sandbox.stub(Logger)
819
+ Logger.info = sandbox.stub()
820
+ Logger.error = sandbox.stub()
821
+ participant.validateParticipant = sandbox.stub().resolves({})
822
+ participant.sendErrorToParticipant = sandbox.stub().throws(new Error('sendErrorToParticipant failed'))
823
+ const expectedCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
824
+ const headers = {
825
+ accept: 'application/vnd.interoperability.participants+json;version=1',
826
+ 'fspiop-destination': 'payerfsp',
827
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
828
+ date: '2019-05-24 08:52:19',
829
+ 'fspiop-source': Config.HUB_NAME
830
+ }
831
+ const params = {
832
+ ID: '123456',
833
+ Type: 'MSISDN',
834
+ SubId: 'SubId'
835
+ }
836
+ const payload = {
837
+ fspId: 'payerfsp',
838
+ currency: 'USD'
839
+ }
840
+ const dataUri = ''
841
+
842
+ // Act
843
+ await participantsDomain.putParticipantsErrorByTypeAndID(headers, params, payload, dataUri)
844
+
845
+ // Assert
846
+ expect(participant.sendErrorToParticipant.callCount).toBe(2)
847
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
848
+ const secondCallArgs = participant.sendErrorToParticipant.getCall(1).args
849
+ expect(firstCallArgs[1]).toBe(expectedCallbackEndpointType)
850
+ expect(secondCallArgs[0]).toBe(Config.HUB_NAME)
851
+ expect(Logger.error.callCount).toBe(2)
852
+ })
853
+ })
854
+
855
+ describe('postParticipants', () => {
856
+ let sandbox
857
+
858
+ beforeEach(() => {
859
+ sandbox = Sinon.createSandbox()
860
+ })
861
+
862
+ afterEach(() => {
863
+ sandbox.restore()
864
+ })
865
+
866
+ it('sends the request to the participant', async () => {
867
+ expect.hasAssertions()
868
+ // Arrange
869
+ participant.validateParticipant = sandbox.stub().resolves({})
870
+ oracle.oracleRequest = sandbox.stub().resolves({
871
+ data: {
872
+ partyList: [
873
+ { fspId: 'fsp1' }
874
+ ]
875
+ },
876
+ status: 201
877
+ })
878
+ participant.sendRequest = sandbox.stub()
879
+ const headers = {
880
+ accept: 'application/vnd.interoperability.participants+json;version=1',
881
+ 'fspiop-destination': Config.HUB_NAME,
882
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
883
+ date: '2019-05-24 08:52:19',
884
+ 'fspiop-source': 'fsp1'
885
+ }
886
+ const params = {
887
+ ID: '123456',
888
+ Type: 'MSISDN'
889
+ }
890
+ const payload = {
891
+ fspId: 'fsp1',
892
+ currency: 'USD'
893
+ }
894
+
895
+ // Act
896
+ await participantsDomain.postParticipants(headers, 'get', params, payload, Helper.mockSpan())
897
+
898
+ // Assert
899
+ expect(participant.sendRequest.callCount).toBe(1)
900
+ const firstCallArgs = participant.sendRequest.getCall(0).args
901
+ expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe(Config.HUB_NAME)
902
+ })
903
+
904
+ it('sends the request to the participant with SubId', async () => {
905
+ expect.hasAssertions()
906
+ // Arrange
907
+ participant.validateParticipant = sandbox.stub().resolves({})
908
+ oracle.oracleRequest = sandbox.stub().resolves({
909
+ data: {
910
+ partyList: [
911
+ { fspId: 'fsp1' }
912
+ ]
913
+ },
914
+ status: 201
915
+ })
916
+ participant.sendRequest = sandbox.stub()
917
+ const expectedCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT
918
+ const headers = {
919
+ accept: 'application/vnd.interoperability.participants+json;version=1',
920
+ 'fspiop-destination': Config.HUB_NAME,
921
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
922
+ date: '2019-05-24 08:52:19',
923
+ 'fspiop-source': 'fsp1'
924
+ }
925
+ const params = {
926
+ ID: '123456',
927
+ Type: 'MSISDN',
928
+ SubId: 'subId'
929
+ }
930
+ const payload = {
931
+ fspId: 'fsp1',
932
+ currency: 'USD'
933
+ }
934
+
935
+ // Act
936
+ await participantsDomain.postParticipants(headers, 'get', params, payload)
937
+
938
+ // Assert
939
+ expect(participant.sendRequest.callCount).toBe(1)
940
+ const firstCallArgs = participant.sendRequest.getCall(0).args
941
+ expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe(Config.HUB_NAME)
942
+ expect(firstCallArgs[2]).toBe(expectedCallbackEndpointType)
943
+ expect(firstCallArgs[4].partyList[0].partySubIdOrType).toBe('subId')
944
+ })
945
+
946
+ it('handles the request without fspiop-dest header', async () => {
947
+ expect.hasAssertions()
948
+ // Arrange
949
+ participant.validateParticipant = sandbox.stub().resolves({})
950
+ oracle.oracleRequest = sandbox.stub().resolves({
951
+ data: {
952
+ partyList: [
953
+ { fspId: 'fsp2' }
954
+ ]
955
+ },
956
+ status: 201
957
+ })
958
+ participant.sendRequest = sandbox.stub()
959
+ const headers = {
960
+ accept: 'application/vnd.interoperability.participants+json;version=1',
961
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
962
+ date: '2019-05-24 08:52:19',
963
+ 'fspiop-source': 'fsp1'
964
+ }
965
+ const params = {
966
+ ID: '123456',
967
+ Type: 'MSISDN'
968
+ }
969
+ const payload = {
970
+ fspId: 'fsp2',
971
+ currency: 'USD'
972
+ }
973
+
974
+ // Act
975
+ await participantsDomain.postParticipants(headers, 'get', params, payload)
976
+
977
+ // Assert
978
+ expect(participant.sendRequest.callCount).toBe(1)
979
+ const firstCallArgs = participant.sendRequest.getCall(0).args
980
+ expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe('fsp2')
981
+ })
982
+
983
+ it('handles the case where `oracleRequest` returns has no response.data', async () => {
984
+ expect.hasAssertions()
985
+ // Arrange
986
+ participant.validateParticipant = sandbox.stub().resolves({})
987
+ oracle.oracleRequest = sandbox.stub().resolves({})
988
+ participant.sendErrorToParticipant = sandbox.stub()
989
+ const headers = {
990
+ accept: 'application/vnd.interoperability.participants+json;version=1',
991
+ 'fspiop-destination': Config.HUB_NAME,
992
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
993
+ date: '2019-05-24 08:52:19',
994
+ 'fspiop-source': 'fsp1'
995
+ }
996
+ const params = {
997
+ ID: '123456',
998
+ Type: 'MSISDN'
999
+ }
1000
+ const payload = {
1001
+ fspId: 'fsp1',
1002
+ currency: 'USD'
1003
+ }
1004
+
1005
+ // Act
1006
+ await participantsDomain.postParticipants(headers, 'get', params, payload, Helper.mockSpan())
1007
+
1008
+ // Assert
1009
+ expect(participant.sendErrorToParticipant.callCount).toBe(1)
1010
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
1011
+ expect(firstCallArgs[0]).toBe('fsp1')
1012
+ })
1013
+
1014
+ it('handles the case where SubId is supplied but `oracleRequest` returns has no response.data', async () => {
1015
+ expect.hasAssertions()
1016
+ // Arrange
1017
+ participant.validateParticipant = sandbox.stub().resolves({})
1018
+ oracle.oracleRequest = sandbox.stub().resolves({})
1019
+ participant.sendErrorToParticipant = sandbox.stub()
1020
+ const expectedErrorCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
1021
+ const headers = {
1022
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1023
+ 'fspiop-destination': Config.HUB_NAME,
1024
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1025
+ date: '2019-05-24 08:52:19',
1026
+ 'fspiop-source': 'fsp1'
1027
+ }
1028
+ const params = {
1029
+ ID: '123456',
1030
+ Type: 'MSISDN',
1031
+ SubId: 'subId'
1032
+ }
1033
+ const payload = {
1034
+ fspId: 'fsp1',
1035
+ currency: 'USD'
1036
+ }
1037
+
1038
+ // Act
1039
+ await participantsDomain.postParticipants(headers, 'get', params, payload)
1040
+
1041
+ // Assert
1042
+ expect(participant.sendErrorToParticipant.callCount).toBe(1)
1043
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
1044
+ expect(firstCallArgs[0]).toBe('fsp1')
1045
+ expect(firstCallArgs[1]).toBe(expectedErrorCallbackEndpointType)
1046
+ })
1047
+
1048
+ it('handles the case where `validateParticipant` returns null', async () => {
1049
+ expect.hasAssertions()
1050
+ // Arrange
1051
+ participant.validateParticipant = sandbox.stub().resolves(null)
1052
+ sandbox.stub(Logger)
1053
+ Logger.error = sandbox.stub()
1054
+ participant.sendErrorToParticipant = sandbox.stub()
1055
+ const headers = {
1056
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1057
+ 'fspiop-destination': Config.HUB_NAME,
1058
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1059
+ date: '2019-05-24 08:52:19',
1060
+ 'fspiop-source': 'fsp1'
1061
+ }
1062
+ const params = {
1063
+ ID: '123456',
1064
+ Type: 'MSISDN'
1065
+ }
1066
+ const payload = {
1067
+ fspId: 'fsp1',
1068
+ currency: 'USD'
1069
+ }
1070
+
1071
+ // Act
1072
+ await participantsDomain.postParticipants(headers, 'get', params, payload)
1073
+
1074
+ // Assert
1075
+ const loggerFirstCallArgs = Logger.error.getCall(0).args
1076
+ expect(loggerFirstCallArgs[0]).toBe('Requester FSP not found')
1077
+ })
1078
+
1079
+ it('handles case where type is not in `PartyAccountTypes`', async () => {
1080
+ expect.hasAssertions()
1081
+ // Arrange
1082
+ sandbox.stub(Logger)
1083
+ Logger.error = sandbox.stub()
1084
+ participant.sendErrorToParticipant = sandbox.stub()
1085
+
1086
+ const headers = {
1087
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1088
+ 'fspiop-destination': Config.HUB_NAME,
1089
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1090
+ date: '2019-05-24 08:52:19',
1091
+ 'fspiop-source': 'fsp1'
1092
+ }
1093
+ const params = {
1094
+ ID: '123456',
1095
+ Type: 'UNKNOWN_TYPE'
1096
+ }
1097
+ const payload = {
1098
+ fspId: 'fsp1',
1099
+ currency: 'USD'
1100
+ }
1101
+
1102
+ // Act
1103
+ await participantsDomain.postParticipants(headers, 'get', params, payload)
1104
+
1105
+ // Assert
1106
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
1107
+ expect(firstCallArgs[0]).toBe('fsp1')
1108
+ })
1109
+
1110
+ it('handles case where type is not in `PartyAccountTypes` and `sendErrorToParticipant` fails', async () => {
1111
+ expect.hasAssertions()
1112
+ // Arrange
1113
+ sandbox.stub(Logger)
1114
+ Logger.error = sandbox.stub()
1115
+ participant.sendErrorToParticipant = sandbox.stub().throws(new Error('Error sending error to participant'))
1116
+
1117
+ const headers = {
1118
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1119
+ 'fspiop-destination': Config.HUB_NAME,
1120
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1121
+ date: '2019-05-24 08:52:19',
1122
+ 'fspiop-source': 'fsp1'
1123
+ }
1124
+ const params = {
1125
+ ID: '123456',
1126
+ Type: 'UNKNOWN_TYPE'
1127
+ }
1128
+ const payload = {
1129
+ fspId: 'fsp1',
1130
+ currency: 'USD'
1131
+ }
1132
+
1133
+ // Act
1134
+ await participantsDomain.postParticipants(headers, 'get', params, payload)
1135
+
1136
+ // Assert
1137
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
1138
+ expect(firstCallArgs[0]).toBe('fsp1')
1139
+ })
1140
+
1141
+ it('handles case where SubId is supplied but validation fails and an error is thrown while sending error callback', async () => {
1142
+ expect.hasAssertions()
1143
+ // Arrange
1144
+ sandbox.stub(Logger)
1145
+ Logger.error = sandbox.stub()
1146
+ participant.sendErrorToParticipant = sandbox.stub().throws(new Error('Error sending error to participant'))
1147
+ const expectedErrorCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
1148
+ const headers = {
1149
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1150
+ 'fspiop-destination': Config.HUB_NAME,
1151
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1152
+ date: '2019-05-24 08:52:19',
1153
+ 'fspiop-source': 'fsp1'
1154
+ }
1155
+ const params = {
1156
+ ID: '123456',
1157
+ Type: 'UNKNOWN_TYPE',
1158
+ SubId: 'subId'
1159
+ }
1160
+ const payload = {
1161
+ fspId: 'fsp1',
1162
+ currency: 'USD'
1163
+ }
1164
+
1165
+ // Act
1166
+ await participantsDomain.postParticipants(headers, 'get', params, payload)
1167
+
1168
+ // Assert
1169
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
1170
+ expect(firstCallArgs[0]).toBe('fsp1')
1171
+ expect(firstCallArgs[1]).toBe(expectedErrorCallbackEndpointType)
1172
+ })
1173
+ })
1174
+
1175
+ describe('postParticipantsBatch', () => {
1176
+ let sandbox
1177
+
1178
+ beforeEach(() => {
1179
+ sandbox = Sinon.createSandbox()
1180
+ })
1181
+
1182
+ afterEach(() => {
1183
+ sandbox.restore()
1184
+ })
1185
+
1186
+ it('sends a batch request to all oracles, with the given partyList', async () => {
1187
+ expect.hasAssertions()
1188
+ // Arrange
1189
+ sandbox.stub(Logger)
1190
+ Logger.error = sandbox.stub()
1191
+ participant.validateParticipant = sandbox.stub().resolves({})
1192
+ oracle.oracleBatchRequest = sandbox.stub().resolves({
1193
+ data: {
1194
+ partyList: [
1195
+ { partyId: { currency: 'USD' } },
1196
+ { partyId: { currency: 'USD' } },
1197
+ { partyId: { currency: 'USD' } }
1198
+ ]
1199
+ }
1200
+ })
1201
+ participant.sendRequest = sandbox.stub()
1202
+
1203
+ const headers = {
1204
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1205
+ 'fspiop-destination': Config.HUB_NAME,
1206
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1207
+ date: '2019-05-24 08:52:19',
1208
+ 'fspiop-source': 'fsp1'
1209
+ }
1210
+ const payload = {
1211
+ requestId: '1234-5678',
1212
+ currency: 'USD',
1213
+ partyList: [
1214
+ { fspId: 'fsp1', partyIdType: Enums.Accounts.PartyAccountTypes.MSISDN },
1215
+ { fspId: 'fsp1', partyIdType: Enums.Accounts.PartyAccountTypes.MSISDN },
1216
+ { fspId: 'fsp1', partyIdType: Enums.Accounts.PartyAccountTypes.MSISDN }
1217
+ ]
1218
+ }
1219
+ const expected = {
1220
+ currency: 'USD',
1221
+ partyList: [
1222
+ { partyId: { currency: undefined } },
1223
+ { partyId: { currency: undefined } },
1224
+ { partyId: { currency: undefined } }
1225
+ ]
1226
+ }
1227
+
1228
+ // Act
1229
+ await participantsDomain.postParticipantsBatch(headers, 'get', payload, Helper.mockSpan())
1230
+
1231
+ // Assert
1232
+ expect(participant.sendRequest.callCount).toBe(1)
1233
+ const firstCallArgs = participant.sendRequest.getCall(0).args
1234
+ expect(firstCallArgs[4]).toEqual(expected)
1235
+ })
1236
+
1237
+ it('sends a batch request to all oracles, when fspiop-dest is missing', async () => {
1238
+ expect.hasAssertions()
1239
+ // Arrange
1240
+ sandbox.stub(Logger)
1241
+ Logger.error = sandbox.stub()
1242
+ participant.validateParticipant = sandbox.stub().resolves({})
1243
+ oracle.oracleBatchRequest = sandbox.stub().resolves({
1244
+ data: {
1245
+ partyList: [
1246
+ { partyId: { currency: 'USD' } },
1247
+ { partyId: { currency: 'USD' } },
1248
+ { partyId: { currency: 'USD' } }
1249
+ ]
1250
+ }
1251
+ })
1252
+ participant.sendRequest = sandbox.stub()
1253
+
1254
+ const headers = {
1255
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1256
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1257
+ date: '2019-05-24 08:52:19',
1258
+ 'fspiop-source': 'fsp1'
1259
+ }
1260
+ const payload = {
1261
+ requestId: '1234-5678',
1262
+ currency: 'USD',
1263
+ partyList: [
1264
+ { fspId: 'fsp1', partyIdType: Enums.Accounts.PartyAccountTypes.MSISDN },
1265
+ { fspId: 'fsp1', partyIdType: Enums.Accounts.PartyAccountTypes.MSISDN },
1266
+ { fspId: 'fsp1', partyIdType: Enums.Accounts.PartyAccountTypes.MSISDN }
1267
+ ]
1268
+ }
1269
+ const expected = {
1270
+ currency: 'USD',
1271
+ partyList: [
1272
+ { partyId: { currency: undefined } },
1273
+ { partyId: { currency: undefined } },
1274
+ { partyId: { currency: undefined } }
1275
+ ]
1276
+ }
1277
+
1278
+ // Act
1279
+ await participantsDomain.postParticipantsBatch(headers, 'get', payload)
1280
+
1281
+ // Assert
1282
+ expect(participant.sendRequest.callCount).toBe(1)
1283
+ const firstCallArgs = participant.sendRequest.getCall(0).args
1284
+ expect(firstCallArgs[4]).toEqual(expected)
1285
+ })
1286
+
1287
+ it('sends errors when party.fspId does not match the source and partyIdType is invalid', async () => {
1288
+ expect.hasAssertions()
1289
+ // Arrange
1290
+ sandbox.stub(Logger)
1291
+ Logger.error = sandbox.stub()
1292
+ participant.validateParticipant = sandbox.stub().resolves({})
1293
+ oracle.oracleBatchRequest = sandbox.stub().resolves({
1294
+ data: {
1295
+ partyList: [
1296
+ { partyId: { currency: 'USD' } }
1297
+ ]
1298
+ }
1299
+ })
1300
+ participant.sendRequest = sandbox.stub()
1301
+
1302
+ const headers = {
1303
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1304
+ 'fspiop-destination': Config.HUB_NAME,
1305
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1306
+ date: '2019-05-24 08:52:19',
1307
+ 'fspiop-source': 'fsp1'
1308
+ }
1309
+ const payload = {
1310
+ requestId: '1234-5678',
1311
+ currency: 'USD',
1312
+ partyList: [
1313
+ { fspId: 'fsp1', partyIdType: Enums.Accounts.PartyAccountTypes.MSISDN },
1314
+ { fspId: 'fsp_not_valid', partyIdType: Enums.Accounts.PartyAccountTypes.MSISDN },
1315
+ { fspId: 'fsp_not_valid', partyIdType: 'NOT_A_VALID_PARTY_ID' }
1316
+ ]
1317
+ }
1318
+ const expected = {
1319
+ currency: 'USD',
1320
+ partyList: [
1321
+ ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND, undefined, undefined, undefined, [{
1322
+ key: Enums.Accounts.PartyAccountTypes.MSISDN,
1323
+ value: undefined
1324
+ }]).toApiErrorObject(Config.ERROR_HANDLING),
1325
+ ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR, undefined, undefined, undefined, [{
1326
+ key: 'NOT_A_VALID_PARTY_ID',
1327
+ value: undefined
1328
+ }]).toApiErrorObject(Config.ERROR_HANDLING),
1329
+ { partyId: { currency: undefined } }
1330
+ ]
1331
+ }
1332
+
1333
+ // Act
1334
+ await participantsDomain.postParticipantsBatch(headers, 'get', payload, Helper.mockSpan())
1335
+
1336
+ // Assert
1337
+ expect(participant.sendRequest.callCount).toBe(1)
1338
+ const firstCallArgs = participant.sendRequest.getCall(0).args
1339
+ expect(firstCallArgs[4]).toEqual(expected)
1340
+ })
1341
+
1342
+ it('handles error when `validateParticipant` fails and `sendErrorToParticipant` throws', async () => {
1343
+ expect.hasAssertions()
1344
+ // Arrange
1345
+ sandbox.stub(Logger)
1346
+ Logger.error = sandbox.stub()
1347
+ participant.validateParticipant = sandbox.stub().resolves(null)
1348
+ participant.sendErrorToParticipant = sandbox.stub().throws(new Error('unknown error'))
1349
+
1350
+ const headers = {
1351
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1352
+ 'fspiop-destination': Config.HUB_NAME,
1353
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1354
+ date: '2019-05-24 08:52:19',
1355
+ 'fspiop-source': 'fsp1'
1356
+ }
1357
+ const payload = {
1358
+ requestId: '1234-5678',
1359
+ currency: 'USD',
1360
+ partyList: [
1361
+ { fspId: 'fsp1', partyIdType: Enums.Accounts.PartyAccountTypes.MSISDN },
1362
+ { fspId: 'fsp_not_valid', partyIdType: Enums.Accounts.PartyAccountTypes.MSISDN },
1363
+ { fspId: 'fsp_not_valid', partyIdType: 'NOT_A_VALID_PARTY_ID' }
1364
+ ]
1365
+ }
1366
+
1367
+ // Act
1368
+ await participantsDomain.postParticipantsBatch(headers, 'get', payload, Helper.mockSpan())
1369
+
1370
+ // Assert
1371
+ const firstCallArgs = Logger.error.getCall(0).args
1372
+ const thirdCallArgs = Logger.error.getCall(2).args
1373
+ expect(firstCallArgs[0]).toBe('Requester FSP not found')
1374
+ expect(thirdCallArgs[0].message).toBe('unknown error')
1375
+ })
1376
+
1377
+ it('handles error when `oracleBatchRequest` returns no result', async () => {
1378
+ expect.hasAssertions()
1379
+ // Arrange
1380
+ participant.validateParticipant = sandbox.stub().resolves({})
1381
+ participant.sendRequest = sandbox.stub().resolves({})
1382
+ oracle.oracleBatchRequest = sandbox.stub().resolves(null)
1383
+
1384
+ const headers = {
1385
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1386
+ 'fspiop-destination': Config.HUB_NAME,
1387
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1388
+ date: '2019-05-24 08:52:19',
1389
+ 'fspiop-source': 'fsp1'
1390
+ }
1391
+ const payload = {
1392
+ requestId: '1234-5678',
1393
+ currency: 'USD',
1394
+ partyList: [
1395
+ { fspId: 'fsp1', partyIdType: Enums.Accounts.PartyAccountTypes.MSISDN },
1396
+ { fspId: 'fsp1', partyIdType: Enums.Accounts.PartyAccountTypes.MSISDN },
1397
+ { fspId: 'fsp1', partyIdType: Enums.Accounts.PartyAccountTypes.MSISDN }
1398
+ ]
1399
+ }
1400
+
1401
+ // Act
1402
+ await participantsDomain.postParticipantsBatch(headers, 'get', payload)
1403
+
1404
+ // Assert
1405
+ expect(participant.sendRequest.callCount).toBe(1)
1406
+ const firstCallArgs = participant.sendRequest.getCall(0).args
1407
+ expect(firstCallArgs[4].partyList[0].errorInformation).toBeDefined()
1408
+ expect(firstCallArgs[4].partyList[1].errorInformation).toBeDefined()
1409
+ expect(firstCallArgs[4].partyList[2].errorInformation).toBeDefined()
1410
+ })
1411
+
1412
+ it('handles error when `oracleBatchRequest` returns result but no partyList', async () => {
1413
+ expect.hasAssertions()
1414
+ // Arrange
1415
+ participant.validateParticipant = sandbox.stub().resolves({})
1416
+ participant.sendRequest = sandbox.stub().resolves({})
1417
+ oracle.oracleBatchRequest = sandbox.stub().resolves({
1418
+ data: {
1419
+ partyList: []
1420
+ }
1421
+ })
1422
+
1423
+ const headers = {
1424
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1425
+ 'fspiop-destination': Config.HUB_NAME,
1426
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1427
+ date: '2019-05-24 08:52:19',
1428
+ 'fspiop-source': 'fsp1'
1429
+ }
1430
+ const payload = {
1431
+ requestId: '1234-5678',
1432
+ currency: 'USD',
1433
+ partyList: [
1434
+ { fspId: 'fsp1', partyIdType: Enums.Accounts.PartyAccountTypes.MSISDN },
1435
+ { fspId: 'fsp1', partyIdType: Enums.Accounts.PartyAccountTypes.MSISDN },
1436
+ { fspId: 'fsp1', partyIdType: Enums.Accounts.PartyAccountTypes.MSISDN }
1437
+ ]
1438
+ }
1439
+
1440
+ // Act
1441
+ await participantsDomain.postParticipantsBatch(headers, 'get', payload)
1442
+
1443
+ // Assert
1444
+ expect(participant.sendRequest.callCount).toBe(1)
1445
+ const firstCallArgs = participant.sendRequest.getCall(0).args
1446
+ expect(firstCallArgs[4].partyList[0].errorInformation).toBeDefined()
1447
+ expect(firstCallArgs[4].partyList[1].errorInformation).toBeDefined()
1448
+ expect(firstCallArgs[4].partyList[2].errorInformation).toBeDefined()
1449
+ })
1450
+ })
1451
+
1452
+ describe('deleteParticipants', () => {
1453
+ let sandbox
1454
+
1455
+ beforeEach(() => {
1456
+ sandbox = Sinon.createSandbox()
1457
+ })
1458
+
1459
+ afterEach(() => {
1460
+ sandbox.restore()
1461
+ })
1462
+
1463
+ it('sends DELETE request to the participant', async () => {
1464
+ expect.hasAssertions()
1465
+ // Arrange
1466
+ participant.validateParticipant = sandbox.stub().resolves({})
1467
+ oracle.oracleRequest = sandbox.stub().resolves({
1468
+ data: {
1469
+ partyList: [
1470
+ { fspId: 'fsp1' }
1471
+ ]
1472
+ }
1473
+ })
1474
+ participant.sendRequest = sandbox.stub()
1475
+ const headers = {
1476
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1477
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1478
+ date: '2019-05-24 08:52:19',
1479
+ 'fspiop-source': 'fsp1'
1480
+ }
1481
+ const params = {
1482
+ ID: '123456',
1483
+ Type: 'MSISDN'
1484
+ }
1485
+ const method = 'delete'
1486
+ const query = {
1487
+ currency: 'USD'
1488
+ }
1489
+
1490
+ // Act
1491
+ await participantsDomain.deleteParticipants(headers, params, method, query)
1492
+
1493
+ // Assert
1494
+ expect(participant.sendRequest.callCount).toBe(1)
1495
+ const firstCallArgs = participant.sendRequest.getCall(0).args
1496
+ expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe('fsp1')
1497
+ })
1498
+
1499
+ it('sends DELETE request to the participant with SubId', async () => {
1500
+ expect.hasAssertions()
1501
+ // Arrange
1502
+ participant.validateParticipant = sandbox.stub().resolves({})
1503
+ oracle.oracleRequest = sandbox.stub().resolves({
1504
+ data: {
1505
+ partyList: [
1506
+ { fspId: 'fsp1' }
1507
+ ]
1508
+ }
1509
+ })
1510
+ participant.sendRequest = sandbox.stub()
1511
+ const expectedCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT
1512
+ const headers = {
1513
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1514
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1515
+ date: '2019-05-24 08:52:19',
1516
+ 'fspiop-source': 'fsp1'
1517
+ }
1518
+ const params = {
1519
+ ID: '123456',
1520
+ Type: 'MSISDN',
1521
+ SubId: 'subId'
1522
+ }
1523
+ const method = 'delete'
1524
+ const query = {
1525
+ currency: 'USD'
1526
+ }
1527
+
1528
+ // Act
1529
+ await participantsDomain.deleteParticipants(headers, params, method, query)
1530
+
1531
+ // Assert
1532
+ expect(participant.sendRequest.callCount).toBe(1)
1533
+ const firstCallArgs = participant.sendRequest.getCall(0).args
1534
+ expect(firstCallArgs[0][Enums.Http.Headers.FSPIOP.DESTINATION]).toBe('fsp1')
1535
+ expect(firstCallArgs[2]).toBe(expectedCallbackEndpointType)
1536
+ expect(firstCallArgs[4].fspId).toBe('fsp1')
1537
+ })
1538
+
1539
+ it('handles the case where `oracleRequest` returns has no response', async () => {
1540
+ expect.hasAssertions()
1541
+ // Arrange
1542
+ participant.validateParticipant = sandbox.stub().resolves({})
1543
+ oracle.oracleRequest = sandbox.stub().resolves(null)
1544
+ participant.sendErrorToParticipant = sandbox.stub()
1545
+ const headers = {
1546
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1547
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1548
+ date: '2019-05-24 08:52:19',
1549
+ 'fspiop-source': 'fsp1'
1550
+ }
1551
+ const params = {
1552
+ ID: '123456',
1553
+ Type: 'MSISDN'
1554
+ }
1555
+ const method = 'delete'
1556
+ const query = {
1557
+ currency: 'USD'
1558
+ }
1559
+
1560
+ // Act
1561
+ await participantsDomain.deleteParticipants(headers, params, method, query)
1562
+
1563
+ // Assert
1564
+ expect(participant.sendErrorToParticipant.callCount).toBe(1)
1565
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
1566
+ expect(firstCallArgs[0]).toBe('fsp1')
1567
+ })
1568
+
1569
+ it('handles the case where SubId is supplied but `oracleRequest` returns has no response', async () => {
1570
+ expect.hasAssertions()
1571
+ // Arrange
1572
+ participant.validateParticipant = sandbox.stub().resolves({})
1573
+ oracle.oracleRequest = sandbox.stub().resolves(null)
1574
+ participant.sendErrorToParticipant = sandbox.stub()
1575
+ const expectedErrorCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
1576
+ const headers = {
1577
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1578
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1579
+ date: '2019-05-24 08:52:19',
1580
+ 'fspiop-source': 'fsp1'
1581
+ }
1582
+ const params = {
1583
+ ID: '123456',
1584
+ Type: 'MSISDN',
1585
+ SubId: 'subId'
1586
+ }
1587
+ const method = 'delete'
1588
+ const query = {
1589
+ currency: 'USD'
1590
+ }
1591
+
1592
+ // Act
1593
+ await participantsDomain.deleteParticipants(headers, params, method, query)
1594
+
1595
+ // Assert
1596
+ expect(participant.sendErrorToParticipant.callCount).toBe(1)
1597
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
1598
+ expect(firstCallArgs[0]).toBe('fsp1')
1599
+ expect(firstCallArgs[1]).toBe(expectedErrorCallbackEndpointType)
1600
+ })
1601
+
1602
+ it('handles the case where `validateParticipant` returns null', async () => {
1603
+ expect.hasAssertions()
1604
+ // Arrange
1605
+ participant.validateParticipant = sandbox.stub().resolves(null)
1606
+ sandbox.stub(Logger)
1607
+ Logger.error = sandbox.stub()
1608
+ participant.sendErrorToParticipant = sandbox.stub()
1609
+ const headers = {
1610
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1611
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1612
+ date: '2019-05-24 08:52:19',
1613
+ 'fspiop-source': 'fsp1'
1614
+ }
1615
+ const params = {
1616
+ ID: '123456',
1617
+ Type: 'MSISDN'
1618
+ }
1619
+ const method = 'delete'
1620
+ const query = {
1621
+ currency: 'USD'
1622
+ }
1623
+
1624
+ // Act
1625
+ await participantsDomain.deleteParticipants(headers, params, method, query)
1626
+
1627
+ // Assert
1628
+ const loggerFirstCallArgs = Logger.error.getCall(0).args
1629
+ expect(loggerFirstCallArgs[0]).toBe('Requester FSP not found')
1630
+ })
1631
+
1632
+ it('handles case where type is not in `PartyAccountTypes`', async () => {
1633
+ expect.hasAssertions()
1634
+ // Arrange
1635
+ sandbox.stub(Logger)
1636
+ Logger.error = sandbox.stub()
1637
+ participant.sendErrorToParticipant = sandbox.stub()
1638
+
1639
+ const headers = {
1640
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1641
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1642
+ date: '2019-05-24 08:52:19',
1643
+ 'fspiop-source': 'fsp1'
1644
+ }
1645
+ const params = {
1646
+ ID: '123456',
1647
+ Type: 'UNKNOWN_TYPE'
1648
+ }
1649
+ const method = 'delete'
1650
+ const query = {
1651
+ currency: 'USD'
1652
+ }
1653
+
1654
+ // Act
1655
+ await participantsDomain.deleteParticipants(headers, params, method, query)
1656
+
1657
+ // Assert
1658
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
1659
+ expect(firstCallArgs[0]).toBe('fsp1')
1660
+ })
1661
+
1662
+ it('handles case where type is not in `PartyAccountTypes` and `sendErrorToParticipant` fails', async () => {
1663
+ expect.hasAssertions()
1664
+ // Arrange
1665
+ sandbox.stub(Logger)
1666
+ Logger.error = sandbox.stub()
1667
+ participant.sendErrorToParticipant = sandbox.stub().throws(new Error('Error sending error to participant'))
1668
+
1669
+ const headers = {
1670
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1671
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1672
+ date: '2019-05-24 08:52:19',
1673
+ 'fspiop-source': 'fsp1'
1674
+ }
1675
+ const params = {
1676
+ ID: '123456',
1677
+ Type: 'UNKNOWN_TYPE'
1678
+ }
1679
+ const method = 'delete'
1680
+ const query = {
1681
+ currency: 'USD'
1682
+ }
1683
+
1684
+ // Act
1685
+ await participantsDomain.deleteParticipants(headers, params, method, query)
1686
+
1687
+ // Assert
1688
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
1689
+ expect(firstCallArgs[0]).toBe('fsp1')
1690
+ })
1691
+
1692
+ it('handles case where SubId is supplied but validation fails and an error is thrown while sending error callback', async () => {
1693
+ expect.hasAssertions()
1694
+ // Arrange
1695
+ sandbox.stub(Logger)
1696
+ Logger.error = sandbox.stub()
1697
+ participant.sendErrorToParticipant = sandbox.stub().throws(new Error('Error sending error to participant'))
1698
+ const expectedErrorCallbackEndpointType = Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
1699
+ const headers = {
1700
+ accept: 'application/vnd.interoperability.participants+json;version=1',
1701
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
1702
+ date: '2019-05-24 08:52:19',
1703
+ 'fspiop-source': 'fsp1'
1704
+ }
1705
+ const params = {
1706
+ ID: '123456',
1707
+ Type: 'UNKNOWN_TYPE',
1708
+ SubId: 'subId'
1709
+ }
1710
+ const method = 'delete'
1711
+ const query = {
1712
+ currency: 'USD'
1713
+ }
1714
+
1715
+ // Act
1716
+ await participantsDomain.deleteParticipants(headers, params, method, query)
1717
+
1718
+ // Assert
1719
+ const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
1720
+ expect(firstCallArgs[0]).toBe('fsp1')
1721
+ expect(firstCallArgs[1]).toBe(expectedErrorCallbackEndpointType)
1722
+ })
1723
+ })
1724
+ })