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