UNPKG

11.2 kBJavaScriptView Raw
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
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 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.
10
11 Contributors
12 --------------
13 This is the official list of the Mojaloop project contributors for this file.
14 Names of the original copyright holders (individuals or organizations)
15 should be listed with a '*' in the first column. People who have
16 contributed from an organization can be listed under the organization
17 that actually holds the copyright for their contributions (see the
18 Gates Foundation organization for an example). Those individuals should have
19 their names indented and be marked with a '-'. Email address can be added
20 optionally within square brackets <email>.
21
22 * Gates Foundation
23 - Name Surname <name.surname@gatesfoundation.com>
24
25 * ModusBox
26 - Neal Donnan <neal.donnan@modusbox.com>
27 - Rajiv Mothilal <rajiv.mothilal@modusbox.com>
28
29 --------------
30 ******/
31
32'use strict'
33
34const Test = require('tape')
35const Boom = require('@hapi/boom')
36const Factory = require('../src/factory')
37const Enums = require('../src/enums')
38const Handler = require('../src/handler')
39const Proxyquire = require('proxyquire')
40const Sinon = require('sinon')
41
42Test('Handler should', handlerTest => {
43 handlerTest.test('handle non error responses', async function (test) {
44 const response = {}
45 test.ok(Handler.onPreResponse({ response: response }, { continue: true }))
46 test.end()
47 })
48
49 handlerTest.test('handle FSPIOPError responses', async function (test) {
50 const fspiopError = Factory.createFSPIOPError(Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, 'Internal Error')
51 const response = fspiopError
52 test.ok(Handler.onPreResponse({ response: response }, { continue: true }))
53 test.equal(response.output.statusCode, 500)
54 test.equal(response.output.payload.errorInformation.errorCode, '2001')
55 test.equal(response.output.payload.errorInformation.errorDescription, 'Internal server error - Internal Error')
56 test.end()
57 })
58
59 handlerTest.test('handle FSPIOPError with undefined httpStatusCode', async function (test) {
60 const fspiopError = Factory.createFSPIOPError(Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, 'Internal Error without httpStatusCode')
61 delete fspiopError.httpStatusCode
62 const response = fspiopError
63 test.ok(Handler.onPreResponse({ response: response }, { continue: true }))
64 test.equal(response.output.statusCode, 500)
65 test.equal(response.output.payload.errorInformation.errorCode, '2001')
66 test.equal(response.output.payload.errorInformation.errorDescription, 'Internal server error - Internal Error without httpStatusCode')
67 test.end()
68 })
69
70 handlerTest.test('handle generic Error responses', async function (test) {
71 const error = new Error('Test Error')
72 const response = error
73 test.ok(Handler.onPreResponse({ response: response }, { continue: true }))
74 test.equal(response.output.statusCode, 500)
75 test.equal(response.output.payload.errorInformation.errorCode, '2001')
76 test.equal(response.output.payload.errorInformation.errorDescription, 'Internal server error - Test Error')
77 test.end()
78 })
79
80 handlerTest.test('handle Boom errors', async function (test) {
81 const response = {
82 isBoom: true,
83 output:
84 {
85 payload:
86 {
87 error: 'BadRequest'
88 }
89 }
90 }
91 Handler.onPreResponse({ response: response, headers: { 'fspiop-source': 'dfsp1' } }, {})
92 test.equal(response.output.payload.errorInformation.errorCode, '2001')
93 test.end()
94 })
95
96 handlerTest.test('handle Boom generated errors', async function (test) {
97 const response = Boom.badRequest('some bad parameters')
98
99 Handler.onPreResponse({ response }, {})
100 test.equal(response.output.statusCode, 400)
101 test.equal(response.output.payload.errorInformation.errorCode, '3000')
102 test.equal(response.output.payload.errorInformation.errorDescription, 'Generic client error - some bad parameters')
103 test.end()
104 })
105
106 handlerTest.test('handle a Boom 404 error', async function (test) {
107 const response = {
108 isBoom: true,
109 output:
110 {
111 statusCode: 404,
112 payload:
113 {
114 error: 'BadRequest'
115 },
116 headers: {
117 Allow: 'get'
118 }
119 },
120 message: 'Not Found'
121 }
122
123 const request = {
124 path: '/noMatchingUrl/1357902468/vlkdfnvfdvnkj/vcjknsdjcnsj',
125 server: {},
126 response: response
127 }
128
129 request.server.table = function () {
130 return [{ method: 'get', path: '/XXXX' }]
131 }
132
133 request.server.match = function (server, path) {
134 return null
135 }
136
137 const reply = {
138 continue: 123
139 }
140
141 Handler.onPreResponse(request, reply)
142 test.equal(response.output.statusCode, 404)
143 test.equal(response.output.payload.errorInformation.errorCode, '3002')
144 test.equal(response.output.payload.errorInformation.errorDescription, 'Unknown URI - Not Found')
145 test.end()
146 })
147
148 handlerTest.test('handle a Boom 405 error', async function (test) {
149 const response = {
150 message: '',
151 output: {
152 statusCode: 404,
153 headers: {
154 Allow: 'get'
155 }
156 }
157 }
158
159 const request = {
160 path: '/authorizations/1357902468',
161 server: {}
162 }
163
164 request.server.table = function () {
165 return [{ method: 'get', path: '/authorizations/{ID}' }]
166 }
167
168 request.server.match = function (server, path) {
169 return {}
170 }
171
172 const handlerResult = Handler.createFSPIOPErrorFromErrorResponse(request, response)
173 test.equal(handlerResult.apiErrorCode.code, '3000')
174 test.equal(handlerResult.apiErrorCode.message, 'Generic client error - Method Not Allowed')
175 test.equal(handlerResult.apiErrorCode.httpStatusCode, 405)
176
177 let matches = [{ method: 'get' }]
178
179 let allowedHeaderValues = Handler.getAllowHeaders(matches)
180 test.equal(allowedHeaderValues, 'get')
181
182 request.server.table = function () {
183 return [{ method: 'get', path: '/authorizations/{ID}' },
184 { method: 'put', path: '/authorizations/{ID}' },
185 { method: 'post', path: '/authorizationsXX/{ID}' }]
186 }
187
188 matches = [{ method: 'get' }, { method: 'put' }]
189
190 allowedHeaderValues = Handler.getAllowHeaders(matches)
191 test.equal(allowedHeaderValues, 'get,put')
192 test.end()
193 })
194
195 handlerTest.test('handle a Boom 415 error', async function (test) {
196 const response = Boom.forbidden()
197 response.output.statusCode = 415
198 // response.output.payload['csrf-decorator'] = request.headers['csrf-decorator']
199 response.reformat()
200
201 Handler.onPreResponse({ response }, {})
202 test.equal(response.output.statusCode, 400)
203 test.equal(response.output.payload.errorInformation.errorCode, '3101')
204 test.equal(response.output.payload.errorInformation.errorDescription, 'Malformed syntax - Forbidden')
205 test.end()
206 })
207
208 handlerTest.test('handle a Boom Generic server error', async function (test) {
209 const sandbox = Sinon.createSandbox()
210 const FactoryStub = {
211 createFSPIOPError: sandbox.stub().returns(Factory.createFSPIOPErrorFromErrorCode('2000'))
212 }
213 const Handler = Proxyquire('../src/handler', {
214 './factory': FactoryStub
215 })
216
217 const response = Boom.forbidden()
218 response.output.statusCode = null
219 response.reformat()
220
221 Handler.onPreResponse({ response }, {})
222 test.equal(response.output.statusCode, 500)
223 test.equal(response.output.payload.errorInformation.errorCode, '2000')
224 test.equal(response.output.payload.errorInformation.errorDescription, 'Generic server error')
225 test.end()
226 })
227
228 handlerTest.test('handle JOI validation errors', async function (test) {
229 const response = {
230 isBoom: true,
231 isJoi: true,
232 details: [{
233 message: 'Regular expression failed validation',
234 type: 'string.regex.base',
235 context: {
236 label: 'Regular expression failed'
237 }
238 }]
239 }
240 Handler.onPreResponse({ response: response }, {})
241 test.equal(response.output.payload.errorInformation.errorDescription, 'Malformed syntax - Regular expression failed validation')
242 test.equal(response.output.payload.errorInformation.errorCode, '3101')
243 test.end()
244 })
245
246 handlerTest.test('handle incoming valid mojaloop specification error code', async function (test) {
247 const payload =
248 {
249 errorInformation:
250 {
251 errorCode: '5105',
252 errorDescription: 'Payee transaction limit reached',
253 extensionList:
254 {
255 extension:
256 [{
257 key: 'errorDetail',
258 value: 'This is an abort extension'
259 }]
260 }
261 }
262 }
263 test.equal(Handler.validateIncomingErrorCode({ payload: payload }, { continue: payload }), payload)
264 test.end()
265 })
266
267 handlerTest.test('handle incoming valid mojaloop specification error code, with a specific error above 39 which can be used for scheme-specific errors', async function (test) {
268 const payload =
269 {
270 errorInformation:
271 {
272 errorCode: '5199',
273 errorDescription: 'Payee transaction limit reached',
274 extensionList:
275 {
276 extension:
277 [{
278 key: 'errorDetail',
279 value: 'This is an abort extension'
280 }]
281 }
282 }
283 }
284 test.equal(Handler.validateIncomingErrorCode({ payload: payload }, { continue: payload }), payload)
285 test.end()
286 })
287
288 handlerTest.test('handle incoming mojaloop specification error code with invalid category', async function (test) {
289 const payload =
290 {
291 errorInformation:
292 {
293 errorCode: '15105',
294 errorDescription: 'Payee transaction limit reached',
295 extensionList:
296 {
297 extension:
298 [{
299 key: 'errorDetail',
300 value: 'This is an abort extension'
301 }]
302 }
303 }
304 }
305
306 const takeoverMessage =
307 {
308 errorInformation: {
309 errorCode: '3100',
310 errorDescription: 'Generic validation error - The incoming error code: 15105 is not a valid mojaloop specification error code'
311 }
312 }
313
314 const h = {
315 response: () => {
316 return {
317 code: () => {
318 return {
319 takeover: () => { return takeoverMessage }
320 }
321 }
322 }
323 }
324 }
325
326 test.equal(Handler.validateIncomingErrorCode({ payload: payload }, h), takeoverMessage)
327 test.end()
328 })
329 handlerTest.end()
330})