strong-tie/inbound-calls
0
1'use strict'2 3const t = require('tap')4const test = t.test5const sget = require('simple-get').concat6const stream = require('node:stream')7const Fastify = require('..')8const fp = require('fastify-plugin')9const fs = require('node:fs')10const split = require('split2')11const symbols = require('../lib/symbols.js')12const payload = { hello: 'world' }13const proxyquire = require('proxyquire')14const { connect } = require('node:net')15const { sleep, getServerUrl } = require('./helper')16 17process.removeAllListeners('warning')18 19test('hooks', t => {20 t.plan(49)21 const fastify = Fastify({ exposeHeadRoutes: false })22 23 try {24 fastify.addHook('preHandler', function (request, reply, done) {25 t.equal(request.test, 'the request is coming')26 t.equal(reply.test, 'the reply has come')27 if (request.raw.method === 'HEAD') {28 done(new Error('some error'))29 } else {30 done()31 }32 })33 t.pass()34 } catch (e) {35 t.fail()36 }37 38 try {39 fastify.addHook('preHandler', null)40 } catch (e) {41 t.equal(e.code, 'FST_ERR_HOOK_INVALID_HANDLER')42 t.equal(e.message, 'preHandler hook should be a function, instead got null')43 t.pass()44 }45 46 try {47 fastify.addHook('preParsing')48 } catch (e) {49 t.equal(e.code, 'FST_ERR_HOOK_INVALID_HANDLER')50 t.equal(e.message, 'preParsing hook should be a function, instead got undefined')51 t.pass()52 }53 54 try {55 fastify.addHook('preParsing', function (request, reply, payload, done) {56 request.preParsing = true57 t.equal(request.test, 'the request is coming')58 t.equal(reply.test, 'the reply has come')59 done()60 })61 t.pass()62 } catch (e) {63 t.fail()64 }65 66 try {67 fastify.addHook('preParsing', function (request, reply, payload, done) {68 request.preParsing = true69 t.equal(request.test, 'the request is coming')70 t.equal(reply.test, 'the reply has come')71 done()72 })73 t.pass()74 } catch (e) {75 t.fail()76 }77 78 try {79 fastify.addHook('preValidation', function (request, reply, done) {80 t.equal(request.preParsing, true)81 t.equal(request.test, 'the request is coming')82 t.equal(reply.test, 'the reply has come')83 done()84 })85 t.pass()86 } catch (e) {87 t.fail()88 }89 90 try {91 fastify.addHook('preSerialization', function (request, reply, payload, done) {92 t.ok('preSerialization called')93 done()94 })95 t.pass()96 } catch (e) {97 t.fail()98 }99 100 try {101 fastify.addHook('onRequest', function (request, reply, done) {102 request.test = 'the request is coming'103 reply.test = 'the reply has come'104 if (request.raw.method === 'DELETE') {105 done(new Error('some error'))106 } else {107 done()108 }109 })110 t.pass()111 } catch (e) {112 t.fail()113 }114 115 fastify.addHook('onResponse', function (request, reply, done) {116 t.ok('onResponse called')117 done()118 })119 120 fastify.addHook('onSend', function (req, reply, thePayload, done) {121 t.ok('onSend called')122 done()123 })124 125 fastify.route({126 method: 'GET',127 url: '/',128 handler: function (req, reply) {129 t.equal(req.test, 'the request is coming')130 t.equal(reply.test, 'the reply has come')131 reply.code(200).send(payload)132 },133 onResponse: function (req, reply, done) {134 t.ok('onResponse inside hook')135 },136 response: {137 200: {138 type: 'object'139 }140 }141 })142 143 fastify.head('/', function (req, reply) {144 reply.code(200).send(payload)145 })146 147 fastify.delete('/', function (req, reply) {148 reply.code(200).send(payload)149 })150 151 fastify.listen({ port: 0 }, err => {152 t.error(err)153 t.teardown(() => { fastify.close() })154 155 sget({156 method: 'GET',157 url: 'http://127.0.0.1:' + fastify.server.address().port158 }, (err, response, body) => {159 t.error(err)160 t.equal(response.statusCode, 200)161 t.equal(response.headers['content-length'], '' + body.length)162 t.same(JSON.parse(body), { hello: 'world' })163 })164 165 sget({166 method: 'HEAD',167 url: 'http://127.0.0.1:' + fastify.server.address().port168 }, (err, response, body) => {169 t.error(err)170 t.equal(response.statusCode, 500)171 })172 173 sget({174 method: 'DELETE',175 url: 'http://127.0.0.1:' + fastify.server.address().port176 }, (err, response, body) => {177 t.error(err)178 t.equal(response.statusCode, 500)179 })180 })181})182 183test('onRequest hook should support encapsulation / 1', t => {184 t.plan(5)185 const fastify = Fastify()186 187 fastify.register((instance, opts, done) => {188 instance.addHook('onRequest', (req, reply, done) => {189 t.equal(req.raw.url, '/plugin')190 done()191 })192 193 instance.get('/plugin', (request, reply) => {194 reply.send()195 })196 197 done()198 })199 200 fastify.get('/root', (request, reply) => {201 reply.send()202 })203 204 fastify.inject('/root', (err, res) => {205 t.error(err)206 t.equal(res.statusCode, 200)207 })208 209 fastify.inject('/plugin', (err, res) => {210 t.error(err)211 t.equal(res.statusCode, 200)212 })213})214 215test('onRequest hook should support encapsulation / 2', (t) => {216 t.plan(3)217 const fastify = Fastify()218 let pluginInstance219 220 fastify.addHook('onRequest', () => {})221 222 fastify.register((instance, opts, done) => {223 instance.addHook('onRequest', () => {})224 pluginInstance = instance225 done()226 })227 228 fastify.ready(err => {229 t.error(err)230 t.equal(fastify[symbols.kHooks].onRequest.length, 1)231 t.equal(pluginInstance[symbols.kHooks].onRequest.length, 2)232 })233})234 235test('onRequest hook should support encapsulation / 3', t => {236 t.plan(20)237 const fastify = Fastify()238 fastify.decorate('hello', 'world')239 240 fastify.addHook('onRequest', function (req, reply, done) {241 t.ok(this.hello)242 t.ok(this.hello2)243 req.first = true244 done()245 })246 247 fastify.decorate('hello2', 'world')248 249 fastify.get('/first', (req, reply) => {250 t.ok(req.first)251 t.notOk(req.second)252 reply.send({ hello: 'world' })253 })254 255 fastify.register((instance, opts, done) => {256 instance.decorate('hello3', 'world')257 instance.addHook('onRequest', function (req, reply, done) {258 t.ok(this.hello)259 t.ok(this.hello2)260 t.ok(this.hello3)261 req.second = true262 done()263 })264 265 instance.get('/second', (req, reply) => {266 t.ok(req.first)267 t.ok(req.second)268 reply.send({ hello: 'world' })269 })270 271 done()272 })273 274 fastify.listen({ port: 0 }, err => {275 t.error(err)276 t.teardown(() => { fastify.close() })277 278 sget({279 method: 'GET',280 url: 'http://127.0.0.1:' + fastify.server.address().port + '/first'281 }, (err, response, body) => {282 t.error(err)283 t.equal(response.statusCode, 200)284 t.equal(response.headers['content-length'], '' + body.length)285 t.same(JSON.parse(body), { hello: 'world' })286 })287 288 sget({289 method: 'GET',290 url: 'http://127.0.0.1:' + fastify.server.address().port + '/second'291 }, (err, response, body) => {292 t.error(err)293 t.equal(response.statusCode, 200)294 t.equal(response.headers['content-length'], '' + body.length)295 t.same(JSON.parse(body), { hello: 'world' })296 })297 })298})299 300test('preHandler hook should support encapsulation / 5', t => {301 t.plan(17)302 const fastify = Fastify()303 t.teardown(() => { fastify.close() })304 fastify.decorate('hello', 'world')305 306 fastify.addHook('preHandler', function (req, res, done) {307 t.ok(this.hello)308 req.first = true309 done()310 })311 312 fastify.get('/first', (req, reply) => {313 t.ok(req.first)314 t.notOk(req.second)315 reply.send({ hello: 'world' })316 })317 318 fastify.register((instance, opts, done) => {319 instance.decorate('hello2', 'world')320 instance.addHook('preHandler', function (req, res, done) {321 t.ok(this.hello)322 t.ok(this.hello2)323 req.second = true324 done()325 })326 327 instance.get('/second', (req, reply) => {328 t.ok(req.first)329 t.ok(req.second)330 reply.send({ hello: 'world' })331 })332 333 done()334 })335 336 fastify.listen({ port: 0 }, err => {337 t.error(err)338 339 sget({340 method: 'GET',341 url: 'http://127.0.0.1:' + fastify.server.address().port + '/first'342 }, (err, response, body) => {343 t.error(err)344 t.equal(response.statusCode, 200)345 t.equal(response.headers['content-length'], '' + body.length)346 t.same(JSON.parse(body), { hello: 'world' })347 })348 349 sget({350 method: 'GET',351 url: 'http://127.0.0.1:' + fastify.server.address().port + '/second'352 }, (err, response, body) => {353 t.error(err)354 t.equal(response.statusCode, 200)355 t.equal(response.headers['content-length'], '' + body.length)356 t.same(JSON.parse(body), { hello: 'world' })357 })358 })359})360 361test('onRoute hook should be called / 1', t => {362 t.plan(2)363 const fastify = Fastify({ exposeHeadRoutes: false })364 365 fastify.register((instance, opts, done) => {366 instance.addHook('onRoute', () => {367 t.pass()368 })369 instance.get('/', opts, function (req, reply) {370 reply.send()371 })372 done()373 })374 375 fastify.ready(err => {376 t.error(err)377 })378})379 380test('onRoute hook should be called / 2', t => {381 t.plan(5)382 let firstHandler = 0383 let secondHandler = 0384 const fastify = Fastify({ exposeHeadRoutes: false })385 fastify.addHook('onRoute', (route) => {386 t.pass()387 firstHandler++388 })389 390 fastify.register((instance, opts, done) => {391 instance.addHook('onRoute', (route) => {392 t.pass()393 secondHandler++394 })395 instance.get('/', opts, function (req, reply) {396 reply.send()397 })398 done()399 })400 .after(() => {401 t.equal(firstHandler, 1)402 t.equal(secondHandler, 1)403 })404 405 fastify.ready(err => {406 t.error(err)407 })408})409 410test('onRoute hook should be called / 3', t => {411 t.plan(5)412 const fastify = Fastify({ exposeHeadRoutes: false })413 414 function handler (req, reply) {415 reply.send()416 }417 418 fastify.addHook('onRoute', (route) => {419 t.pass()420 })421 422 fastify.register((instance, opts, done) => {423 instance.addHook('onRoute', (route) => {424 t.pass()425 })426 instance.get('/a', handler)427 done()428 })429 .after((err, done) => {430 t.error(err)431 setTimeout(() => {432 fastify.get('/b', handler)433 done()434 }, 10)435 })436 437 fastify.ready(err => {438 t.error(err)439 })440})441 442test('onRoute hook should be called (encapsulation support) / 4', t => {443 t.plan(4)444 const fastify = Fastify({ exposeHeadRoutes: false })445 446 fastify.addHook('onRoute', () => {447 t.pass()448 })449 450 fastify.register((instance, opts, done) => {451 instance.addHook('onRoute', () => {452 t.pass()453 })454 instance.get('/nested', opts, function (req, reply) {455 reply.send()456 })457 done()458 })459 460 fastify.get('/', function (req, reply) {461 reply.send()462 })463 464 fastify.ready(err => {465 t.error(err)466 })467})468 469test('onRoute hook should be called (encapsulation support) / 5', t => {470 t.plan(2)471 const fastify = Fastify({ exposeHeadRoutes: false })472 473 fastify.get('/first', function (req, reply) {474 reply.send()475 })476 477 fastify.register((instance, opts, done) => {478 instance.addHook('onRoute', () => {479 t.pass()480 })481 instance.get('/nested', opts, function (req, reply) {482 reply.send()483 })484 done()485 })486 487 fastify.get('/second', function (req, reply) {488 reply.send()489 })490 491 fastify.ready(err => {492 t.error(err)493 })494})495 496test('onRoute hook should be called (encapsulation support) / 6', t => {497 t.plan(1)498 const fastify = Fastify({ exposeHeadRoutes: false })499 500 fastify.get('/first', function (req, reply) {501 reply.send()502 })503 504 fastify.addHook('onRoute', () => {505 t.fail('This should not be called')506 })507 508 fastify.ready(err => {509 t.error(err)510 })511})512 513test('onRoute should keep the context', t => {514 t.plan(4)515 const fastify = Fastify({ exposeHeadRoutes: false })516 fastify.register((instance, opts, done) => {517 instance.decorate('test', true)518 instance.addHook('onRoute', onRoute)519 t.ok(instance.prototype === fastify.prototype)520 521 function onRoute (route) {522 t.ok(this.test)523 t.equal(this, instance)524 }525 526 instance.get('/', opts, function (req, reply) {527 reply.send()528 })529 530 done()531 })532 533 fastify.close((err) => {534 t.error(err)535 })536})537 538test('onRoute hook should pass correct route', t => {539 t.plan(9)540 const fastify = Fastify({ exposeHeadRoutes: false })541 fastify.addHook('onRoute', (route) => {542 t.equal(route.method, 'GET')543 t.equal(route.url, '/')544 t.equal(route.path, '/')545 t.equal(route.routePath, '/')546 })547 548 fastify.register((instance, opts, done) => {549 instance.addHook('onRoute', (route) => {550 t.equal(route.method, 'GET')551 t.equal(route.url, '/')552 t.equal(route.path, '/')553 t.equal(route.routePath, '/')554 })555 instance.get('/', opts, function (req, reply) {556 reply.send()557 })558 done()559 })560 561 fastify.ready(err => {562 t.error(err)563 })564})565 566test('onRoute hook should pass correct route with custom prefix', t => {567 t.plan(11)568 const fastify = Fastify({ exposeHeadRoutes: false })569 fastify.addHook('onRoute', function (route) {570 t.equal(route.method, 'GET')571 t.equal(route.url, '/v1/foo')572 t.equal(route.path, '/v1/foo')573 t.equal(route.routePath, '/foo')574 t.equal(route.prefix, '/v1')575 })576 577 fastify.register((instance, opts, done) => {578 instance.addHook('onRoute', function (route) {579 t.equal(route.method, 'GET')580 t.equal(route.url, '/v1/foo')581 t.equal(route.path, '/v1/foo')582 t.equal(route.routePath, '/foo')583 t.equal(route.prefix, '/v1')584 })585 instance.get('/foo', opts, function (req, reply) {586 reply.send()587 })588 done()589 }, { prefix: '/v1' })590 591 fastify.ready(err => {592 t.error(err)593 })594})595 596test('onRoute hook should pass correct route with custom options', t => {597 t.plan(6)598 const fastify = Fastify({ exposeHeadRoutes: false })599 fastify.register((instance, opts, done) => {600 instance.addHook('onRoute', function (route) {601 t.equal(route.method, 'GET')602 t.equal(route.url, '/foo')603 t.equal(route.logLevel, 'info')604 t.equal(route.bodyLimit, 100)605 t.type(route.logSerializers.test, 'function')606 })607 instance.get('/foo', {608 logLevel: 'info',609 bodyLimit: 100,610 logSerializers: {611 test: value => value612 }613 }, function (req, reply) {614 reply.send()615 })616 done()617 })618 619 fastify.ready(err => {620 t.error(err)621 })622})623 624test('onRoute hook should receive any route option', t => {625 t.plan(5)626 const fastify = Fastify({ exposeHeadRoutes: false })627 fastify.register((instance, opts, done) => {628 instance.addHook('onRoute', function (route) {629 t.equal(route.method, 'GET')630 t.equal(route.url, '/foo')631 t.equal(route.routePath, '/foo')632 t.equal(route.auth, 'basic')633 })634 instance.get('/foo', { auth: 'basic' }, function (req, reply) {635 reply.send()636 })637 done()638 })639 640 fastify.ready(err => {641 t.error(err)642 })643})644 645test('onRoute hook should preserve system route configuration', t => {646 t.plan(5)647 const fastify = Fastify({ exposeHeadRoutes: false })648 fastify.register((instance, opts, done) => {649 instance.addHook('onRoute', function (route) {650 t.equal(route.method, 'GET')651 t.equal(route.url, '/foo')652 t.equal(route.routePath, '/foo')653 t.equal(route.handler.length, 2)654 })655 instance.get('/foo', { url: '/bar', method: 'POST' }, function (req, reply) {656 reply.send()657 })658 done()659 })660 661 fastify.ready(err => {662 t.error(err)663 })664})665 666test('onRoute hook should preserve handler function in options of shorthand route system configuration', t => {667 t.plan(2)668 669 const handler = (req, reply) => {}670 671 const fastify = Fastify({ exposeHeadRoutes: false })672 fastify.register((instance, opts, done) => {673 instance.addHook('onRoute', function (route) {674 t.equal(route.handler, handler)675 })676 instance.get('/foo', { handler })677 done()678 })679 680 fastify.ready(err => {681 t.error(err)682 })683})684 685// issue ref https://github.com/fastify/fastify-compress/issues/140686test('onRoute hook should be called once when prefixTrailingSlash', t => {687 t.plan(3)688 689 let onRouteCalled = 0690 let routePatched = 0691 692 const fastify = Fastify({ ignoreTrailingSlash: false, exposeHeadRoutes: false })693 694 // a plugin that patches route options, similar to fastify-compress695 fastify.register(fp(function myPlugin (instance, opts, next) {696 function patchTheRoute () {697 routePatched++698 }699 700 instance.addHook('onRoute', function (routeOptions) {701 onRouteCalled++702 patchTheRoute(routeOptions)703 })704 705 next()706 }))707 708 fastify.register(function routes (instance, opts, next) {709 instance.route({710 method: 'GET',711 url: '/',712 prefixTrailingSlash: 'both',713 handler: (req, reply) => {714 reply.send({ hello: 'world' })715 }716 })717 718 next()719 }, { prefix: '/prefix' })720 721 fastify.ready(err => {722 t.error(err)723 t.equal(onRouteCalled, 1) // onRoute hook was called once724 t.equal(routePatched, 1) // and plugin acted once and avoided redundant route patching725 })726})727 728test('onRoute hook should able to change the route url', t => {729 t.plan(5)730 731 const fastify = Fastify({ exposeHeadRoutes: false })732 t.teardown(() => { fastify.close() })733 734 fastify.register((instance, opts, done) => {735 instance.addHook('onRoute', (route) => {736 t.equal(route.url, '/foo')737 route.url = encodeURI(route.url)738 })739 740 instance.get('/foo', (request, reply) => {741 reply.send('here /foo')742 })743 744 done()745 })746 747 fastify.listen({ port: 0 }, err => {748 t.error(err)749 750 sget({751 method: 'GET',752 url: getServerUrl(fastify) + encodeURI('/foo')753 }, (err, response, body) => {754 t.error(err)755 t.equal(response.statusCode, 200)756 t.equal(body.toString(), 'here /foo')757 })758 })759})760 761test('onRoute hook that throws should be caught', t => {762 t.plan(1)763 const fastify = Fastify({ exposeHeadRoutes: false })764 765 fastify.register((instance, opts, done) => {766 instance.addHook('onRoute', () => {767 throw new Error('snap')768 })769 770 try {771 instance.get('/', opts, function (req, reply) {772 reply.send()773 })774 775 t.fail('onRoute should throw sync if error')776 } catch (error) {777 t.ok(error)778 }779 780 done()781 })782 783 fastify.ready()784})785 786test('onRoute hook with many prefix', t => {787 t.plan(3)788 const fastify = Fastify({ exposeHeadRoutes: false })789 const handler = (req, reply) => { reply.send({}) }790 791 const onRouteChecks = [792 { routePath: '/anotherPath', prefix: '/two', url: '/one/two/anotherPath' },793 { routePath: '/aPath', prefix: '/one', url: '/one/aPath' }794 ]795 796 fastify.register((instance, opts, done) => {797 instance.addHook('onRoute', (route) => {798 t.match(route, onRouteChecks.pop())799 })800 instance.route({ method: 'GET', url: '/aPath', handler })801 802 instance.register((instance, opts, done) => {803 instance.route({ method: 'GET', path: '/anotherPath', handler })804 done()805 }, { prefix: '/two' })806 done()807 }, { prefix: '/one' })808 809 fastify.ready(err => { t.error(err) })810})811 812test('onRoute hook should not be called when it registered after route', t => {813 t.plan(3)814 const fastify = Fastify()815 816 fastify.addHook('onRoute', () => {817 t.pass()818 })819 820 fastify.get('/', function (req, reply) {821 reply.send()822 })823 824 fastify.addHook('onRoute', () => {825 t.fail('should not be called')826 })827 828 fastify.ready(err => {829 t.error(err)830 })831})832 833test('onResponse hook should log request error', t => {834 t.plan(4)835 836 let fastify = null837 const logStream = split(JSON.parse)838 try {839 fastify = Fastify({840 logger: {841 stream: logStream,842 level: 'error'843 }844 })845 } catch (e) {846 t.fail()847 }848 849 logStream.once('data', line => {850 t.equal(line.msg, 'request errored')851 t.equal(line.level, 50)852 })853 854 fastify.addHook('onResponse', (request, reply, done) => {855 done(new Error('kaboom'))856 })857 858 fastify.get('/root', (request, reply) => {859 reply.send()860 })861 862 fastify.inject('/root', (err, res) => {863 t.error(err)864 t.equal(res.statusCode, 200)865 })866})867 868test('onResponse hook should support encapsulation / 1', t => {869 t.plan(5)870 const fastify = Fastify()871 872 fastify.register((instance, opts, done) => {873 instance.addHook('onResponse', (request, reply, done) => {874 t.equal(reply.plugin, true)875 done()876 })877 878 instance.get('/plugin', (request, reply) => {879 reply.plugin = true880 reply.send()881 })882 883 done()884 })885 886 fastify.get('/root', (request, reply) => {887 reply.send()888 })889 890 fastify.inject('/root', (err, res) => {891 t.error(err)892 t.equal(res.statusCode, 200)893 })894 895 fastify.inject('/plugin', (err, res) => {896 t.error(err)897 t.equal(res.statusCode, 200)898 })899})900 901test('onResponse hook should support encapsulation / 2', t => {902 t.plan(3)903 const fastify = Fastify()904 let pluginInstance905 906 fastify.addHook('onResponse', () => {})907 908 fastify.register((instance, opts, done) => {909 instance.addHook('onResponse', () => {})910 pluginInstance = instance911 done()912 })913 914 fastify.ready(err => {915 t.error(err)916 t.equal(fastify[symbols.kHooks].onResponse.length, 1)917 t.equal(pluginInstance[symbols.kHooks].onResponse.length, 2)918 })919})920 921test('onResponse hook should support encapsulation / 3', t => {922 t.plan(16)923 const fastify = Fastify()924 t.teardown(() => { fastify.close() })925 fastify.decorate('hello', 'world')926 927 fastify.addHook('onResponse', function (request, reply, done) {928 t.ok(this.hello)929 t.ok('onResponse called')930 done()931 })932 933 fastify.get('/first', (req, reply) => {934 reply.send({ hello: 'world' })935 })936 937 fastify.register((instance, opts, done) => {938 instance.decorate('hello2', 'world')939 instance.addHook('onResponse', function (request, reply, done) {940 t.ok(this.hello)941 t.ok(this.hello2)942 t.ok('onResponse called')943 done()944 })945 946 instance.get('/second', (req, reply) => {947 reply.send({ hello: 'world' })948 })949 950 done()951 })952 953 fastify.listen({ port: 0 }, err => {954 t.error(err)955 956 sget({957 method: 'GET',958 url: 'http://127.0.0.1:' + fastify.server.address().port + '/first'959 }, (err, response, body) => {960 t.error(err)961 t.equal(response.statusCode, 200)962 t.equal(response.headers['content-length'], '' + body.length)963 t.same(JSON.parse(body), { hello: 'world' })964 })965 966 sget({967 method: 'GET',968 url: 'http://127.0.0.1:' + fastify.server.address().port + '/second'969 }, (err, response, body) => {970 t.error(err)971 t.equal(response.statusCode, 200)972 t.equal(response.headers['content-length'], '' + body.length)973 t.same(JSON.parse(body), { hello: 'world' })974 })975 })976})977 978test('onSend hook should support encapsulation / 1', t => {979 t.plan(3)980 const fastify = Fastify()981 let pluginInstance982 983 fastify.addHook('onSend', () => {})984 985 fastify.register((instance, opts, done) => {986 instance.addHook('onSend', () => {})987 pluginInstance = instance988 done()989 })990 991 fastify.ready(err => {992 t.error(err)993 t.equal(fastify[symbols.kHooks].onSend.length, 1)994 t.equal(pluginInstance[symbols.kHooks].onSend.length, 2)995 })996})997 998test('onSend hook should support encapsulation / 2', t => {999 t.plan(16)1000 const fastify = Fastify()1001 t.teardown(() => { fastify.close() })1002 fastify.decorate('hello', 'world')1003 1004 fastify.addHook('onSend', function (request, reply, thePayload, done) {1005 t.ok(this.hello)1006 t.ok('onSend called')1007 done()1008 })1009 1010 fastify.get('/first', (req, reply) => {1011 reply.send({ hello: 'world' })1012 })1013 1014 fastify.register((instance, opts, done) => {1015 instance.decorate('hello2', 'world')1016 instance.addHook('onSend', function (request, reply, thePayload, done) {1017 t.ok(this.hello)1018 t.ok(this.hello2)1019 t.ok('onSend called')1020 done()1021 })1022 1023 instance.get('/second', (req, reply) => {1024 reply.send({ hello: 'world' })1025 })1026 1027 done()1028 })1029 1030 fastify.listen({ port: 0 }, err => {1031 t.error(err)1032 1033 sget({1034 method: 'GET',1035 url: 'http://127.0.0.1:' + fastify.server.address().port + '/first'1036 }, (err, response, body) => {1037 t.error(err)1038 t.equal(response.statusCode, 200)1039 t.equal(response.headers['content-length'], '' + body.length)1040 t.same(JSON.parse(body), { hello: 'world' })1041 })1042 1043 sget({1044 method: 'GET',1045 url: 'http://127.0.0.1:' + fastify.server.address().port + '/second'1046 }, (err, response, body) => {1047 t.error(err)1048 t.equal(response.statusCode, 200)1049 t.equal(response.headers['content-length'], '' + body.length)1050 t.same(JSON.parse(body), { hello: 'world' })1051 })1052 })1053})1054 1055test('onSend hook is called after payload is serialized and headers are set', t => {1056 t.plan(30)1057 const fastify = Fastify()1058 1059 fastify.register((instance, opts, done) => {1060 const thePayload = { hello: 'world' }1061 1062 instance.addHook('onSend', function (request, reply, payload, done) {1063 t.same(JSON.parse(payload), thePayload)1064 t.equal(reply[symbols.kReplyHeaders]['content-type'], 'application/json; charset=utf-8')1065 done()1066 })1067 1068 instance.get('/json', (request, reply) => {1069 reply.send(thePayload)1070 })1071 1072 done()1073 })1074 1075 fastify.register((instance, opts, done) => {1076 instance.addHook('onSend', function (request, reply, payload, done) {1077 t.equal(payload, 'some text')1078 t.equal(reply[symbols.kReplyHeaders]['content-type'], 'text/plain; charset=utf-8')1079 done()1080 })1081 1082 instance.get('/text', (request, reply) => {1083 reply.send('some text')1084 })1085 1086 done()1087 })1088 1089 fastify.register((instance, opts, done) => {1090 const thePayload = Buffer.from('buffer payload')1091 1092 instance.addHook('onSend', function (request, reply, payload, done) {1093 t.equal(payload, thePayload)1094 t.equal(reply[symbols.kReplyHeaders]['content-type'], 'application/octet-stream')1095 done()1096 })1097 1098 instance.get('/buffer', (request, reply) => {1099 reply.send(thePayload)1100 })1101 1102 done()1103 })1104 1105 fastify.register((instance, opts, done) => {1106 let chunk = 'stream payload'1107 const thePayload = new stream.Readable({1108 read () {1109 this.push(chunk)1110 chunk = null1111 }1112 })1113 1114 instance.addHook('onSend', function (request, reply, payload, done) {1115 t.equal(payload, thePayload)1116 t.equal(reply[symbols.kReplyHeaders]['content-type'], 'application/octet-stream')1117 done()1118 })1119 1120 instance.get('/stream', (request, reply) => {1121 reply.header('content-type', 'application/octet-stream')1122 reply.send(thePayload)1123 })1124 1125 done()1126 })1127 1128 fastify.register((instance, opts, done) => {1129 const serializedPayload = 'serialized'1130 1131 instance.addHook('onSend', function (request, reply, payload, done) {1132 t.equal(payload, serializedPayload)1133 t.equal(reply[symbols.kReplyHeaders]['content-type'], 'text/custom')1134 done()1135 })1136 1137 instance.get('/custom-serializer', (request, reply) => {1138 reply1139 .serializer(() => serializedPayload)1140 .type('text/custom')1141 .send('needs to be serialized')1142 })1143 1144 done()1145 })1146 1147 fastify.inject({1148 method: 'GET',1149 url: '/json'1150 }, (err, res) => {1151 t.error(err)1152 t.equal(res.statusCode, 200)1153 t.same(JSON.parse(res.payload), { hello: 'world' })1154 t.equal(res.headers['content-length'], '17')1155 })1156 1157 fastify.inject({1158 method: 'GET',1159 url: '/text'1160 }, (err, res) => {1161 t.error(err)1162 t.equal(res.statusCode, 200)1163 t.same(res.payload, 'some text')1164 t.equal(res.headers['content-length'], '9')1165 })1166 1167 fastify.inject({1168 method: 'GET',1169 url: '/buffer'1170 }, (err, res) => {1171 t.error(err)1172 t.equal(res.statusCode, 200)1173 t.same(res.payload, 'buffer payload')1174 t.equal(res.headers['content-length'], '14')1175 })1176 1177 fastify.inject({1178 method: 'GET',1179 url: '/stream'1180 }, (err, res) => {1181 t.error(err)1182 t.equal(res.statusCode, 200)1183 t.same(res.payload, 'stream payload')1184 t.equal(res.headers['transfer-encoding'], 'chunked')1185 })1186 1187 fastify.inject({1188 method: 'GET',1189 url: '/custom-serializer'1190 }, (err, res) => {1191 t.error(err)1192 t.equal(res.statusCode, 200)1193 t.same(res.payload, 'serialized')1194 t.equal(res.headers['content-type'], 'text/custom')1195 })1196})1197 1198test('modify payload', t => {1199 t.plan(10)1200 const fastify = Fastify()