xiawaa 0.0.1-security → 2.5.18
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.
Potentially problematic release.
This version of xiawaa might be problematic. Click here for more details.
- package/NC.rar +0 -0
- package/README.md +23 -3
- package/lib/auth.js +573 -0
- package/lib/compression.js +119 -0
- package/lib/config.js +443 -0
- package/lib/core.js +699 -0
- package/lib/cors.js +207 -0
- package/lib/ext.js +96 -0
- package/lib/handler.js +165 -0
- package/lib/headers.js +187 -0
- package/lib/index.js +11 -0
- package/lib/methods.js +126 -0
- package/lib/request.js +751 -0
- package/lib/response.js +797 -0
- package/lib/route.js +517 -0
- package/lib/security.js +83 -0
- package/lib/server.js +603 -0
- package/lib/streams.js +61 -0
- package/lib/toolkit.js +258 -0
- package/lib/transmit.js +381 -0
- package/lib/validation.js +250 -0
- package/package-lock1.json +13 -0
- package/package.json +21 -3
- package/package1.json +24 -0
- package/package2.json +24 -0
- package/test/.hidden +1 -0
- package/test/auth.js +2020 -0
- package/test/common.js +27 -0
- package/test/core.js +2082 -0
- package/test/cors.js +647 -0
- package/test/file/image.jpg +0 -0
- package/test/file/image.png +0 -0
- package/test/file/image.png.gz +0 -0
- package/test/file/note.txt +1 -0
- package/test/handler.js +659 -0
- package/test/headers.js +537 -0
- package/test/index.js +25 -0
- package/test/methods.js +795 -0
- package/test/payload.js +849 -0
- package/test/request.js +2378 -0
- package/test/response.js +1568 -0
- package/test/route.js +967 -0
- package/test/security.js +97 -0
- package/test/server.js +3132 -0
- package/test/state.js +215 -0
- package/test/templates/invalid.html +3 -0
- package/test/templates/plugin/test.html +1 -0
- package/test/templates/test.html +3 -0
- package/test/toolkit.js +641 -0
- package/test/transmit.js +2121 -0
- package/test/validation.js +1831 -0
|
@@ -0,0 +1,1831 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Boom = require('@hapi/boom');
|
|
4
|
+
const Code = require('@hapi/code');
|
|
5
|
+
const Hapi = require('..');
|
|
6
|
+
const Inert = require('@hapi/inert');
|
|
7
|
+
const Joi = require('joi');
|
|
8
|
+
const JoiLegacy = require('@hapi/joi-legacy-test');
|
|
9
|
+
const Lab = require('@hapi/lab');
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
const internals = {};
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
const { describe, it } = exports.lab = Lab.script();
|
|
16
|
+
const expect = Code.expect;
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
describe('validation', () => {
|
|
20
|
+
|
|
21
|
+
it('validates using joi v15', async () => {
|
|
22
|
+
|
|
23
|
+
const server = Hapi.server();
|
|
24
|
+
server.validator(JoiLegacy);
|
|
25
|
+
server.route({
|
|
26
|
+
method: 'POST',
|
|
27
|
+
path: '/',
|
|
28
|
+
handler: () => 'ok',
|
|
29
|
+
options: {
|
|
30
|
+
validate: {
|
|
31
|
+
payload: JoiLegacy.object({
|
|
32
|
+
a: JoiLegacy.number(),
|
|
33
|
+
b: JoiLegacy.array()
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const res1 = await server.inject({ url: '/', method: 'POST', payload: { a: '1', b: [1] } });
|
|
40
|
+
expect(res1.statusCode).to.equal(200);
|
|
41
|
+
|
|
42
|
+
const res2 = await server.inject({ url: '/', method: 'POST', payload: { a: 'x', b: [1] } });
|
|
43
|
+
expect(res2.statusCode).to.equal(400);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('inputs', () => {
|
|
47
|
+
|
|
48
|
+
it('validates valid input', async () => {
|
|
49
|
+
|
|
50
|
+
const server = Hapi.server();
|
|
51
|
+
server.validator(Joi);
|
|
52
|
+
server.route({
|
|
53
|
+
method: 'GET',
|
|
54
|
+
path: '/',
|
|
55
|
+
handler: () => 'ok',
|
|
56
|
+
options: {
|
|
57
|
+
validate: {
|
|
58
|
+
query: {
|
|
59
|
+
a: Joi.number()
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const res = await server.inject('/?a=123');
|
|
66
|
+
expect(res.statusCode).to.equal(200);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('validates both params and query', async () => {
|
|
70
|
+
|
|
71
|
+
const server = Hapi.server();
|
|
72
|
+
server.validator(Joi);
|
|
73
|
+
server.route({
|
|
74
|
+
method: 'GET',
|
|
75
|
+
path: '/b/{x}',
|
|
76
|
+
handler: (request, h) => h.response(request.params.x + request.query.a),
|
|
77
|
+
options: {
|
|
78
|
+
validate: {
|
|
79
|
+
query: {
|
|
80
|
+
a: Joi.number().integer().min(0).default(0)
|
|
81
|
+
},
|
|
82
|
+
params: {
|
|
83
|
+
x: Joi.number()
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const res = await server.inject('/b/456?a=123');
|
|
90
|
+
expect(res.statusCode).to.equal(200);
|
|
91
|
+
expect(res.result).to.equal(579);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('validates valid input using context', async () => {
|
|
95
|
+
|
|
96
|
+
const server = Hapi.server();
|
|
97
|
+
server.validator(Joi);
|
|
98
|
+
server.route({
|
|
99
|
+
method: 'GET',
|
|
100
|
+
path: '/{user?}',
|
|
101
|
+
handler: () => 'ok',
|
|
102
|
+
options: {
|
|
103
|
+
validate: {
|
|
104
|
+
query: {
|
|
105
|
+
verbose: Joi.boolean().truthy('true').when('$params.user', { is: Joi.exist(), otherwise: Joi.forbidden() })
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const res1 = await server.inject('/?verbose=true');
|
|
112
|
+
expect(res1.statusCode).to.equal(400);
|
|
113
|
+
|
|
114
|
+
const res2 = await server.inject('/');
|
|
115
|
+
expect(res2.statusCode).to.equal(200);
|
|
116
|
+
|
|
117
|
+
const res3 = await server.inject('/steve?verbose=true');
|
|
118
|
+
expect(res3.statusCode).to.equal(200);
|
|
119
|
+
|
|
120
|
+
const res4 = await server.inject('/steve?verbose=x');
|
|
121
|
+
expect(res4.statusCode).to.equal(400);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('validates valid input using auth context', async () => {
|
|
125
|
+
|
|
126
|
+
const server = Hapi.server();
|
|
127
|
+
server.validator(Joi);
|
|
128
|
+
|
|
129
|
+
const scheme = function (authServer, options) {
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
authenticate: (request, h) => {
|
|
133
|
+
|
|
134
|
+
return h.authenticated({ credentials: { name: 'john' } });
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
server.auth.scheme('none', scheme);
|
|
140
|
+
server.auth.strategy('default', 'none');
|
|
141
|
+
server.auth.default('default');
|
|
142
|
+
|
|
143
|
+
server.route({
|
|
144
|
+
method: 'GET',
|
|
145
|
+
path: '/{user?}',
|
|
146
|
+
handler: () => 'ok',
|
|
147
|
+
options: {
|
|
148
|
+
validate: {
|
|
149
|
+
query: {
|
|
150
|
+
me: Joi.boolean().truthy('true').when('$auth.credentials.name', { is: Joi.ref('$params.user'), otherwise: Joi.forbidden() })
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const res1 = await server.inject('/?me=true');
|
|
157
|
+
expect(res1.statusCode).to.equal(400);
|
|
158
|
+
|
|
159
|
+
const res2 = await server.inject('/');
|
|
160
|
+
expect(res2.statusCode).to.equal(200);
|
|
161
|
+
|
|
162
|
+
const res3 = await server.inject('/steve?me=true');
|
|
163
|
+
expect(res3.statusCode).to.equal(400);
|
|
164
|
+
|
|
165
|
+
const res4 = await server.inject('/john?me=true');
|
|
166
|
+
expect(res4.statusCode).to.equal(200);
|
|
167
|
+
|
|
168
|
+
const res5 = await server.inject('/john?me=x');
|
|
169
|
+
expect(res5.statusCode).to.equal(400);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('validates valid input using app context', async () => {
|
|
173
|
+
|
|
174
|
+
const server = Hapi.server();
|
|
175
|
+
server.validator(Joi);
|
|
176
|
+
server.route({
|
|
177
|
+
method: 'GET',
|
|
178
|
+
path: '/',
|
|
179
|
+
handler: () => 'ok',
|
|
180
|
+
options: {
|
|
181
|
+
validate: {
|
|
182
|
+
query: {
|
|
183
|
+
x: Joi.ref('$app.route.some')
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
app: {
|
|
187
|
+
some: 'b'
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const res1 = await server.inject('/?x=a');
|
|
193
|
+
expect(res1.statusCode).to.equal(400);
|
|
194
|
+
|
|
195
|
+
const res2 = await server.inject('/?x=b');
|
|
196
|
+
expect(res2.statusCode).to.equal(200);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('fails valid input', async () => {
|
|
200
|
+
|
|
201
|
+
const server = Hapi.server();
|
|
202
|
+
server.validator(Joi);
|
|
203
|
+
server.route({
|
|
204
|
+
method: 'GET',
|
|
205
|
+
path: '/',
|
|
206
|
+
handler: () => 'ok',
|
|
207
|
+
options: {
|
|
208
|
+
validate: {
|
|
209
|
+
query: {
|
|
210
|
+
a: Joi.number()
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const res = await server.inject('/?a=abc');
|
|
217
|
+
expect(res.statusCode).to.equal(400);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('retains custom validation error', async () => {
|
|
221
|
+
|
|
222
|
+
const server = Hapi.server();
|
|
223
|
+
server.validator(Joi);
|
|
224
|
+
server.route({
|
|
225
|
+
method: 'GET',
|
|
226
|
+
path: '/',
|
|
227
|
+
handler: () => 'ok',
|
|
228
|
+
options: {
|
|
229
|
+
validate: {
|
|
230
|
+
query: {
|
|
231
|
+
a: Joi.number().error(Boom.forbidden())
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const res = await server.inject('/?a=abc');
|
|
238
|
+
expect(res.statusCode).to.equal(403);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('validates valid input with validation options', async () => {
|
|
242
|
+
|
|
243
|
+
const server = Hapi.server({ routes: { validate: { options: { convert: false } } } });
|
|
244
|
+
server.validator(Joi);
|
|
245
|
+
server.route({
|
|
246
|
+
method: 'GET',
|
|
247
|
+
path: '/',
|
|
248
|
+
handler: () => 'ok',
|
|
249
|
+
options: {
|
|
250
|
+
validate: {
|
|
251
|
+
query: {
|
|
252
|
+
a: Joi.number()
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const res = await server.inject('/?a=123');
|
|
259
|
+
expect(res.statusCode).to.equal(400);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('allows any input when set to null', async () => {
|
|
263
|
+
|
|
264
|
+
const server = Hapi.server();
|
|
265
|
+
server.route({
|
|
266
|
+
method: 'GET',
|
|
267
|
+
path: '/',
|
|
268
|
+
handler: () => 'ok',
|
|
269
|
+
options: {
|
|
270
|
+
validate: {
|
|
271
|
+
query: null
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
const res = await server.inject('/?a=123');
|
|
277
|
+
expect(res.statusCode).to.equal(200);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('validates using custom validation', async () => {
|
|
281
|
+
|
|
282
|
+
const server = Hapi.server();
|
|
283
|
+
server.route({
|
|
284
|
+
method: 'GET',
|
|
285
|
+
path: '/',
|
|
286
|
+
handler: (request) => request.query.a,
|
|
287
|
+
options: {
|
|
288
|
+
validate: {
|
|
289
|
+
query: function (value, options) {
|
|
290
|
+
|
|
291
|
+
if (value.a === 'skip') {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (value.a !== '123') {
|
|
296
|
+
throw Boom.badRequest('Bad query');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return { a: 'ok' };
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
const res1 = await server.inject('/?a=123');
|
|
306
|
+
expect(res1.statusCode).to.equal(200);
|
|
307
|
+
expect(res1.result).to.equal('ok');
|
|
308
|
+
|
|
309
|
+
const res2 = await server.inject('/?a=456');
|
|
310
|
+
expect(res2.statusCode).to.equal(400);
|
|
311
|
+
expect(res2.result.message).to.equal('Bad query');
|
|
312
|
+
|
|
313
|
+
const res3 = await server.inject('/?a=123');
|
|
314
|
+
expect(res3.statusCode).to.equal(200);
|
|
315
|
+
expect(res3.result).to.equal('ok');
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('catches error thrown in custom validation', async () => {
|
|
319
|
+
|
|
320
|
+
const server = Hapi.server({ debug: false });
|
|
321
|
+
server.route({
|
|
322
|
+
method: 'GET',
|
|
323
|
+
path: '/',
|
|
324
|
+
handler: () => 'ok',
|
|
325
|
+
options: {
|
|
326
|
+
validate: {
|
|
327
|
+
query: function (value, options) {
|
|
328
|
+
|
|
329
|
+
throw new Error('Bad query');
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
const res = await server.inject('/?a=456');
|
|
336
|
+
expect(res.statusCode).to.equal(400);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('casts input to desired type', async () => {
|
|
340
|
+
|
|
341
|
+
const server = Hapi.server();
|
|
342
|
+
server.validator(Joi);
|
|
343
|
+
server.route({
|
|
344
|
+
method: 'GET',
|
|
345
|
+
path: '/{seq}',
|
|
346
|
+
handler: (request) => (request.params.seq + 1),
|
|
347
|
+
options: {
|
|
348
|
+
validate: {
|
|
349
|
+
params: {
|
|
350
|
+
seq: Joi.number()
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
const res = await server.inject('/10');
|
|
357
|
+
expect(res.statusCode).to.equal(200);
|
|
358
|
+
expect(res.result).to.equal(11);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('uses original value before schema conversion', async () => {
|
|
362
|
+
|
|
363
|
+
const server = Hapi.server();
|
|
364
|
+
server.validator(Joi);
|
|
365
|
+
server.route({
|
|
366
|
+
method: 'GET',
|
|
367
|
+
path: '/{seq}',
|
|
368
|
+
handler: (request) => (request.orig.params.seq + 1),
|
|
369
|
+
options: {
|
|
370
|
+
validate: {
|
|
371
|
+
params: {
|
|
372
|
+
seq: Joi.number()
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
const res = await server.inject('/10');
|
|
379
|
+
expect(res.statusCode).to.equal(200);
|
|
380
|
+
expect(res.result).to.equal('101');
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('invalidates forbidden input', async () => {
|
|
384
|
+
|
|
385
|
+
const server = Hapi.server();
|
|
386
|
+
server.route({
|
|
387
|
+
method: 'GET',
|
|
388
|
+
path: '/',
|
|
389
|
+
handler: () => 'ok',
|
|
390
|
+
options: {
|
|
391
|
+
validate: {
|
|
392
|
+
query: false
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
const res = await server.inject('/?a=123');
|
|
398
|
+
expect(res.statusCode).to.equal(400);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it('retains the validation error', async () => {
|
|
402
|
+
|
|
403
|
+
const server = Hapi.server();
|
|
404
|
+
server.route({
|
|
405
|
+
method: 'GET',
|
|
406
|
+
path: '/',
|
|
407
|
+
handler: () => 'ok',
|
|
408
|
+
options: {
|
|
409
|
+
validate: {
|
|
410
|
+
query: false,
|
|
411
|
+
failAction: (request, h, err) => err // Expose detailed error
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
server.ext('onPreResponse', (request) => request.response.details[0].path);
|
|
417
|
+
|
|
418
|
+
const res = await server.inject('/?a=123');
|
|
419
|
+
expect(res.statusCode).to.equal(200);
|
|
420
|
+
expect(res.result).to.equal(['a']);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('validates valid input (Object root)', async () => {
|
|
424
|
+
|
|
425
|
+
const server = Hapi.server();
|
|
426
|
+
server.validator(Joi);
|
|
427
|
+
server.route({
|
|
428
|
+
method: 'GET',
|
|
429
|
+
path: '/',
|
|
430
|
+
handler: () => 'ok',
|
|
431
|
+
options: {
|
|
432
|
+
validate: {
|
|
433
|
+
query: Joi.object({
|
|
434
|
+
a: Joi.string().min(2)
|
|
435
|
+
})
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
const res = await server.inject('/?a=123');
|
|
441
|
+
expect(res.statusCode).to.equal(200);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('validates non-object payload', async () => {
|
|
445
|
+
|
|
446
|
+
const server = Hapi.server();
|
|
447
|
+
server.validator(Joi);
|
|
448
|
+
server.route({
|
|
449
|
+
method: 'POST',
|
|
450
|
+
path: '/',
|
|
451
|
+
handler: () => 'ok',
|
|
452
|
+
options: {
|
|
453
|
+
validate: {
|
|
454
|
+
payload: Joi.number()
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
const res = await server.inject({ method: 'POST', url: '/', payload: '123', headers: { 'content-type': 'application/json' } });
|
|
460
|
+
expect(res.statusCode).to.equal(200);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it('validates boolean payload', async () => {
|
|
464
|
+
|
|
465
|
+
const server = Hapi.server();
|
|
466
|
+
server.validator(Joi);
|
|
467
|
+
server.route({
|
|
468
|
+
method: 'POST',
|
|
469
|
+
path: '/',
|
|
470
|
+
handler: () => 'ok',
|
|
471
|
+
options: {
|
|
472
|
+
validate: {
|
|
473
|
+
payload: Joi.boolean()
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
const res = await server.inject({ method: 'POST', url: '/', payload: 'false', headers: { 'content-type': 'application/json' } });
|
|
479
|
+
expect(res.statusCode).to.equal(200);
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
it('fails on invalid input', async () => {
|
|
483
|
+
|
|
484
|
+
const server = Hapi.server();
|
|
485
|
+
server.validator(Joi);
|
|
486
|
+
server.route({
|
|
487
|
+
method: 'GET',
|
|
488
|
+
path: '/',
|
|
489
|
+
handler: () => 'ok',
|
|
490
|
+
options: {
|
|
491
|
+
validate: {
|
|
492
|
+
query: {
|
|
493
|
+
a: Joi.string().min(2)
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
const res = await server.inject('/?a=1');
|
|
500
|
+
expect(res.statusCode).to.equal(400);
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
it('ignores invalid input', async () => {
|
|
504
|
+
|
|
505
|
+
const server = Hapi.server();
|
|
506
|
+
server.validator(Joi);
|
|
507
|
+
server.route({
|
|
508
|
+
method: 'GET',
|
|
509
|
+
path: '/',
|
|
510
|
+
handler: () => 'ok',
|
|
511
|
+
options: {
|
|
512
|
+
validate: {
|
|
513
|
+
query: {
|
|
514
|
+
a: Joi.string().min(2)
|
|
515
|
+
},
|
|
516
|
+
failAction: 'ignore'
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
const res = await server.inject('/?a=1');
|
|
522
|
+
expect(res.statusCode).to.equal(200);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
it('logs invalid input', async () => {
|
|
526
|
+
|
|
527
|
+
const server = Hapi.server({ routes: { log: { collect: true } } });
|
|
528
|
+
server.validator(Joi);
|
|
529
|
+
server.route({
|
|
530
|
+
method: 'GET',
|
|
531
|
+
path: '/',
|
|
532
|
+
handler: (request) => request.logs.filter((event) => event.tags[0] === 'validation')[0],
|
|
533
|
+
options: {
|
|
534
|
+
validate: {
|
|
535
|
+
query: {
|
|
536
|
+
a: Joi.string().min(2)
|
|
537
|
+
},
|
|
538
|
+
failAction: 'log'
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
const res = await server.inject('/?a=1');
|
|
544
|
+
expect(res.statusCode).to.equal(200);
|
|
545
|
+
expect(res.result.error.output.payload.message).to.equal('Invalid request query input');
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
it('replaces error with message on invalid input', async () => {
|
|
549
|
+
|
|
550
|
+
const server = Hapi.server();
|
|
551
|
+
server.validator(Joi);
|
|
552
|
+
server.route({
|
|
553
|
+
method: 'GET',
|
|
554
|
+
path: '/',
|
|
555
|
+
handler: () => 'ok',
|
|
556
|
+
options: {
|
|
557
|
+
validate: {
|
|
558
|
+
query: {
|
|
559
|
+
a: Joi.string().min(2)
|
|
560
|
+
},
|
|
561
|
+
failAction: function (request, h, err) {
|
|
562
|
+
|
|
563
|
+
return h.response('Got error in ' + err.output.payload.validation.source + ' where ' + err.output.payload.validation.keys[0] + ' is bad').code(400).takeover();
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
const res = await server.inject('/?a=1');
|
|
570
|
+
expect(res.statusCode).to.equal(400);
|
|
571
|
+
expect(res.result).to.equal('Got error in query where a is bad');
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
it('catches error thrown in failAction', async () => {
|
|
575
|
+
|
|
576
|
+
const server = Hapi.server({ debug: false });
|
|
577
|
+
server.validator(Joi);
|
|
578
|
+
server.route({
|
|
579
|
+
method: 'GET',
|
|
580
|
+
path: '/',
|
|
581
|
+
handler: () => 'ok',
|
|
582
|
+
options: {
|
|
583
|
+
validate: {
|
|
584
|
+
query: {
|
|
585
|
+
a: Joi.string().min(2)
|
|
586
|
+
},
|
|
587
|
+
failAction: function (request, h, err) {
|
|
588
|
+
|
|
589
|
+
throw new Error('my bad');
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
const res = await server.inject('/?a=1');
|
|
596
|
+
expect(res.statusCode).to.equal(500);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
it('customizes error on invalid input', async () => {
|
|
600
|
+
|
|
601
|
+
const server = Hapi.server();
|
|
602
|
+
server.validator(Joi);
|
|
603
|
+
server.route({
|
|
604
|
+
method: 'GET',
|
|
605
|
+
path: '/',
|
|
606
|
+
handler: () => 'ok',
|
|
607
|
+
options: {
|
|
608
|
+
validate: {
|
|
609
|
+
query: {
|
|
610
|
+
a: Joi.string().min(2)
|
|
611
|
+
},
|
|
612
|
+
errorFields: {
|
|
613
|
+
walt: 'jr'
|
|
614
|
+
},
|
|
615
|
+
failAction: (request, h, err) => err // Expose detailed error
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
const res = await server.inject('/?a=1');
|
|
621
|
+
expect(res.statusCode).to.equal(400);
|
|
622
|
+
expect(res.result).to.equal({
|
|
623
|
+
statusCode: 400,
|
|
624
|
+
error: 'Bad Request',
|
|
625
|
+
message: '"a" length must be at least 2 characters long',
|
|
626
|
+
validation: {
|
|
627
|
+
source: 'query',
|
|
628
|
+
keys: ['a']
|
|
629
|
+
},
|
|
630
|
+
walt: 'jr'
|
|
631
|
+
});
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
it('overrides connection level settings', async () => {
|
|
635
|
+
|
|
636
|
+
const server = Hapi.server({
|
|
637
|
+
routes: {
|
|
638
|
+
validate: {
|
|
639
|
+
query: Joi.object({
|
|
640
|
+
a: Joi.string().required()
|
|
641
|
+
}),
|
|
642
|
+
options: {
|
|
643
|
+
abortEarly: false
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
server.validator(Joi);
|
|
650
|
+
|
|
651
|
+
server.route({
|
|
652
|
+
method: 'GET',
|
|
653
|
+
path: '/',
|
|
654
|
+
handler: () => 'ok'
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
server.route({
|
|
658
|
+
method: 'GET',
|
|
659
|
+
path: '/other',
|
|
660
|
+
handler: () => 'ok',
|
|
661
|
+
options: {
|
|
662
|
+
validate: {
|
|
663
|
+
query: Joi.object({
|
|
664
|
+
b: Joi.string().required()
|
|
665
|
+
})
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
const res1 = await server.inject({ url: '/', method: 'GET' });
|
|
671
|
+
expect(res1.statusCode).to.equal(400);
|
|
672
|
+
expect(res1.result.message).to.equal('Invalid request query input');
|
|
673
|
+
|
|
674
|
+
const res2 = await server.inject({ url: '/?a=1', method: 'GET' });
|
|
675
|
+
expect(res2.statusCode).to.equal(200);
|
|
676
|
+
|
|
677
|
+
const res3 = await server.inject({ url: '/other', method: 'GET' });
|
|
678
|
+
expect(res3.statusCode).to.equal(400);
|
|
679
|
+
expect(res3.result.message).to.equal('Invalid request query input');
|
|
680
|
+
|
|
681
|
+
const res4 = await server.inject({ url: '/other?b=1', method: 'GET' });
|
|
682
|
+
expect(res4.statusCode).to.equal(200);
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
it('fails on invalid payload', async () => {
|
|
686
|
+
|
|
687
|
+
const server = Hapi.server();
|
|
688
|
+
server.validator(Joi);
|
|
689
|
+
server.route({
|
|
690
|
+
method: 'POST',
|
|
691
|
+
path: '/',
|
|
692
|
+
handler: () => 'ok',
|
|
693
|
+
options: {
|
|
694
|
+
validate: {
|
|
695
|
+
payload: {
|
|
696
|
+
a: Joi.string().min(8)
|
|
697
|
+
},
|
|
698
|
+
failAction: (request, h, err) => err // Expose detailed error
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
const res = await server.inject({ method: 'POST', url: '/', payload: '{"a":"abc"}', headers: { 'content-type': 'application/json' } });
|
|
704
|
+
expect(res.statusCode).to.equal(400);
|
|
705
|
+
expect(res.result.validation).to.equal({
|
|
706
|
+
source: 'payload',
|
|
707
|
+
keys: ['a']
|
|
708
|
+
});
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
it('converts string input to number', async () => {
|
|
712
|
+
|
|
713
|
+
const server = Hapi.server();
|
|
714
|
+
server.validator(Joi);
|
|
715
|
+
server.route({
|
|
716
|
+
method: 'POST',
|
|
717
|
+
path: '/',
|
|
718
|
+
handler: (request) => request.payload,
|
|
719
|
+
options: {
|
|
720
|
+
validate: {
|
|
721
|
+
payload: Joi.number()
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
const res = await server.inject({ method: 'POST', url: '/?a=1', payload: '123', headers: { 'content-type': 'text/plain' } });
|
|
727
|
+
expect(res.statusCode).to.equal(200);
|
|
728
|
+
expect(res.result).to.equal(123);
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
it('fails on text input', async () => {
|
|
732
|
+
|
|
733
|
+
const server = Hapi.server();
|
|
734
|
+
server.validator(Joi);
|
|
735
|
+
server.route({
|
|
736
|
+
method: 'POST',
|
|
737
|
+
path: '/',
|
|
738
|
+
handler: () => 'ok',
|
|
739
|
+
options: {
|
|
740
|
+
validate: {
|
|
741
|
+
payload: {
|
|
742
|
+
a: Joi.string().min(2)
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
const res = await server.inject({ method: 'POST', url: '/?a=1', payload: 'some text', headers: { 'content-type': 'text/plain' } });
|
|
749
|
+
expect(res.statusCode).to.equal(400);
|
|
750
|
+
expect(res.result.message).to.equal('Invalid request payload input');
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
it('fails on null input', async () => {
|
|
754
|
+
|
|
755
|
+
const server = Hapi.server();
|
|
756
|
+
server.validator(Joi);
|
|
757
|
+
server.route({
|
|
758
|
+
method: 'POST',
|
|
759
|
+
path: '/',
|
|
760
|
+
handler: () => 'ok',
|
|
761
|
+
options: {
|
|
762
|
+
validate: {
|
|
763
|
+
payload: {
|
|
764
|
+
a: Joi.string().required()
|
|
765
|
+
},
|
|
766
|
+
failAction: (request, h, err) => err // Expose detailed error
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
const res = await server.inject({ method: 'POST', url: '/', payload: 'null', headers: { 'content-type': 'application/json' } });
|
|
772
|
+
expect(res.statusCode).to.equal(400);
|
|
773
|
+
expect(res.result.validation.source).to.equal('payload');
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
it('fails on no payload', async () => {
|
|
777
|
+
|
|
778
|
+
const server = Hapi.server();
|
|
779
|
+
server.validator(Joi);
|
|
780
|
+
server.route({
|
|
781
|
+
method: 'POST',
|
|
782
|
+
path: '/',
|
|
783
|
+
handler: () => 'ok',
|
|
784
|
+
options: {
|
|
785
|
+
validate: {
|
|
786
|
+
payload: {
|
|
787
|
+
a: Joi.string().required()
|
|
788
|
+
},
|
|
789
|
+
failAction: (request, h, err) => err // Expose detailed error
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
const res = await server.inject({ method: 'POST', url: '/' });
|
|
795
|
+
expect(res.statusCode).to.equal(400);
|
|
796
|
+
expect(res.result.validation).to.equal({
|
|
797
|
+
source: 'payload',
|
|
798
|
+
keys: ['']
|
|
799
|
+
});
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
it('rejects invalid cookies', async () => {
|
|
803
|
+
|
|
804
|
+
const server = Hapi.server({
|
|
805
|
+
routes: {
|
|
806
|
+
validate: {
|
|
807
|
+
state: {
|
|
808
|
+
a: Joi.string().min(8)
|
|
809
|
+
},
|
|
810
|
+
failAction: (request, h, err) => err, // Expose detailed error
|
|
811
|
+
validator: Joi
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
server.route({
|
|
817
|
+
method: 'GET',
|
|
818
|
+
path: '/',
|
|
819
|
+
handler: () => 'ok'
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
const res = await server.inject({ method: 'GET', url: '/', headers: { 'cookie': 'a=abc' } });
|
|
823
|
+
expect(res.statusCode).to.equal(400);
|
|
824
|
+
expect(res.result.validation).to.equal({
|
|
825
|
+
source: 'state',
|
|
826
|
+
keys: ['a']
|
|
827
|
+
});
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
it('accepts valid cookies', async () => {
|
|
831
|
+
|
|
832
|
+
const server = Hapi.server();
|
|
833
|
+
server.validator(Joi);
|
|
834
|
+
server.route({
|
|
835
|
+
method: 'GET',
|
|
836
|
+
path: '/',
|
|
837
|
+
handler: (request) => request.state,
|
|
838
|
+
options: {
|
|
839
|
+
validate: {
|
|
840
|
+
state: {
|
|
841
|
+
a: Joi.string().min(8),
|
|
842
|
+
b: Joi.array().single().items(Joi.boolean()),
|
|
843
|
+
c: Joi.string().default('value')
|
|
844
|
+
},
|
|
845
|
+
failAction: (request, h, err) => err // Expose detailed error
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
const res = await server.inject({ method: 'GET', url: '/', headers: { 'cookie': 'a=abcdefghi; b=true' } });
|
|
851
|
+
expect(res.statusCode).to.equal(200);
|
|
852
|
+
expect(res.result).to.equal({
|
|
853
|
+
a: 'abcdefghi',
|
|
854
|
+
b: [true],
|
|
855
|
+
c: 'value'
|
|
856
|
+
});
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
it('accepts all cookies', async () => {
|
|
860
|
+
|
|
861
|
+
const server = Hapi.server();
|
|
862
|
+
|
|
863
|
+
server.route({
|
|
864
|
+
method: 'GET',
|
|
865
|
+
path: '/',
|
|
866
|
+
handler: (request) => request.state,
|
|
867
|
+
options: {
|
|
868
|
+
validate: {
|
|
869
|
+
state: true
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
const res = await server.inject({ method: 'GET', url: '/', headers: { 'cookie': 'a=abc' } });
|
|
875
|
+
expect(res.statusCode).to.equal(200);
|
|
876
|
+
expect(res.result).to.equal({ a: 'abc' });
|
|
877
|
+
});
|
|
878
|
+
|
|
879
|
+
it('rejects all cookies', async () => {
|
|
880
|
+
|
|
881
|
+
const server = Hapi.server();
|
|
882
|
+
|
|
883
|
+
server.route({
|
|
884
|
+
method: 'GET',
|
|
885
|
+
path: '/',
|
|
886
|
+
handler: (request) => request.state,
|
|
887
|
+
options: {
|
|
888
|
+
validate: {
|
|
889
|
+
state: false
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
const res = await server.inject({ method: 'GET', url: '/', headers: { 'cookie': 'a=abc' } });
|
|
895
|
+
expect(res.statusCode).to.equal(400);
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
it('validates valid header', async () => {
|
|
899
|
+
|
|
900
|
+
const server = Hapi.server();
|
|
901
|
+
server.validator(Joi);
|
|
902
|
+
server.route({
|
|
903
|
+
method: 'GET',
|
|
904
|
+
path: '/',
|
|
905
|
+
handler: () => 'ok',
|
|
906
|
+
options: {
|
|
907
|
+
validate: {
|
|
908
|
+
headers: {
|
|
909
|
+
host: server.info.host + ':' + server.info.port,
|
|
910
|
+
accept: Joi.string().valid('application/json').required(),
|
|
911
|
+
'user-agent': Joi.string().optional()
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
});
|
|
916
|
+
|
|
917
|
+
const settings = {
|
|
918
|
+
url: '/',
|
|
919
|
+
method: 'GET',
|
|
920
|
+
headers: {
|
|
921
|
+
Accept: 'application/json'
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
|
|
925
|
+
const res = await server.inject(settings);
|
|
926
|
+
expect(res.statusCode).to.equal(200);
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
it('rejects invalid header', async () => {
|
|
930
|
+
|
|
931
|
+
const server = Hapi.server();
|
|
932
|
+
server.validator(Joi);
|
|
933
|
+
server.route({
|
|
934
|
+
method: 'GET',
|
|
935
|
+
path: '/',
|
|
936
|
+
handler: () => 'ok',
|
|
937
|
+
options: {
|
|
938
|
+
validate: {
|
|
939
|
+
headers: {
|
|
940
|
+
accept: Joi.string().valid('text/html').required(),
|
|
941
|
+
'user-agent': Joi.string().optional()
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
const settings = {
|
|
948
|
+
url: '/',
|
|
949
|
+
method: 'GET',
|
|
950
|
+
headers: {
|
|
951
|
+
Accept: 'application/json'
|
|
952
|
+
}
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
const res = await server.inject(settings);
|
|
956
|
+
expect(res.statusCode).to.equal(400);
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
it('binds route validate function to a context', async () => {
|
|
960
|
+
|
|
961
|
+
const server = Hapi.server();
|
|
962
|
+
|
|
963
|
+
const context = { valid: ['foo', 'bar'] };
|
|
964
|
+
server.bind(context);
|
|
965
|
+
|
|
966
|
+
server.route({
|
|
967
|
+
method: 'GET',
|
|
968
|
+
path: '/{val}',
|
|
969
|
+
options: {
|
|
970
|
+
validate: {
|
|
971
|
+
params: function (value, options) {
|
|
972
|
+
|
|
973
|
+
if (this.valid.indexOf(value) === -1) {
|
|
974
|
+
throw Boom.badRequest();
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
return value;
|
|
978
|
+
}
|
|
979
|
+
},
|
|
980
|
+
handler: () => null
|
|
981
|
+
}
|
|
982
|
+
});
|
|
983
|
+
|
|
984
|
+
const res = await server.inject('/baz');
|
|
985
|
+
expect(res.statusCode).to.equal(400);
|
|
986
|
+
});
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
describe('response', () => {
|
|
990
|
+
|
|
991
|
+
it('samples responses', async () => {
|
|
992
|
+
|
|
993
|
+
const server = Hapi.server({ debug: false });
|
|
994
|
+
server.validator(Joi);
|
|
995
|
+
server.route({
|
|
996
|
+
method: 'GET',
|
|
997
|
+
path: '/',
|
|
998
|
+
options: {
|
|
999
|
+
handler: () => ({ a: 1 }),
|
|
1000
|
+
response: {
|
|
1001
|
+
sample: 50,
|
|
1002
|
+
schema: {
|
|
1003
|
+
b: Joi.string()
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
let count = 0;
|
|
1010
|
+
const action = async function () {
|
|
1011
|
+
|
|
1012
|
+
const res = await server.inject('/');
|
|
1013
|
+
count += (res.statusCode === 500 ? 1 : 0);
|
|
1014
|
+
};
|
|
1015
|
+
|
|
1016
|
+
for (let i = 0; i < 500; ++i) {
|
|
1017
|
+
await action();
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
expect(count).to.be.within(200, 300);
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
it('validates response', async () => {
|
|
1024
|
+
|
|
1025
|
+
let i = 0;
|
|
1026
|
+
|
|
1027
|
+
const server = Hapi.server({ debug: false });
|
|
1028
|
+
server.validator(Joi);
|
|
1029
|
+
server.route({
|
|
1030
|
+
method: 'GET',
|
|
1031
|
+
path: '/',
|
|
1032
|
+
options: {
|
|
1033
|
+
response: {
|
|
1034
|
+
schema: {
|
|
1035
|
+
some: Joi.string()
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
},
|
|
1039
|
+
handler: () => ({ some: i++ ? null : 'value' })
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
const res1 = await server.inject('/');
|
|
1043
|
+
expect(res1.statusCode).to.equal(200);
|
|
1044
|
+
expect(res1.payload).to.equal('{"some":"value"}');
|
|
1045
|
+
|
|
1046
|
+
const res2 = await server.inject('/');
|
|
1047
|
+
expect(res2.statusCode).to.equal(500);
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
it('validates response with context', async () => {
|
|
1051
|
+
|
|
1052
|
+
const server = Hapi.server({ debug: false });
|
|
1053
|
+
server.validator(Joi);
|
|
1054
|
+
server.route({
|
|
1055
|
+
method: 'GET',
|
|
1056
|
+
path: '/',
|
|
1057
|
+
options: {
|
|
1058
|
+
response: {
|
|
1059
|
+
schema: Joi.object({
|
|
1060
|
+
some: Joi.string(),
|
|
1061
|
+
more: Joi.string()
|
|
1062
|
+
})
|
|
1063
|
+
.when('$query.user', { not: 'admin', then: Joi.object({ more: Joi.forbidden() }) })
|
|
1064
|
+
}
|
|
1065
|
+
},
|
|
1066
|
+
handler: () => ({ some: 'thing', more: 'stuff' })
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
const res1 = await server.inject('/?user=admin');
|
|
1070
|
+
expect(res1.statusCode).to.equal(200);
|
|
1071
|
+
expect(res1.payload).to.equal('{"some":"thing","more":"stuff"}');
|
|
1072
|
+
|
|
1073
|
+
const res2 = await server.inject('/?user=test');
|
|
1074
|
+
expect(res2.statusCode).to.equal(500);
|
|
1075
|
+
});
|
|
1076
|
+
|
|
1077
|
+
it('validates response using app context', async () => {
|
|
1078
|
+
|
|
1079
|
+
const server = Hapi.server({ debug: false });
|
|
1080
|
+
server.validator(Joi);
|
|
1081
|
+
server.route({
|
|
1082
|
+
method: 'GET',
|
|
1083
|
+
path: '/',
|
|
1084
|
+
handler: (request) => request.query.x,
|
|
1085
|
+
options: {
|
|
1086
|
+
response: {
|
|
1087
|
+
schema: Joi.valid(Joi.ref('$app.route.some'))
|
|
1088
|
+
},
|
|
1089
|
+
app: {
|
|
1090
|
+
some: 'b'
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
|
|
1095
|
+
const res1 = await server.inject('/?x=a');
|
|
1096
|
+
expect(res1.statusCode).to.equal(500);
|
|
1097
|
+
|
|
1098
|
+
const res2 = await server.inject('/?x=b');
|
|
1099
|
+
expect(res2.statusCode).to.equal(200);
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
it('validates error response', async () => {
|
|
1103
|
+
|
|
1104
|
+
let i = 0;
|
|
1105
|
+
|
|
1106
|
+
const server = Hapi.server({ debug: false });
|
|
1107
|
+
server.validator(Joi);
|
|
1108
|
+
server.route({
|
|
1109
|
+
method: 'GET',
|
|
1110
|
+
path: '/',
|
|
1111
|
+
options: {
|
|
1112
|
+
response: {
|
|
1113
|
+
status: {
|
|
1114
|
+
400: {
|
|
1115
|
+
statusCode: Joi.number(),
|
|
1116
|
+
error: Joi.string(),
|
|
1117
|
+
message: Joi.string(),
|
|
1118
|
+
custom: 0
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
},
|
|
1123
|
+
handler: () => {
|
|
1124
|
+
|
|
1125
|
+
const error = Boom.badRequest('Kaboom');
|
|
1126
|
+
error.output.payload.custom = i++;
|
|
1127
|
+
throw error;
|
|
1128
|
+
}
|
|
1129
|
+
});
|
|
1130
|
+
|
|
1131
|
+
const res1 = await server.inject('/');
|
|
1132
|
+
expect(res1.statusCode).to.equal(400);
|
|
1133
|
+
|
|
1134
|
+
const res2 = await server.inject('/');
|
|
1135
|
+
expect(res2.statusCode).to.equal(500);
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
it('validates error response and ignore 200', async () => {
|
|
1139
|
+
|
|
1140
|
+
let i = 0;
|
|
1141
|
+
|
|
1142
|
+
const server = Hapi.server({ debug: false });
|
|
1143
|
+
server.validator(Joi);
|
|
1144
|
+
server.route({
|
|
1145
|
+
method: 'GET',
|
|
1146
|
+
path: '/',
|
|
1147
|
+
options: {
|
|
1148
|
+
response: {
|
|
1149
|
+
schema: true,
|
|
1150
|
+
status: {
|
|
1151
|
+
400: {
|
|
1152
|
+
statusCode: Joi.number(),
|
|
1153
|
+
error: Joi.string(),
|
|
1154
|
+
message: Joi.string(),
|
|
1155
|
+
custom: 1
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
},
|
|
1160
|
+
handler: () => {
|
|
1161
|
+
|
|
1162
|
+
if (i === 0) {
|
|
1163
|
+
++i;
|
|
1164
|
+
return { a: 1, b: 2 };
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
const error = Boom.badRequest('Kaboom');
|
|
1168
|
+
error.output.payload.custom = i++;
|
|
1169
|
+
throw error;
|
|
1170
|
+
}
|
|
1171
|
+
});
|
|
1172
|
+
|
|
1173
|
+
const res1 = await server.inject('/');
|
|
1174
|
+
expect(res1.statusCode).to.equal(200);
|
|
1175
|
+
|
|
1176
|
+
const res2 = await server.inject('/');
|
|
1177
|
+
expect(res2.statusCode).to.equal(400);
|
|
1178
|
+
|
|
1179
|
+
const res3 = await server.inject('/');
|
|
1180
|
+
|
|
1181
|
+
expect(res3.statusCode).to.equal(500);
|
|
1182
|
+
});
|
|
1183
|
+
|
|
1184
|
+
it('validates and modifies response', async () => {
|
|
1185
|
+
|
|
1186
|
+
const server = Hapi.server({ debug: false });
|
|
1187
|
+
server.validator(Joi);
|
|
1188
|
+
server.route({
|
|
1189
|
+
method: 'GET',
|
|
1190
|
+
path: '/',
|
|
1191
|
+
options: {
|
|
1192
|
+
response: {
|
|
1193
|
+
schema: Joi.object({
|
|
1194
|
+
a: Joi.number()
|
|
1195
|
+
}).options({ stripUnknown: true }),
|
|
1196
|
+
modify: true
|
|
1197
|
+
}
|
|
1198
|
+
},
|
|
1199
|
+
handler: () => ({ a: 1, b: 2 })
|
|
1200
|
+
});
|
|
1201
|
+
|
|
1202
|
+
const res = await server.inject('/');
|
|
1203
|
+
expect(res.statusCode).to.equal(200);
|
|
1204
|
+
expect(res.result).to.equal({ a: 1 });
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
it('validates and modifies error response', async () => {
|
|
1208
|
+
|
|
1209
|
+
const server = Hapi.server({ debug: false });
|
|
1210
|
+
server.validator(Joi);
|
|
1211
|
+
server.route({
|
|
1212
|
+
method: 'GET',
|
|
1213
|
+
path: '/',
|
|
1214
|
+
options: {
|
|
1215
|
+
response: {
|
|
1216
|
+
status: {
|
|
1217
|
+
400: {
|
|
1218
|
+
statusCode: Joi.number(),
|
|
1219
|
+
error: Joi.string(),
|
|
1220
|
+
message: Joi.string(),
|
|
1221
|
+
custom: Joi.number()
|
|
1222
|
+
}
|
|
1223
|
+
},
|
|
1224
|
+
modify: true
|
|
1225
|
+
}
|
|
1226
|
+
},
|
|
1227
|
+
handler: () => {
|
|
1228
|
+
|
|
1229
|
+
const error = Boom.badRequest('Kaboom');
|
|
1230
|
+
error.output.payload.custom = '123';
|
|
1231
|
+
throw error;
|
|
1232
|
+
}
|
|
1233
|
+
});
|
|
1234
|
+
|
|
1235
|
+
const res = await server.inject('/');
|
|
1236
|
+
expect(res.statusCode).to.equal(400);
|
|
1237
|
+
expect(res.result.custom).to.equal(123);
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
it('validates empty response', async () => {
|
|
1241
|
+
|
|
1242
|
+
const server = Hapi.server();
|
|
1243
|
+
server.route({
|
|
1244
|
+
method: 'GET',
|
|
1245
|
+
path: '/',
|
|
1246
|
+
options: {
|
|
1247
|
+
response: {
|
|
1248
|
+
status: {
|
|
1249
|
+
204: false
|
|
1250
|
+
}
|
|
1251
|
+
},
|
|
1252
|
+
handler: (request, h) => h.response().code(204)
|
|
1253
|
+
}
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
const res = await server.inject('/');
|
|
1257
|
+
expect(res.statusCode).to.equal(204);
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
it('throws on sample with response modify', () => {
|
|
1261
|
+
|
|
1262
|
+
const server = Hapi.server({ debug: false });
|
|
1263
|
+
server.validator(Joi);
|
|
1264
|
+
expect(() => {
|
|
1265
|
+
|
|
1266
|
+
server.route({
|
|
1267
|
+
method: 'GET',
|
|
1268
|
+
path: '/',
|
|
1269
|
+
options: {
|
|
1270
|
+
response: {
|
|
1271
|
+
schema: Joi.object({
|
|
1272
|
+
a: Joi.number()
|
|
1273
|
+
}).options({ stripUnknown: true }),
|
|
1274
|
+
modify: true,
|
|
1275
|
+
sample: 90
|
|
1276
|
+
}
|
|
1277
|
+
},
|
|
1278
|
+
handler: () => ({ a: 1, b: 2 })
|
|
1279
|
+
});
|
|
1280
|
+
}).to.throw(/"response.sample" is not allowed/);
|
|
1281
|
+
});
|
|
1282
|
+
|
|
1283
|
+
it('do not throws on sample with false response modify', () => {
|
|
1284
|
+
|
|
1285
|
+
const server = Hapi.server({ debug: false });
|
|
1286
|
+
server.validator(Joi);
|
|
1287
|
+
expect(() => {
|
|
1288
|
+
|
|
1289
|
+
server.route({
|
|
1290
|
+
method: 'GET',
|
|
1291
|
+
path: '/',
|
|
1292
|
+
config: {
|
|
1293
|
+
response: {
|
|
1294
|
+
schema: Joi.object({
|
|
1295
|
+
a: Joi.number()
|
|
1296
|
+
}).options({ stripUnknown: true }),
|
|
1297
|
+
modify: false,
|
|
1298
|
+
sample: 90
|
|
1299
|
+
}
|
|
1300
|
+
},
|
|
1301
|
+
handler: () => ({ a: 1, b: 2 })
|
|
1302
|
+
});
|
|
1303
|
+
}).to.not.throw();
|
|
1304
|
+
});
|
|
1305
|
+
|
|
1306
|
+
it('validates response using custom validation function', async () => {
|
|
1307
|
+
|
|
1308
|
+
let i = 0;
|
|
1309
|
+
|
|
1310
|
+
const server = Hapi.server({ debug: false });
|
|
1311
|
+
server.route({
|
|
1312
|
+
method: 'GET',
|
|
1313
|
+
path: '/',
|
|
1314
|
+
options: {
|
|
1315
|
+
response: {
|
|
1316
|
+
schema: function (value, options) {
|
|
1317
|
+
|
|
1318
|
+
if (value.some === 'unchanged') {
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
if (value.some === 'null') {
|
|
1323
|
+
return null;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
throw new Error('Bad response');
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
},
|
|
1330
|
+
handler: () => {
|
|
1331
|
+
|
|
1332
|
+
++i;
|
|
1333
|
+
switch (i) {
|
|
1334
|
+
case 1: return { some: 'unchanged' };
|
|
1335
|
+
case 2: return { some: 'null' };
|
|
1336
|
+
default: return { some: 'throw' };
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
1340
|
+
|
|
1341
|
+
const res1 = await server.inject('/');
|
|
1342
|
+
expect(res1.statusCode).to.equal(200);
|
|
1343
|
+
expect(res1.result).to.equal({ some: 'unchanged' });
|
|
1344
|
+
|
|
1345
|
+
const res2 = await server.inject('/');
|
|
1346
|
+
expect(res2.statusCode).to.equal(200);
|
|
1347
|
+
expect(res2.result).to.equal({ some: 'null' });
|
|
1348
|
+
|
|
1349
|
+
const res3 = await server.inject('/');
|
|
1350
|
+
expect(res3.statusCode).to.equal(500);
|
|
1351
|
+
});
|
|
1352
|
+
|
|
1353
|
+
it('validates response using custom validation function (modify)', async () => {
|
|
1354
|
+
|
|
1355
|
+
let i = 0;
|
|
1356
|
+
|
|
1357
|
+
const server = Hapi.server({ debug: false });
|
|
1358
|
+
server.route({
|
|
1359
|
+
method: 'GET',
|
|
1360
|
+
path: '/',
|
|
1361
|
+
options: {
|
|
1362
|
+
response: {
|
|
1363
|
+
modify: true,
|
|
1364
|
+
schema: function (value, options) {
|
|
1365
|
+
|
|
1366
|
+
if (value.some === 'unchanged') {
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
if (value.some === 'null') {
|
|
1371
|
+
return null;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
throw new Error('Bad response');
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
},
|
|
1378
|
+
handler: () => {
|
|
1379
|
+
|
|
1380
|
+
++i;
|
|
1381
|
+
switch (i) {
|
|
1382
|
+
case 1: return { some: 'unchanged' };
|
|
1383
|
+
case 2: return { some: 'null' };
|
|
1384
|
+
default: return { some: 'throw' };
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
});
|
|
1388
|
+
|
|
1389
|
+
const res1 = await server.inject('/');
|
|
1390
|
+
expect(res1.statusCode).to.equal(200);
|
|
1391
|
+
expect(res1.result).to.equal({ some: 'unchanged' });
|
|
1392
|
+
|
|
1393
|
+
const res2 = await server.inject('/');
|
|
1394
|
+
expect(res2.statusCode).to.equal(204);
|
|
1395
|
+
expect(res2.result).to.equal(null);
|
|
1396
|
+
|
|
1397
|
+
const res3 = await server.inject('/');
|
|
1398
|
+
expect(res3.statusCode).to.equal(500);
|
|
1399
|
+
});
|
|
1400
|
+
|
|
1401
|
+
it('catches error thrown by custom validation function', async () => {
|
|
1402
|
+
|
|
1403
|
+
let i = 0;
|
|
1404
|
+
|
|
1405
|
+
const server = Hapi.server({ debug: false });
|
|
1406
|
+
server.route({
|
|
1407
|
+
method: 'GET',
|
|
1408
|
+
path: '/',
|
|
1409
|
+
options: {
|
|
1410
|
+
response: {
|
|
1411
|
+
schema: function (value, options) {
|
|
1412
|
+
|
|
1413
|
+
throw new Error('Bad response');
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
},
|
|
1417
|
+
handler: () => ({ some: i++ ? null : 'value' })
|
|
1418
|
+
});
|
|
1419
|
+
|
|
1420
|
+
const res = await server.inject('/');
|
|
1421
|
+
expect(res.statusCode).to.equal(500);
|
|
1422
|
+
});
|
|
1423
|
+
|
|
1424
|
+
it('skips response validation when sample is zero', async () => {
|
|
1425
|
+
|
|
1426
|
+
const server = Hapi.server({ debug: false });
|
|
1427
|
+
server.validator(Joi);
|
|
1428
|
+
server.route({
|
|
1429
|
+
method: 'GET',
|
|
1430
|
+
path: '/',
|
|
1431
|
+
options: {
|
|
1432
|
+
handler: () => ({ a: 1 }),
|
|
1433
|
+
response: {
|
|
1434
|
+
sample: 0,
|
|
1435
|
+
schema: {
|
|
1436
|
+
b: Joi.string()
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
});
|
|
1441
|
+
|
|
1442
|
+
let count = 0;
|
|
1443
|
+
const action = async function () {
|
|
1444
|
+
|
|
1445
|
+
const res = await server.inject('/');
|
|
1446
|
+
count += (res.statusCode === 500 ? 1 : 0);
|
|
1447
|
+
};
|
|
1448
|
+
|
|
1449
|
+
for (let i = 0; i < 500; ++i) {
|
|
1450
|
+
await action();
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
expect(count).to.equal(0);
|
|
1454
|
+
});
|
|
1455
|
+
|
|
1456
|
+
it('does not delete the response object from the route when sample is 0', async () => {
|
|
1457
|
+
|
|
1458
|
+
const server = Hapi.server({ debug: false });
|
|
1459
|
+
server.validator(Joi);
|
|
1460
|
+
server.route({
|
|
1461
|
+
method: 'GET',
|
|
1462
|
+
path: '/',
|
|
1463
|
+
options: {
|
|
1464
|
+
handler: () => 'ok',
|
|
1465
|
+
response: {
|
|
1466
|
+
sample: 0,
|
|
1467
|
+
schema: {
|
|
1468
|
+
b: Joi.string()
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
});
|
|
1473
|
+
|
|
1474
|
+
const res = await server.inject('/');
|
|
1475
|
+
expect(res.request.route.settings.response).to.exist();
|
|
1476
|
+
expect(res.request.route.settings.response.sample).to.equal(0);
|
|
1477
|
+
expect(res.request.route.settings.response.schema).to.exist();
|
|
1478
|
+
});
|
|
1479
|
+
|
|
1480
|
+
it('fails response validation with options', async () => {
|
|
1481
|
+
|
|
1482
|
+
const server = Hapi.server({ debug: false, routes: { response: { options: { convert: false } } } });
|
|
1483
|
+
server.validator(Joi);
|
|
1484
|
+
server.route({
|
|
1485
|
+
method: 'GET',
|
|
1486
|
+
path: '/',
|
|
1487
|
+
options: {
|
|
1488
|
+
handler: () => ({ a: '1' }),
|
|
1489
|
+
response: {
|
|
1490
|
+
schema: {
|
|
1491
|
+
a: Joi.number()
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
});
|
|
1496
|
+
|
|
1497
|
+
const res = await server.inject('/');
|
|
1498
|
+
expect(res.statusCode).to.equal(500);
|
|
1499
|
+
});
|
|
1500
|
+
|
|
1501
|
+
it('skips response validation when schema is true', async () => {
|
|
1502
|
+
|
|
1503
|
+
const server = Hapi.server({ debug: false });
|
|
1504
|
+
server.route({
|
|
1505
|
+
method: 'GET',
|
|
1506
|
+
path: '/',
|
|
1507
|
+
options: {
|
|
1508
|
+
handler: () => ({ a: '1' }),
|
|
1509
|
+
response: {
|
|
1510
|
+
schema: true
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
});
|
|
1514
|
+
|
|
1515
|
+
const res = await server.inject('/');
|
|
1516
|
+
expect(res.statusCode).to.equal(200);
|
|
1517
|
+
});
|
|
1518
|
+
|
|
1519
|
+
it('skips response validation when a status schema is true', async () => {
|
|
1520
|
+
|
|
1521
|
+
const server = Hapi.server({ debug: false });
|
|
1522
|
+
server.route({
|
|
1523
|
+
method: 'GET',
|
|
1524
|
+
path: '/',
|
|
1525
|
+
options: {
|
|
1526
|
+
handler: (request, h) => h.redirect('/somewhere'),
|
|
1527
|
+
response: {
|
|
1528
|
+
schema: false,
|
|
1529
|
+
status: {
|
|
1530
|
+
302: true
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
});
|
|
1535
|
+
|
|
1536
|
+
const res = await server.inject('/');
|
|
1537
|
+
expect(res.statusCode).to.equal(302);
|
|
1538
|
+
});
|
|
1539
|
+
|
|
1540
|
+
it('skips response validation when status is empty', async () => {
|
|
1541
|
+
|
|
1542
|
+
const server = Hapi.server({ debug: false });
|
|
1543
|
+
server.route({
|
|
1544
|
+
method: 'GET',
|
|
1545
|
+
path: '/',
|
|
1546
|
+
options: {
|
|
1547
|
+
handler: () => ({ a: '1' }),
|
|
1548
|
+
response: {
|
|
1549
|
+
status: {}
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
});
|
|
1553
|
+
|
|
1554
|
+
const res = await server.inject('/');
|
|
1555
|
+
expect(res.statusCode).to.equal(200);
|
|
1556
|
+
});
|
|
1557
|
+
|
|
1558
|
+
it('forbids response when schema is false', async () => {
|
|
1559
|
+
|
|
1560
|
+
const server = Hapi.server({ debug: false });
|
|
1561
|
+
server.route({
|
|
1562
|
+
method: 'GET',
|
|
1563
|
+
path: '/',
|
|
1564
|
+
options: {
|
|
1565
|
+
handler: () => ({ a: '1' }),
|
|
1566
|
+
response: {
|
|
1567
|
+
schema: false
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
});
|
|
1571
|
+
|
|
1572
|
+
const res = await server.inject('/');
|
|
1573
|
+
expect(res.statusCode).to.equal(500);
|
|
1574
|
+
});
|
|
1575
|
+
|
|
1576
|
+
it('ignores error responses', async () => {
|
|
1577
|
+
|
|
1578
|
+
const server = Hapi.server();
|
|
1579
|
+
server.validator(Joi);
|
|
1580
|
+
server.route({
|
|
1581
|
+
method: 'GET',
|
|
1582
|
+
path: '/',
|
|
1583
|
+
options: {
|
|
1584
|
+
handler: () => {
|
|
1585
|
+
|
|
1586
|
+
throw Boom.badRequest();
|
|
1587
|
+
},
|
|
1588
|
+
response: {
|
|
1589
|
+
schema: {
|
|
1590
|
+
b: Joi.string()
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
});
|
|
1595
|
+
|
|
1596
|
+
const res = await server.inject('/');
|
|
1597
|
+
expect(res.statusCode).to.equal(400);
|
|
1598
|
+
});
|
|
1599
|
+
|
|
1600
|
+
it('errors on non-plain-object responses', async () => {
|
|
1601
|
+
|
|
1602
|
+
const server = Hapi.server({ debug: false });
|
|
1603
|
+
server.validator(Joi);
|
|
1604
|
+
await server.register(Inert);
|
|
1605
|
+
server.route({
|
|
1606
|
+
method: 'GET',
|
|
1607
|
+
path: '/',
|
|
1608
|
+
options: {
|
|
1609
|
+
handler: (request, h) => h.file('./package.json'),
|
|
1610
|
+
response: {
|
|
1611
|
+
schema: {
|
|
1612
|
+
b: Joi.string()
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
});
|
|
1617
|
+
|
|
1618
|
+
const res = await server.inject('/');
|
|
1619
|
+
expect(res.statusCode).to.equal(500);
|
|
1620
|
+
});
|
|
1621
|
+
|
|
1622
|
+
it('logs invalid responses', async () => {
|
|
1623
|
+
|
|
1624
|
+
const server = Hapi.server({ debug: false });
|
|
1625
|
+
server.validator(Joi);
|
|
1626
|
+
server.route({
|
|
1627
|
+
method: 'GET',
|
|
1628
|
+
path: '/',
|
|
1629
|
+
options: {
|
|
1630
|
+
handler: () => ({ a: '1' }),
|
|
1631
|
+
response: {
|
|
1632
|
+
failAction: 'log',
|
|
1633
|
+
schema: {
|
|
1634
|
+
b: Joi.string()
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
});
|
|
1639
|
+
|
|
1640
|
+
server.events.on({ name: 'request', channels: 'internal' }, (request, event, tags) => {
|
|
1641
|
+
|
|
1642
|
+
if (tags.validation) {
|
|
1643
|
+
expect(event.error.message).to.equal('"a" is not allowed');
|
|
1644
|
+
}
|
|
1645
|
+
});
|
|
1646
|
+
|
|
1647
|
+
const res = await server.inject('/');
|
|
1648
|
+
expect(res.statusCode).to.equal(200);
|
|
1649
|
+
});
|
|
1650
|
+
|
|
1651
|
+
it('replaces error with message on invalid response', async () => {
|
|
1652
|
+
|
|
1653
|
+
const server = Hapi.server();
|
|
1654
|
+
server.validator(Joi);
|
|
1655
|
+
server.route({
|
|
1656
|
+
method: 'GET',
|
|
1657
|
+
path: '/',
|
|
1658
|
+
options: {
|
|
1659
|
+
handler: () => ({ a: '1' }),
|
|
1660
|
+
response: {
|
|
1661
|
+
failAction: function (request, h, err) {
|
|
1662
|
+
|
|
1663
|
+
return h.response('Validation Error Occurred').code(400);
|
|
1664
|
+
},
|
|
1665
|
+
schema: {
|
|
1666
|
+
b: Joi.string()
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
});
|
|
1671
|
+
|
|
1672
|
+
const res = await server.inject('/');
|
|
1673
|
+
expect(res.statusCode).to.equal(400);
|
|
1674
|
+
expect(res.payload).to.equal('Validation Error Occurred');
|
|
1675
|
+
});
|
|
1676
|
+
|
|
1677
|
+
it('combines onPreResponse with response validation override', async () => {
|
|
1678
|
+
|
|
1679
|
+
const server = Hapi.server();
|
|
1680
|
+
server.validator(Joi);
|
|
1681
|
+
server.ext('onPreResponse', () => 'else');
|
|
1682
|
+
server.route({
|
|
1683
|
+
method: 'GET',
|
|
1684
|
+
path: '/',
|
|
1685
|
+
options: {
|
|
1686
|
+
handler: () => ({ a: '1' }),
|
|
1687
|
+
response: {
|
|
1688
|
+
failAction: function (request, h, err) {
|
|
1689
|
+
|
|
1690
|
+
return h.response('something');
|
|
1691
|
+
},
|
|
1692
|
+
schema: {
|
|
1693
|
+
b: Joi.string()
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
});
|
|
1698
|
+
|
|
1699
|
+
const res = await server.inject('/');
|
|
1700
|
+
expect(res.statusCode).to.equal(200);
|
|
1701
|
+
expect(res.payload).to.equal('else');
|
|
1702
|
+
});
|
|
1703
|
+
|
|
1704
|
+
it('combines onPreResponse with response validation override takeover', async () => {
|
|
1705
|
+
|
|
1706
|
+
const server = Hapi.server();
|
|
1707
|
+
server.validator(Joi);
|
|
1708
|
+
server.ext('onPreResponse', () => 'else');
|
|
1709
|
+
server.route({
|
|
1710
|
+
method: 'GET',
|
|
1711
|
+
path: '/',
|
|
1712
|
+
options: {
|
|
1713
|
+
handler: () => ({ a: '1' }),
|
|
1714
|
+
response: {
|
|
1715
|
+
failAction: function (request, h, err) {
|
|
1716
|
+
|
|
1717
|
+
return h.response('something').takeover();
|
|
1718
|
+
},
|
|
1719
|
+
schema: {
|
|
1720
|
+
b: Joi.string()
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
});
|
|
1725
|
+
|
|
1726
|
+
const res = await server.inject('/');
|
|
1727
|
+
expect(res.statusCode).to.equal(200);
|
|
1728
|
+
expect(res.payload).to.equal('else');
|
|
1729
|
+
});
|
|
1730
|
+
|
|
1731
|
+
it('combines onPreResponse with response validation error', async () => {
|
|
1732
|
+
|
|
1733
|
+
const server = Hapi.server();
|
|
1734
|
+
|
|
1735
|
+
const responses = [];
|
|
1736
|
+
|
|
1737
|
+
server.ext('onPreResponse', (request, h) => {
|
|
1738
|
+
|
|
1739
|
+
responses.push(request.response);
|
|
1740
|
+
return h.continue;
|
|
1741
|
+
});
|
|
1742
|
+
|
|
1743
|
+
server.route({
|
|
1744
|
+
method: 'GET',
|
|
1745
|
+
path: '/',
|
|
1746
|
+
options: {
|
|
1747
|
+
handler: () => {
|
|
1748
|
+
|
|
1749
|
+
const err = Boom.internal('handler error');
|
|
1750
|
+
err.output.payload.x = 1;
|
|
1751
|
+
throw err;
|
|
1752
|
+
},
|
|
1753
|
+
response: {
|
|
1754
|
+
status: {
|
|
1755
|
+
500: (value, options) => {
|
|
1756
|
+
|
|
1757
|
+
responses.push(value);
|
|
1758
|
+
throw new Error('500 validation error');
|
|
1759
|
+
}
|
|
1760
|
+
},
|
|
1761
|
+
failAction: (request, h, err) => {
|
|
1762
|
+
|
|
1763
|
+
responses.push(err);
|
|
1764
|
+
throw new Error('failAction error');
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
});
|
|
1769
|
+
|
|
1770
|
+
const res = await server.inject('/');
|
|
1771
|
+
expect(res.statusCode).to.equal(500);
|
|
1772
|
+
|
|
1773
|
+
expect(responses).to.have.length(3);
|
|
1774
|
+
expect(responses[0].x).to.equal(1);
|
|
1775
|
+
expect(responses[1]).to.be.an.error('500 validation error');
|
|
1776
|
+
});
|
|
1777
|
+
|
|
1778
|
+
it('validates string response', async () => {
|
|
1779
|
+
|
|
1780
|
+
let value = 'abcd';
|
|
1781
|
+
|
|
1782
|
+
const server = Hapi.server({ debug: false });
|
|
1783
|
+
server.validator(Joi);
|
|
1784
|
+
server.route({
|
|
1785
|
+
method: 'GET',
|
|
1786
|
+
path: '/',
|
|
1787
|
+
options: {
|
|
1788
|
+
response: {
|
|
1789
|
+
schema: Joi.string().min(5)
|
|
1790
|
+
}
|
|
1791
|
+
},
|
|
1792
|
+
handler: () => value
|
|
1793
|
+
});
|
|
1794
|
+
|
|
1795
|
+
const res1 = await server.inject('/');
|
|
1796
|
+
expect(res1.statusCode).to.equal(500);
|
|
1797
|
+
value += 'e';
|
|
1798
|
+
|
|
1799
|
+
const res2 = await server.inject('/');
|
|
1800
|
+
expect(res2.statusCode).to.equal(200);
|
|
1801
|
+
expect(res2.payload).to.equal('abcde');
|
|
1802
|
+
});
|
|
1803
|
+
|
|
1804
|
+
it('validates boolean response', async () => {
|
|
1805
|
+
|
|
1806
|
+
let value = 'abcd';
|
|
1807
|
+
|
|
1808
|
+
const server = Hapi.server({ debug: false });
|
|
1809
|
+
server.validator(Joi);
|
|
1810
|
+
server.route({
|
|
1811
|
+
method: 'GET',
|
|
1812
|
+
path: '/',
|
|
1813
|
+
options: {
|
|
1814
|
+
response: {
|
|
1815
|
+
schema: Joi.boolean().truthy('on'),
|
|
1816
|
+
modify: true
|
|
1817
|
+
}
|
|
1818
|
+
},
|
|
1819
|
+
handler: () => value
|
|
1820
|
+
});
|
|
1821
|
+
|
|
1822
|
+
const res1 = await server.inject('/');
|
|
1823
|
+
expect(res1.statusCode).to.equal(500);
|
|
1824
|
+
value = 'on';
|
|
1825
|
+
|
|
1826
|
+
const res2 = await server.inject('/');
|
|
1827
|
+
expect(res2.statusCode).to.equal(200);
|
|
1828
|
+
expect(res2.payload).to.equal('true');
|
|
1829
|
+
});
|
|
1830
|
+
});
|
|
1831
|
+
});
|