strong-tie/inbound-calls
0
1'use strict'2 3const { test } = require('node:test')4const sget = require('simple-get').concat5const Fastify = require('..')6const split = require('split2')7const pino = require('pino')8const { sleep } = require('./helper')9const statusCodes = require('node:http').STATUS_CODES10 11const opts = {12 schema: {13 response: {14 '2xx': {15 type: 'object',16 properties: {17 hello: {18 type: 'string'19 }20 }21 }22 }23 }24}25 26const optsWithHostnameAndPort = {27 schema: {28 response: {29 '2xx': {30 type: 'object',31 properties: {32 hello: {33 type: 'string'34 },35 hostname: {36 type: 'string'37 },38 port: {39 type: 'string'40 }41 }42 }43 }44 }45}46test('async await', (t, done) => {47 t.plan(16)48 const fastify = Fastify()49 try {50 fastify.get('/', opts, async function awaitMyFunc (req, reply) {51 await sleep(200)52 return { hello: 'world' }53 })54 t.assert.ok(true)55 } catch (e) {56 t.assert.fail()57 }58 59 try {60 fastify.get('/no-await', opts, async function (req, reply) {61 return { hello: 'world' }62 })63 t.assert.ok(true)64 } catch (e) {65 t.assert.fail()66 }67 68 try {69 fastify.get('/await/hostname_port', optsWithHostnameAndPort, async function awaitMyFunc (req, reply) {70 await sleep(200)71 return { hello: 'world', hostname: req.hostname, port: req.port }72 })73 t.assert.ok(true)74 } catch (e) {75 t.assert.fail()76 }77 78 fastify.listen({ port: 0 }, async err => {79 t.assert.ifError(err)80 t.after(() => { fastify.close() })81 82 sget({83 method: 'GET',84 url: 'http://localhost:' + fastify.server.address().port85 }, (err, response, body) => {86 t.assert.ifError(err)87 t.assert.strictEqual(response.statusCode, 200)88 t.assert.strictEqual(response.headers['content-length'], '' + body.length)89 t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })90 })91 92 sget({93 method: 'GET',94 url: 'http://localhost:' + fastify.server.address().port + '/no-await'95 }, (err, response, body) => {96 t.assert.ifError(err)97 t.assert.strictEqual(response.statusCode, 200)98 t.assert.strictEqual(response.headers['content-length'], '' + body.length)99 t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })100 })101 102 sget({103 method: 'GET',104 url: 'http://localhost:' + fastify.server.address().port + '/await/hostname_port'105 }, (err, response, body) => {106 t.assert.ifError(err)107 t.assert.strictEqual(response.statusCode, 200)108 const parsedBody = JSON.parse(body)109 t.assert.strictEqual(parsedBody.hostname, 'localhost')110 t.assert.strictEqual(parseInt(parsedBody.port), fastify.server.address().port)111 done()112 })113 })114})115 116test('ignore the result of the promise if reply.send is called beforehand (undefined)', (t, done) => {117 t.plan(4)118 119 const server = Fastify()120 const payload = { hello: 'world' }121 122 server.get('/', async function awaitMyFunc (req, reply) {123 await reply.send(payload)124 })125 126 t.after(() => { server.close() })127 128 server.listen({ port: 0 }, (err) => {129 t.assert.ifError(err)130 sget({131 method: 'GET',132 url: 'http://localhost:' + server.server.address().port + '/'133 }, (err, res, body) => {134 t.assert.ifError(err)135 t.assert.deepStrictEqual(payload, JSON.parse(body))136 t.assert.strictEqual(res.statusCode, 200)137 done()138 })139 })140})141 142test('ignore the result of the promise if reply.send is called beforehand (object)', (t, done) => {143 t.plan(4)144 145 const server = Fastify()146 const payload = { hello: 'world2' }147 148 server.get('/', async function awaitMyFunc (req, reply) {149 await reply.send(payload)150 return { hello: 'world' }151 })152 153 t.after(() => { server.close() })154 155 server.listen({ port: 0 }, (err) => {156 t.assert.ifError(err)157 sget({158 method: 'GET',159 url: 'http://localhost:' + server.server.address().port + '/'160 }, (err, res, body) => {161 t.assert.ifError(err)162 t.assert.deepStrictEqual(payload, JSON.parse(body))163 t.assert.strictEqual(res.statusCode, 200)164 done()165 })166 })167})168 169test('server logs an error if reply.send is called and a value is returned via async/await', (t, done) => {170 const lines = ['incoming request', 'request completed', 'Reply was already sent, did you forget to "return reply" in "/" (GET)?']171 t.plan(lines.length + 2)172 173 const splitStream = split(JSON.parse)174 splitStream.on('data', (line) => {175 t.assert.strictEqual(line.msg, lines.shift())176 })177 178 const logger = pino(splitStream)179 180 const fastify = Fastify({181 loggerInstance: logger182 })183 184 fastify.get('/', async (req, reply) => {185 await reply.send({ hello: 'world' })186 return { hello: 'world2' }187 })188 189 fastify.inject({190 method: 'GET',191 url: '/'192 }, (err, res) => {193 t.assert.ifError(err)194 const payload = JSON.parse(res.payload)195 t.assert.deepStrictEqual(payload, { hello: 'world' })196 done()197 })198})199 200test('ignore the result of the promise if reply.send is called beforehand (undefined)', (t, done) => {201 t.plan(4)202 203 const server = Fastify()204 const payload = { hello: 'world' }205 206 server.get('/', async function awaitMyFunc (req, reply) {207 await reply.send(payload)208 })209 210 t.after(() => { server.close() })211 212 server.listen({ port: 0 }, (err) => {213 t.assert.ifError(err)214 sget({215 method: 'GET',216 url: 'http://localhost:' + server.server.address().port + '/'217 }, (err, res, body) => {218 t.assert.ifError(err)219 t.assert.deepStrictEqual(payload, JSON.parse(body))220 t.assert.strictEqual(res.statusCode, 200)221 done()222 })223 })224})225 226test('ignore the result of the promise if reply.send is called beforehand (object)', (t, done) => {227 t.plan(4)228 229 const server = Fastify()230 const payload = { hello: 'world2' }231 232 server.get('/', async function awaitMyFunc (req, reply) {233 await reply.send(payload)234 return { hello: 'world' }235 })236 237 t.after(() => { server.close() })238 239 server.listen({ port: 0 }, (err) => {240 t.assert.ifError(err)241 sget({242 method: 'GET',243 url: 'http://localhost:' + server.server.address().port + '/'244 }, (err, res, body) => {245 t.assert.ifError(err)246 t.assert.deepStrictEqual(payload, JSON.parse(body))247 t.assert.strictEqual(res.statusCode, 200)248 done()249 })250 })251})252 253test('await reply if we will be calling reply.send in the future', (t, done) => {254 const lines = ['incoming request', 'request completed']255 t.plan(lines.length + 2)256 257 const splitStream = split(JSON.parse)258 splitStream.on('data', (line) => {259 t.assert.strictEqual(line.msg, lines.shift())260 })261 262 const server = Fastify({263 logger: {264 stream: splitStream265 }266 })267 const payload = { hello: 'world' }268 269 server.get('/', async function awaitMyFunc (req, reply) {270 setImmediate(function () {271 reply.send(payload)272 })273 274 await reply275 })276 277 server.inject({278 method: 'GET',279 url: '/'280 }, (err, res) => {281 t.assert.ifError(err)282 const payload = JSON.parse(res.payload)283 t.assert.deepStrictEqual(payload, { hello: 'world' })284 done()285 })286})287 288test('await reply if we will be calling reply.send in the future (error case)', (t, done) => {289 const lines = ['incoming request', 'kaboom', 'request completed']290 t.plan(lines.length + 2)291 292 const splitStream = split(JSON.parse)293 splitStream.on('data', (line) => {294 t.assert.strictEqual(line.msg, lines.shift())295 })296 297 const server = Fastify({298 logger: {299 stream: splitStream300 }301 })302 303 server.get('/', async function awaitMyFunc (req, reply) {304 setImmediate(function () {305 reply.send(new Error('kaboom'))306 })307 308 await reply309 })310 311 server.inject({312 method: 'GET',313 url: '/'314 }, (err, res) => {315 t.assert.ifError(err)316 t.assert.strictEqual(res.statusCode, 500)317 done()318 })319})320 321test('support reply decorators with await', (t, done) => {322 t.plan(2)323 324 const fastify = Fastify()325 326 fastify.decorateReply('wow', function () {327 setImmediate(() => {328 this.send({ hello: 'world' })329 })330 331 return this332 })333 334 fastify.get('/', async (req, reply) => {335 await sleep(1)336 await reply.wow()337 })338 339 fastify.inject({340 method: 'GET',341 url: '/'342 }, (err, res) => {343 t.assert.ifError(err)344 const payload = JSON.parse(res.payload)345 t.assert.deepStrictEqual(payload, { hello: 'world' })346 done()347 })348})349 350test('inject async await', async t => {351 t.plan(1)352 353 const fastify = Fastify()354 355 fastify.get('/', (req, reply) => {356 reply.send({ hello: 'world' })357 })358 359 try {360 const res = await fastify.inject({ method: 'GET', url: '/' })361 t.assert.deepStrictEqual({ hello: 'world' }, JSON.parse(res.payload))362 } catch (err) {363 t.assert.fail(err)364 }365})366 367test('inject async await - when the server equal up', async t => {368 t.plan(2)369 370 const fastify = Fastify()371 372 fastify.get('/', (req, reply) => {373 reply.send({ hello: 'world' })374 })375 376 try {377 const res = await fastify.inject({ method: 'GET', url: '/' })378 t.assert.deepStrictEqual({ hello: 'world' }, JSON.parse(res.payload))379 } catch (err) {380 t.assert.fail(err)381 }382 383 await sleep(200)384 385 try {386 const res2 = await fastify.inject({ method: 'GET', url: '/' })387 t.assert.deepStrictEqual({ hello: 'world' }, JSON.parse(res2.payload))388 } catch (err) {389 t.assert.fail(err)390 }391})392 393test('async await plugin', async t => {394 t.plan(1)395 396 const fastify = Fastify()397 398 fastify.register(async (fastify, opts) => {399 fastify.get('/', (req, reply) => {400 reply.send({ hello: 'world' })401 })402 403 await sleep(200)404 })405 406 try {407 const res = await fastify.inject({ method: 'GET', url: '/' })408 t.assert.deepStrictEqual({ hello: 'world' }, JSON.parse(res.payload))409 } catch (err) {410 t.assert.fail(err)411 }412})413 414test('does not call reply.send() twice if 204 response equal already sent', (t, done) => {415 t.plan(2)416 417 const fastify = Fastify()418 419 fastify.get('/', async (req, reply) => {420 reply.code(204).send()421 reply.send = () => {422 throw new Error('reply.send() was called twice')423 }424 })425 426 fastify.inject({427 method: 'GET',428 url: '/'429 }, (err, res) => {430 t.assert.ifError(err)431 t.assert.strictEqual(res.statusCode, 204)432 done()433 })434})435 436test('promise was fulfilled with undefined', (t, done) => {437 t.plan(4)438 439 let fastify = null440 const stream = split(JSON.parse)441 try {442 fastify = Fastify({443 logger: {444 stream,445 level: 'error'446 }447 })448 } catch (e) {449 t.assert.fail()450 }451 452 t.after(() => { fastify.close() })453 454 fastify.get('/', async (req, reply) => {455 })456 457 stream.once('data', line => {458 t.assert.fail('should not log an error')459 })460 461 fastify.listen({ port: 0 }, (err) => {462 t.assert.ifError(err)463 t.after(() => { fastify.close() })464 465 sget({466 method: 'GET',467 url: 'http://localhost:' + fastify.server.address().port + '/'468 }, (err, res, body) => {469 t.assert.ifError(err)470 t.assert.strictEqual(res.body, undefined)471 t.assert.strictEqual(res.statusCode, 200)472 done()473 })474 })475})476 477test('promise was fulfilled with undefined using inject', async (t) => {478 const stream = split(JSON.parse)479 const fastify = Fastify({480 logger: {481 stream,482 level: 'error'483 }484 })485 486 fastify.get('/', async (req, reply) => {487 })488 489 stream.once('data', line => {490 t.assert.fail('should not log an error')491 })492 493 const res = await fastify.inject('/')494 495 t.assert.strictEqual(res.body, '')496 t.assert.strictEqual(res.statusCode, 200)497})498 499test('error is not logged because promise was fulfilled with undefined but response was sent before promise resolution', (t, done) => {500 t.plan(4)501 502 let fastify = null503 const stream = split(JSON.parse)504 const payload = { hello: 'world' }505 try {506 fastify = Fastify({507 logger: {508 stream,509 level: 'error'510 }511 })512 } catch (e) {513 t.assert.fail()514 }515 516 t.after(() => { fastify.close() })517 518 fastify.get('/', async (req, reply) => {519 reply.send(payload)520 })521 522 stream.once('data', line => {523 t.assert.fail('should not log an error')524 })525 526 fastify.listen({ port: 0 }, (err) => {527 t.assert.ifError(err)528 t.after(() => { fastify.close() })529 530 sget({531 method: 'GET',532 url: 'http://localhost:' + fastify.server.address().port + '/'533 }, (err, res, body) => {534 t.assert.ifError(err)535 t.assert.strictEqual(res.statusCode, 200)536 t.assert.deepStrictEqual(537 payload,538 JSON.parse(body)539 )540 done()541 })542 })543})544 545test('Thrown Error instance sets HTTP status code', (t, done) => {546 t.plan(3)547 548 const fastify = Fastify()549 550 const err = new Error('winter is coming')551 err.statusCode = 418552 553 fastify.get('/', async (req, reply) => {554 throw err555 })556 557 fastify.inject({558 method: 'GET',559 url: '/'560 }, (error, res) => {561 t.assert.ifError(error)562 t.assert.strictEqual(res.statusCode, 418)563 t.assert.deepStrictEqual(564 {565 error: statusCodes['418'],566 message: err.message,567 statusCode: 418568 },569 JSON.parse(res.payload)570 )571 done()572 })573})574 575test('customErrorHandler support', (t, done) => {576 t.plan(4)577 578 const fastify = Fastify()579 580 fastify.get('/', async (req, reply) => {581 const error = new Error('ouch')582 error.statusCode = 400583 throw error584 })585 586 fastify.setErrorHandler(async err => {587 t.assert.strictEqual(err.message, 'ouch')588 const error = new Error('kaboom')589 error.statusCode = 401590 throw error591 })592 593 fastify.inject({594 method: 'GET',595 url: '/'596 }, (err, res) => {597 t.assert.ifError(err)598 t.assert.strictEqual(res.statusCode, 401)599 t.assert.deepStrictEqual(600 {601 error: statusCodes['401'],602 message: 'kaboom',603 statusCode: 401604 },605 JSON.parse(res.payload)606 )607 done()608 })609})610 611test('customErrorHandler support without throwing', (t, done) => {612 t.plan(4)613 614 const fastify = Fastify()615 616 fastify.get('/', async (req, reply) => {617 const error = new Error('ouch')618 error.statusCode = 400619 throw error620 })621 622 fastify.setErrorHandler(async (err, req, reply) => {623 t.assert.strictEqual(err.message, 'ouch')624 await reply.code(401).send('kaboom')625 reply.send = t.assert.fail.bind(t, 'should not be called')626 })627 628 fastify.inject({629 method: 'GET',630 url: '/'631 }, (err, res) => {632 t.assert.ifError(err)633 t.assert.strictEqual(res.statusCode, 401)634 t.assert.deepStrictEqual(635 'kaboom',636 res.payload637 )638 done()639 })640})641 642// See https://github.com/fastify/fastify/issues/2653643test('customErrorHandler only called if reply not already sent', (t, done) => {644 t.plan(3)645 646 const fastify = Fastify()647 648 fastify.get('/', async (req, reply) => {649 await reply.send('success')650 const error = new Error('ouch')651 error.statusCode = 400652 throw error653 })654 655 fastify.setErrorHandler(t.assert.fail.bind(t, 'should not be called'))656 657 fastify.inject({658 method: 'GET',659 url: '/'660 }, (err, res) => {661 t.assert.ifError(err)662 t.assert.strictEqual(res.statusCode, 200)663 t.assert.deepStrictEqual(664 'success',665 res.payload666 )667 done()668 })669})670 671// See https://github.com/fastify/fastify/issues/3209672test('setNotFoundHandler should accept return value', (t, done) => {673 t.plan(3)674 675 const fastify = Fastify()676 677 fastify.get('/', async () => ({ hello: 'world' }))678 679 fastify.setNotFoundHandler((req, reply) => {680 reply.code(404)681 return {682 error: statusCodes['404'],683 message: 'lost',684 statusCode: 404685 }686 })687 688 fastify.inject({689 method: 'GET',690 url: '/elsewhere'691 }, (err, res) => {692 t.assert.ifError(err)693 t.assert.strictEqual(res.statusCode, 404)694 t.assert.deepStrictEqual(695 {696 error: statusCodes['404'],697 message: 'lost',698 statusCode: 404699 },700 JSON.parse(res.payload)701 )702 done()703 })704})705 706// See https://github.com/fastify/fastify/issues/3209707test('customErrorHandler should accept return value', (t, done) => {708 t.plan(4)709 710 const fastify = Fastify()711 712 fastify.get('/', async (req, reply) => {713 const error = new Error('ouch')714 error.statusCode = 400715 throw error716 })717 718 fastify.setErrorHandler((err, req, reply) => {719 t.assert.strictEqual(err.message, 'ouch')720 reply.code(401)721 return {722 error: statusCodes['401'],723 message: 'kaboom',724 statusCode: 401725 }726 })727 728 fastify.inject({729 method: 'GET',730 url: '/'731 }, (err, res) => {732 t.assert.ifError(err)733 t.assert.strictEqual(res.statusCode, 401)734 t.assert.deepStrictEqual(735 {736 error: statusCodes['401'],737 message: 'kaboom',738 statusCode: 401739 },740 JSON.parse(res.payload)741 )742 done()743 })744})745 746test('await self', async t => {747 const app = Fastify()748 t.assert.strictEqual(await app, app)749})750 