1 | 'use strict'
|
2 |
|
3 | const split = require('split2')
|
4 | const sget = require('simple-get').concat
|
5 | const Fastify = require('..')
|
6 | const fs = require('fs')
|
7 | const { promisify } = require('util')
|
8 | const sleep = promisify(setTimeout)
|
9 |
|
10 | function asyncHookTest (t) {
|
11 | const test = t.test
|
12 | test('async hooks', t => {
|
13 | t.plan(21)
|
14 |
|
15 | const fastify = Fastify()
|
16 | fastify.addHook('onRequest', async function (request, reply) {
|
17 | await sleep(1)
|
18 | request.test = 'the request is coming'
|
19 | reply.test = 'the reply has come'
|
20 | if (request.raw.method === 'DELETE') {
|
21 | throw new Error('some error')
|
22 | }
|
23 | })
|
24 |
|
25 | fastify.addHook('preHandler', async function (request, reply) {
|
26 | await sleep(1)
|
27 | t.is(request.test, 'the request is coming')
|
28 | t.is(reply.test, 'the reply has come')
|
29 | if (request.raw.method === 'HEAD') {
|
30 | throw new Error('some error')
|
31 | }
|
32 | })
|
33 |
|
34 | fastify.addHook('onSend', async function (request, reply, payload) {
|
35 | await sleep(1)
|
36 | t.ok('onSend called')
|
37 | })
|
38 |
|
39 | fastify.addHook('onResponse', async function (request, reply) {
|
40 | await sleep(1)
|
41 | t.ok('onResponse called')
|
42 | })
|
43 |
|
44 | fastify.get('/', function (request, reply) {
|
45 | t.is(request.test, 'the request is coming')
|
46 | t.is(reply.test, 'the reply has come')
|
47 | reply.code(200).send({ hello: 'world' })
|
48 | })
|
49 |
|
50 | fastify.head('/', function (req, reply) {
|
51 | reply.code(200).send({ hello: 'world' })
|
52 | })
|
53 |
|
54 | fastify.delete('/', function (req, reply) {
|
55 | reply.code(200).send({ hello: 'world' })
|
56 | })
|
57 |
|
58 | fastify.listen(0, err => {
|
59 | t.error(err)
|
60 | fastify.server.unref()
|
61 |
|
62 | sget({
|
63 | method: 'GET',
|
64 | url: 'http://localhost:' + fastify.server.address().port
|
65 | }, (err, response, body) => {
|
66 | t.error(err)
|
67 | t.strictEqual(response.statusCode, 200)
|
68 | t.strictEqual(response.headers['content-length'], '' + body.length)
|
69 | t.deepEqual(JSON.parse(body), { hello: 'world' })
|
70 | })
|
71 |
|
72 | sget({
|
73 | method: 'HEAD',
|
74 | url: 'http://localhost:' + fastify.server.address().port
|
75 | }, (err, response, body) => {
|
76 | t.error(err)
|
77 | t.strictEqual(response.statusCode, 500)
|
78 | })
|
79 |
|
80 | sget({
|
81 | method: 'DELETE',
|
82 | url: 'http://localhost:' + fastify.server.address().port
|
83 | }, (err, response, body) => {
|
84 | t.error(err)
|
85 | t.strictEqual(response.statusCode, 500)
|
86 | })
|
87 | })
|
88 | })
|
89 |
|
90 | test('modify payload', t => {
|
91 | t.plan(10)
|
92 | const fastify = Fastify()
|
93 | const payload = { hello: 'world' }
|
94 | const modifiedPayload = { hello: 'modified' }
|
95 | const anotherPayload = '"winter is coming"'
|
96 |
|
97 | fastify.addHook('onSend', async function (request, reply, thePayload) {
|
98 | t.ok('onSend called')
|
99 | t.deepEqual(JSON.parse(thePayload), payload)
|
100 | return thePayload.replace('world', 'modified')
|
101 | })
|
102 |
|
103 | fastify.addHook('onSend', async function (request, reply, thePayload) {
|
104 | t.ok('onSend called')
|
105 | t.deepEqual(JSON.parse(thePayload), modifiedPayload)
|
106 | return anotherPayload
|
107 | })
|
108 |
|
109 | fastify.addHook('onSend', async function (request, reply, thePayload) {
|
110 | t.ok('onSend called')
|
111 | t.strictEqual(thePayload, anotherPayload)
|
112 | })
|
113 |
|
114 | fastify.get('/', (req, reply) => {
|
115 | reply.send(payload)
|
116 | })
|
117 |
|
118 | fastify.inject({
|
119 | method: 'GET',
|
120 | url: '/'
|
121 | }, (err, res) => {
|
122 | t.error(err)
|
123 | t.strictEqual(res.payload, anotherPayload)
|
124 | t.strictEqual(res.statusCode, 200)
|
125 | t.strictEqual(res.headers['content-length'], '18')
|
126 | })
|
127 | })
|
128 |
|
129 | test('onRequest hooks should be able to block a request', t => {
|
130 | t.plan(5)
|
131 | const fastify = Fastify()
|
132 |
|
133 | fastify.addHook('onRequest', async (req, reply) => {
|
134 | reply.send('hello')
|
135 | })
|
136 |
|
137 | fastify.addHook('onRequest', async (req, reply) => {
|
138 | t.fail('this should not be called')
|
139 | })
|
140 |
|
141 | fastify.addHook('preHandler', async (req, reply) => {
|
142 | t.fail('this should not be called')
|
143 | })
|
144 |
|
145 | fastify.addHook('onSend', async (req, reply, payload) => {
|
146 | t.ok('called')
|
147 | })
|
148 |
|
149 | fastify.addHook('onResponse', async (request, reply) => {
|
150 | t.ok('called')
|
151 | })
|
152 |
|
153 | fastify.get('/', function (request, reply) {
|
154 | t.fail('we should not be here')
|
155 | })
|
156 |
|
157 | fastify.inject({
|
158 | url: '/',
|
159 | method: 'GET'
|
160 | }, (err, res) => {
|
161 | t.error(err)
|
162 | t.is(res.statusCode, 200)
|
163 | t.is(res.payload, 'hello')
|
164 | })
|
165 | })
|
166 |
|
167 | test('preHandler hooks should be able to block a request', t => {
|
168 | t.plan(5)
|
169 | const fastify = Fastify()
|
170 |
|
171 | fastify.addHook('preHandler', async (req, reply) => {
|
172 | reply.send('hello')
|
173 | })
|
174 |
|
175 | fastify.addHook('preHandler', async (req, reply) => {
|
176 | t.fail('this should not be called')
|
177 | })
|
178 |
|
179 | fastify.addHook('onSend', async (req, reply, payload) => {
|
180 | t.equal(payload, 'hello')
|
181 | })
|
182 |
|
183 | fastify.addHook('onResponse', async (request, reply) => {
|
184 | t.ok('called')
|
185 | })
|
186 |
|
187 | fastify.get('/', function (request, reply) {
|
188 | t.fail('we should not be here')
|
189 | })
|
190 |
|
191 | fastify.inject({
|
192 | url: '/',
|
193 | method: 'GET'
|
194 | }, (err, res) => {
|
195 | t.error(err)
|
196 | t.is(res.statusCode, 200)
|
197 | t.is(res.payload, 'hello')
|
198 | })
|
199 | })
|
200 |
|
201 | test('preValidation hooks should be able to block a request', t => {
|
202 | t.plan(5)
|
203 | const fastify = Fastify()
|
204 |
|
205 | fastify.addHook('preValidation', async (req, reply) => {
|
206 | reply.send('hello')
|
207 | })
|
208 |
|
209 | fastify.addHook('preValidation', async (req, reply) => {
|
210 | t.fail('this should not be called')
|
211 | })
|
212 |
|
213 | fastify.addHook('onSend', async (req, reply, payload) => {
|
214 | t.equal(payload, 'hello')
|
215 | })
|
216 |
|
217 | fastify.addHook('onResponse', async (request, reply) => {
|
218 | t.ok('called')
|
219 | })
|
220 |
|
221 | fastify.get('/', function (request, reply) {
|
222 | t.fail('we should not be here')
|
223 | })
|
224 |
|
225 | fastify.inject({
|
226 | url: '/',
|
227 | method: 'GET'
|
228 | }, (err, res) => {
|
229 | t.error(err)
|
230 | t.is(res.statusCode, 200)
|
231 | t.is(res.payload, 'hello')
|
232 | })
|
233 | })
|
234 |
|
235 | test('preValidation hooks should be able to block a request with async onSend', t => {
|
236 | t.plan(5)
|
237 | const fastify = Fastify()
|
238 |
|
239 | fastify.addHook('preValidation', async (req, reply) => {
|
240 | reply.send('hello')
|
241 | })
|
242 |
|
243 | fastify.addHook('onSend', async (req, reply, payload) => {
|
244 | await sleep(10)
|
245 | t.equal(payload, 'hello')
|
246 | })
|
247 |
|
248 | fastify.addHook('preHandler', async (request, reply) => {
|
249 | t.fail('we should not be here')
|
250 | })
|
251 |
|
252 | fastify.addHook('onResponse', async (request, reply) => {
|
253 | t.ok('called')
|
254 | })
|
255 |
|
256 | fastify.post('/', {
|
257 | schema: {
|
258 | body: {
|
259 | type: 'object',
|
260 | properties: {
|
261 | hello: {
|
262 | type: 'string'
|
263 | }
|
264 | },
|
265 | required: ['hello']
|
266 | }
|
267 | }
|
268 | }, function (request, reply) {
|
269 | t.fail('we should not be here')
|
270 | })
|
271 |
|
272 | fastify.inject({
|
273 | url: '/',
|
274 | method: 'POST'
|
275 | }, (err, res) => {
|
276 | t.error(err)
|
277 | t.is(res.statusCode, 200)
|
278 | t.is(res.payload, 'hello')
|
279 | })
|
280 | })
|
281 |
|
282 | test('preSerialization hooks should be able to modify the payload', t => {
|
283 | t.plan(3)
|
284 | const fastify = Fastify()
|
285 |
|
286 | fastify.addHook('preSerialization', async (req, reply, payload) => {
|
287 | return { hello: 'another world' }
|
288 | })
|
289 |
|
290 | fastify.get('/', function (request, reply) {
|
291 | reply.send({ hello: 'world' })
|
292 | })
|
293 |
|
294 | fastify.inject({
|
295 | url: '/',
|
296 | method: 'GET'
|
297 | }, (err, res) => {
|
298 | t.error(err)
|
299 | t.is(res.statusCode, 200)
|
300 | t.deepEqual(JSON.parse(res.payload), { hello: 'another world' })
|
301 | })
|
302 | })
|
303 |
|
304 | test('preSerialization hooks should handle errors', t => {
|
305 | t.plan(3)
|
306 | const fastify = Fastify()
|
307 |
|
308 | fastify.addHook('preSerialization', async (req, reply, payload) => {
|
309 | throw new Error('kaboom')
|
310 | })
|
311 |
|
312 | fastify.get('/', function (request, reply) {
|
313 | reply.send({ hello: 'world' })
|
314 | })
|
315 |
|
316 | fastify.inject({
|
317 | url: '/',
|
318 | method: 'GET'
|
319 | }, (err, res) => {
|
320 | t.error(err)
|
321 | t.is(res.statusCode, 500)
|
322 | t.deepEqual(JSON.parse(res.payload), { error: 'Internal Server Error', message: 'kaboom', statusCode: 500 })
|
323 | })
|
324 | })
|
325 |
|
326 | test('onRequest hooks should be able to block a request (last hook)', t => {
|
327 | t.plan(5)
|
328 | const fastify = Fastify()
|
329 |
|
330 | fastify.addHook('onRequest', async (req, reply) => {
|
331 | reply.send('hello')
|
332 | })
|
333 |
|
334 | fastify.addHook('preHandler', async (req, reply) => {
|
335 | t.fail('this should not be called')
|
336 | })
|
337 |
|
338 | fastify.addHook('onSend', async (req, reply, payload) => {
|
339 | t.ok('called')
|
340 | })
|
341 |
|
342 | fastify.addHook('onResponse', async (request, reply) => {
|
343 | t.ok('called')
|
344 | })
|
345 |
|
346 | fastify.get('/', function (request, reply) {
|
347 | t.fail('we should not be here')
|
348 | })
|
349 |
|
350 | fastify.inject({
|
351 | url: '/',
|
352 | method: 'GET'
|
353 | }, (err, res) => {
|
354 | t.error(err)
|
355 | t.is(res.statusCode, 200)
|
356 | t.is(res.payload, 'hello')
|
357 | })
|
358 | })
|
359 |
|
360 | test('preHandler hooks should be able to block a request (last hook)', t => {
|
361 | t.plan(5)
|
362 | const fastify = Fastify()
|
363 |
|
364 | fastify.addHook('preHandler', async (req, reply) => {
|
365 | reply.send('hello')
|
366 | })
|
367 |
|
368 | fastify.addHook('onSend', async (req, reply, payload) => {
|
369 | t.equal(payload, 'hello')
|
370 | })
|
371 |
|
372 | fastify.addHook('onResponse', async (request, reply) => {
|
373 | t.ok('called')
|
374 | })
|
375 |
|
376 | fastify.get('/', function (request, reply) {
|
377 | t.fail('we should not be here')
|
378 | })
|
379 |
|
380 | fastify.inject({
|
381 | url: '/',
|
382 | method: 'GET'
|
383 | }, (err, res) => {
|
384 | t.error(err)
|
385 | t.is(res.statusCode, 200)
|
386 | t.is(res.payload, 'hello')
|
387 | })
|
388 | })
|
389 |
|
390 | test('preHandler hooks should be able to block a request (last hook) with a delay in onSend', t => {
|
391 | t.plan(5)
|
392 | const fastify = Fastify()
|
393 |
|
394 | fastify.addHook('preHandler', async (req, reply) => {
|
395 | reply.send('hello')
|
396 | })
|
397 |
|
398 | fastify.addHook('onSend', async (req, reply, payload) => {
|
399 | await sleep(10)
|
400 | t.equal(payload, 'hello')
|
401 | })
|
402 |
|
403 | fastify.addHook('onResponse', async (request, reply) => {
|
404 | t.ok('called')
|
405 | })
|
406 |
|
407 | fastify.get('/', function (request, reply) {
|
408 | t.fail('we should not be here')
|
409 | })
|
410 |
|
411 | fastify.inject({
|
412 | url: '/',
|
413 | method: 'GET'
|
414 | }, (err, res) => {
|
415 | t.error(err)
|
416 | t.is(res.statusCode, 200)
|
417 | t.is(res.payload, 'hello')
|
418 | })
|
419 | })
|
420 |
|
421 | test('onRequest respond with a stream', t => {
|
422 | t.plan(4)
|
423 | const fastify = Fastify()
|
424 |
|
425 | fastify.addHook('onRequest', async (req, reply) => {
|
426 | return new Promise((resolve, reject) => {
|
427 | const stream = fs.createReadStream(process.cwd() + '/test/stream.test.js', 'utf8')
|
428 |
|
429 |
|
430 | reply.send(stream)
|
431 | reply.res.once('finish', () => resolve())
|
432 | })
|
433 | })
|
434 |
|
435 | fastify.addHook('onRequest', async (req, res) => {
|
436 | t.fail('this should not be called')
|
437 | })
|
438 |
|
439 | fastify.addHook('preHandler', async (req, reply) => {
|
440 | t.fail('this should not be called')
|
441 | })
|
442 |
|
443 | fastify.addHook('onSend', async (req, reply, payload) => {
|
444 | t.ok('called')
|
445 | })
|
446 |
|
447 | fastify.addHook('onResponse', async (request, reply) => {
|
448 | t.ok('called')
|
449 | })
|
450 |
|
451 | fastify.get('/', function (request, reply) {
|
452 | t.fail('we should not be here')
|
453 | })
|
454 |
|
455 | fastify.inject({
|
456 | url: '/',
|
457 | method: 'GET'
|
458 | }, (err, res) => {
|
459 | t.error(err)
|
460 | t.is(res.statusCode, 200)
|
461 | })
|
462 | })
|
463 |
|
464 | test('preHandler respond with a stream', t => {
|
465 | t.plan(7)
|
466 | const fastify = Fastify()
|
467 |
|
468 | fastify.addHook('onRequest', async (req, res) => {
|
469 | t.ok('called')
|
470 | })
|
471 |
|
472 |
|
473 |
|
474 | const order = [1, 2]
|
475 |
|
476 | fastify.addHook('preHandler', async (req, reply) => {
|
477 | return new Promise((resolve, reject) => {
|
478 | const stream = fs.createReadStream(process.cwd() + '/test/stream.test.js', 'utf8')
|
479 | reply.send(stream)
|
480 | reply.res.once('finish', () => {
|
481 | t.is(order.shift(), 2)
|
482 | resolve()
|
483 | })
|
484 | })
|
485 | })
|
486 |
|
487 | fastify.addHook('preHandler', async (req, reply) => {
|
488 | t.fail('this should not be called')
|
489 | })
|
490 |
|
491 | fastify.addHook('onSend', async (req, reply, payload) => {
|
492 | t.is(order.shift(), 1)
|
493 | t.is(typeof payload.pipe, 'function')
|
494 | })
|
495 |
|
496 | fastify.addHook('onResponse', async (request, reply) => {
|
497 | t.ok('called')
|
498 | })
|
499 |
|
500 | fastify.get('/', function (request, reply) {
|
501 | t.fail('we should not be here')
|
502 | })
|
503 |
|
504 | fastify.inject({
|
505 | url: '/',
|
506 | method: 'GET'
|
507 | }, (err, res) => {
|
508 | t.error(err)
|
509 | t.is(res.statusCode, 200)
|
510 | })
|
511 | })
|
512 |
|
513 | test('Should log a warning if is an async function with `next`', t => {
|
514 | t.test('3 arguments', t => {
|
515 | t.plan(3)
|
516 | const stream = split(JSON.parse)
|
517 | const fastify = Fastify({
|
518 | logger: { stream }
|
519 | })
|
520 |
|
521 | stream.on('data', line => {
|
522 | t.strictEqual(line.level, 40)
|
523 | t.true(line.msg.startsWith("Async function has too many arguments. Async hooks should not use the 'next' argument."))
|
524 | t.true(/test(\\|\/)hooks-async\.js/.test(line.msg))
|
525 | })
|
526 |
|
527 | fastify.addHook('onRequest', async (req, reply, next) => {})
|
528 | })
|
529 |
|
530 | t.test('4 arguments', t => {
|
531 | t.plan(6)
|
532 | const stream = split(JSON.parse)
|
533 | const fastify = Fastify({
|
534 | logger: { stream }
|
535 | })
|
536 |
|
537 | stream.on('data', line => {
|
538 | t.strictEqual(line.level, 40)
|
539 | t.true(line.msg.startsWith("Async function has too many arguments. Async hooks should not use the 'next' argument."))
|
540 | t.true(/test(\\|\/)hooks-async\.js/.test(line.msg))
|
541 | })
|
542 |
|
543 | fastify.addHook('onSend', async (req, reply, payload, next) => {})
|
544 | fastify.addHook('preSerialization', async (req, reply, payload, next) => {})
|
545 | })
|
546 |
|
547 | t.end()
|
548 | })
|
549 | }
|
550 |
|
551 | module.exports = asyncHookTest
|