strong-tie/inbound-calls
0
1'use strict'2 3const t = require('tap')4const Fastify = require('../fastify')5const immediate = require('node:util').promisify(setImmediate)6 7t.test('onReady should be called in order', t => {8 t.plan(7)9 const fastify = Fastify()10 11 let order = 012 13 fastify.addHook('onReady', function (done) {14 t.equal(order++, 0, 'called in root')15 t.equal(this.pluginName, fastify.pluginName, 'the this binding is the right instance')16 done()17 })18 19 fastify.register(async (childOne, o) => {20 childOne.addHook('onReady', function (done) {21 t.equal(order++, 1, 'called in childOne')22 t.equal(this.pluginName, childOne.pluginName, 'the this binding is the right instance')23 done()24 })25 26 childOne.register(async (childTwo, o) => {27 childTwo.addHook('onReady', async function () {28 await immediate()29 t.equal(order++, 2, 'called in childTwo')30 t.equal(this.pluginName, childTwo.pluginName, 'the this binding is the right instance')31 })32 })33 })34 35 fastify.ready(err => t.error(err))36})37 38t.test('onReady should be called once', async (t) => {39 const app = Fastify()40 let counter = 041 42 app.addHook('onReady', async function () {43 counter++44 })45 46 const promises = [1, 2, 3, 4, 5].map((id) => app.ready().then(() => id))47 48 const result = await Promise.race(promises)49 50 t.strictSame(result, 1, 'Should resolve in order')51 t.equal(counter, 1, 'Should call onReady only once')52})53 54t.test('async onReady should be called in order', async t => {55 t.plan(7)56 const fastify = Fastify()57 58 let order = 059 60 fastify.addHook('onReady', async function () {61 await immediate()62 t.equal(order++, 0, 'called in root')63 t.equal(this.pluginName, fastify.pluginName, 'the this binding is the right instance')64 })65 66 fastify.register(async (childOne, o) => {67 childOne.addHook('onReady', async function () {68 await immediate()69 t.equal(order++, 1, 'called in childOne')70 t.equal(this.pluginName, childOne.pluginName, 'the this binding is the right instance')71 })72 73 childOne.register(async (childTwo, o) => {74 childTwo.addHook('onReady', async function () {75 await immediate()76 t.equal(order++, 2, 'called in childTwo')77 t.equal(this.pluginName, childTwo.pluginName, 'the this binding is the right instance')78 })79 })80 })81 82 await fastify.ready()83 t.pass('ready')84})85 86t.test('mix ready and onReady', async t => {87 t.plan(2)88 const fastify = Fastify()89 let order = 090 91 fastify.addHook('onReady', async function () {92 await immediate()93 order++94 })95 96 await fastify.ready()97 t.equal(order, 1)98 99 await fastify.ready()100 t.equal(order, 1, 'ready hooks execute once')101})102 103t.test('listen and onReady order', async t => {104 t.plan(9)105 106 const fastify = Fastify()107 let order = 0108 109 fastify.register((instance, opts, done) => {110 instance.ready(checkOrder.bind(null, 0))111 instance.addHook('onReady', checkOrder.bind(null, 4))112 113 instance.register((subinstance, opts, done) => {114 subinstance.ready(checkOrder.bind(null, 1))115 subinstance.addHook('onReady', checkOrder.bind(null, 5))116 117 subinstance.register((realSubInstance, opts, done) => {118 realSubInstance.ready(checkOrder.bind(null, 2))119 realSubInstance.addHook('onReady', checkOrder.bind(null, 6))120 done()121 })122 done()123 })124 done()125 })126 127 fastify.addHook('onReady', checkOrder.bind(null, 3))128 129 await fastify.ready()130 t.pass('trigger the onReady')131 await fastify.listen({ port: 0 })132 t.pass('do not trigger the onReady')133 134 await fastify.close()135 136 function checkOrder (shouldbe) {137 t.equal(order, shouldbe)138 order++139 }140})141 142t.test('multiple ready calls', async t => {143 t.plan(11)144 145 const fastify = Fastify()146 let order = 0147 148 fastify.register(async (instance, opts) => {149 instance.ready(checkOrder.bind(null, 1))150 instance.addHook('onReady', checkOrder.bind(null, 6))151 152 await instance.register(async (subinstance, opts) => {153 subinstance.ready(checkOrder.bind(null, 2))154 subinstance.addHook('onReady', checkOrder.bind(null, 7))155 })156 157 t.equal(order, 0, 'ready and hooks not triggered yet')158 order++159 })160 161 fastify.addHook('onReady', checkOrder.bind(null, 3))162 fastify.addHook('onReady', checkOrder.bind(null, 4))163 fastify.addHook('onReady', checkOrder.bind(null, 5))164 165 await fastify.ready()166 t.pass('trigger the onReady')167 168 await fastify.ready()169 t.pass('do not trigger the onReady')170 171 await fastify.ready()172 t.pass('do not trigger the onReady')173 174 function checkOrder (shouldbe) {175 t.equal(order, shouldbe)176 order++177 }178})179 180t.test('onReady should manage error in sync', t => {181 t.plan(4)182 const fastify = Fastify()183 184 fastify.addHook('onReady', function (done) {185 t.pass('called in root')186 done()187 })188 189 fastify.register(async (childOne, o) => {190 childOne.addHook('onReady', function (done) {191 t.pass('called in childOne')192 done(new Error('FAIL ON READY'))193 })194 195 childOne.register(async (childTwo, o) => {196 childTwo.addHook('onReady', async function () {197 t.fail('should not be called')198 })199 })200 })201 202 fastify.ready(err => {203 t.ok(err)204 t.equal(err.message, 'FAIL ON READY')205 })206})207 208t.test('onReady should manage error in async', t => {209 t.plan(4)210 const fastify = Fastify()211 212 fastify.addHook('onReady', function (done) {213 t.pass('called in root')214 done()215 })216 217 fastify.register(async (childOne, o) => {218 childOne.addHook('onReady', async function () {219 t.pass('called in childOne')220 throw new Error('FAIL ON READY')221 })222 223 childOne.register(async (childTwo, o) => {224 childTwo.addHook('onReady', async function () {225 t.fail('should not be called')226 })227 })228 })229 230 fastify.ready(err => {231 t.ok(err)232 t.equal(err.message, 'FAIL ON READY')233 })234})235 236t.test('onReady should manage sync error', t => {237 t.plan(4)238 const fastify = Fastify()239 240 fastify.addHook('onReady', function (done) {241 t.pass('called in root')242 done()243 })244 245 fastify.register(async (childOne, o) => {246 childOne.addHook('onReady', function (done) {247 t.pass('called in childOne')248 throw new Error('FAIL UNWANTED SYNC EXCEPTION')249 })250 251 childOne.register(async (childTwo, o) => {252 childTwo.addHook('onReady', async function () {253 t.fail('should not be called')254 })255 })256 })257 258 fastify.ready(err => {259 t.ok(err)260 t.equal(err.message, 'FAIL UNWANTED SYNC EXCEPTION')261 })262})263 264t.test('onReady can not add decorators or application hooks', t => {265 t.plan(3)266 const fastify = Fastify()267 268 fastify.addHook('onReady', function (done) {269 t.pass('called in root')270 fastify.decorate('test', () => {})271 272 fastify.addHook('onReady', async function () {273 t.fail('it will be not called')274 })275 done()276 })277 278 fastify.addHook('onReady', function (done) {279 t.ok(this.hasDecorator('test'))280 done()281 })282 283 fastify.ready(err => { t.error(err) })284})285 286t.test('onReady cannot add lifecycle hooks', t => {287 t.plan(5)288 const fastify = Fastify()289 290 fastify.addHook('onReady', function (done) {291 t.pass('called in root')292 try {293 fastify.addHook('onRequest', (request, reply, done) => {})294 } catch (error) {295 t.ok(error)296 t.equal(error.message, 'Root plugin has already booted')297 // TODO: look where the error pops up298 t.equal(error.code, 'AVV_ERR_ROOT_PLG_BOOTED')299 done(error)300 }301 })302 303 fastify.addHook('onRequest', (request, reply, done) => {})304 fastify.get('/', async () => 'hello')305 306 fastify.ready((err) => { t.ok(err) })307})308 309t.test('onReady throw loading error', t => {310 t.plan(2)311 const fastify = Fastify()312 313 try {314 fastify.addHook('onReady', async function (done) {})315 } catch (e) {316 t.ok(e.code, 'FST_ERR_HOOK_INVALID_ASYNC_HANDLER')317 t.ok(e.message === 'Async function has too many arguments. Async hooks should not use the \'done\' argument.')318 }319})320 321t.test('onReady does not call done', t => {322 t.plan(6)323 const fastify = Fastify({ pluginTimeout: 500 })324 325 fastify.addHook('onReady', function someHookName (done) {326 t.pass('called in root')327 // done() // don't call done to test timeout328 })329 330 fastify.ready(err => {331 t.ok(err)332 t.equal(err.message, 'A callback for \'onReady\' hook "someHookName" timed out. You may have forgotten to call \'done\' function or to resolve a Promise')333 t.equal(err.code, 'FST_ERR_HOOK_TIMEOUT')334 t.ok(err.cause)335 t.equal(err.cause.code, 'AVV_ERR_READY_TIMEOUT')336 })337})338 339t.test('onReady execution order', t => {340 t.plan(3)341 const fastify = Fastify({ })342 343 let i = 0344 fastify.ready(() => { i++; t.equal(i, 1) })345 fastify.ready(() => { i++; t.equal(i, 2) })346 fastify.ready(() => { i++; t.equal(i, 3) })347})348 349t.test('ready return the server with callback', t => {350 t.plan(2)351 const fastify = Fastify()352 353 fastify.ready((err, instance) => {354 t.error(err)355 t.same(instance, fastify)356 })357})358 359t.test('ready return the server with Promise', t => {360 t.plan(1)361 const fastify = Fastify()362 363 fastify.ready()364 .then(instance => { t.same(instance, fastify) })365 .catch(err => { t.fail(err) })366})367 368t.test('ready return registered', t => {369 t.plan(4)370 const fastify = Fastify()371 372 fastify.register((one, opts, done) => {373 one.ready().then(itself => { t.same(itself, one) })374 done()375 })376 377 fastify.register((two, opts, done) => {378 two.ready().then(itself => { t.same(itself, two) })379 380 two.register((twoDotOne, opts, done) => {381 twoDotOne.ready().then(itself => { t.same(itself, twoDotOne) })382 done()383 })384 done()385 })386 387 fastify.ready()388 .then(instance => { t.same(instance, fastify) })389 .catch(err => { t.fail(err) })390})391 392t.test('do not crash with error in follow up onReady hook', async t => {393 const fastify = Fastify()394 395 fastify.addHook('onReady', async function () {396 })397 398 fastify.addHook('onReady', function () {399 throw new Error('kaboom')400 })401 402 await t.rejects(fastify.ready())403})404 