UNPKG

18.2 kBJavaScriptView Raw
1'use strict'
2
3/* eslint no-prototype-builtins: 0 */
4
5const t = require('tap')
6const test = t.test
7const Fastify = require('..')
8const sget = require('simple-get').concat
9const fp = require('fastify-plugin')
10const lolex = require('lolex')
11
12test('require a plugin', t => {
13 t.plan(1)
14 const fastify = Fastify()
15 fastify.register(require('./plugin.helper'))
16 fastify.ready(() => {
17 t.ok(fastify.test)
18 })
19})
20
21test('plugin metadata - ignore prefix', t => {
22 t.plan(2)
23 const fastify = Fastify()
24
25 plugin[Symbol.for('skip-override')] = true
26 fastify.register(plugin, { prefix: 'foo' })
27
28 fastify.inject({
29 method: 'GET',
30 url: '/'
31 }, function (err, res) {
32 t.error(err)
33 t.equals(res.payload, 'hello')
34 })
35
36 function plugin (instance, opts, next) {
37 instance.get('/', function (request, reply) {
38 reply.send('hello')
39 })
40 next()
41 }
42})
43
44test('fastify.register with fastify-plugin should not incapsulate his code', t => {
45 t.plan(10)
46 const fastify = Fastify()
47
48 fastify.register((instance, opts, next) => {
49 instance.register(fp((i, o, n) => {
50 i.decorate('test', () => {})
51 t.ok(i.test)
52 n()
53 }))
54
55 t.notOk(instance.test)
56
57 // the decoration is added at the end
58 instance.after(() => {
59 t.ok(instance.test)
60 })
61
62 instance.get('/', (req, reply) => {
63 t.ok(instance.test)
64 reply.send({ hello: 'world' })
65 })
66
67 next()
68 })
69
70 fastify.ready(() => {
71 t.notOk(fastify.test)
72 })
73
74 fastify.listen(0, err => {
75 t.error(err)
76 fastify.server.unref()
77
78 sget({
79 method: 'GET',
80 url: 'http://localhost:' + fastify.server.address().port
81 }, (err, response, body) => {
82 t.error(err)
83 t.strictEqual(response.statusCode, 200)
84 t.strictEqual(response.headers['content-length'], '' + body.length)
85 t.deepEqual(JSON.parse(body), { hello: 'world' })
86 })
87 })
88})
89
90test('fastify.register with fastify-plugin should provide access to external fastify instance if opts argument is a function', t => {
91 t.plan(22)
92 const fastify = Fastify()
93
94 fastify.register((instance, opts, next) => {
95 instance.register(fp((i, o, n) => {
96 i.decorate('global', () => {})
97 t.ok(i.global)
98 n()
99 }))
100
101 instance.register((i, o, n) => n(), p => {
102 t.notOk(p === instance || p === fastify)
103 t.ok(instance.isPrototypeOf(p))
104 t.ok(fastify.isPrototypeOf(p))
105 t.ok(p.global)
106 })
107
108 instance.register((i, o, n) => {
109 i.decorate('local', () => {})
110 n()
111 })
112
113 instance.register((i, o, n) => n(), p => t.notOk(p.local))
114
115 instance.register((i, o, n) => {
116 t.ok(i.local)
117 n()
118 }, p => p.decorate('local', () => {}))
119
120 instance.register((i, o, n) => n(), p => t.notOk(p.local))
121
122 instance.register(fp((i, o, n) => {
123 t.ok(i.global_2)
124 n()
125 }), p => p.decorate('global_2', () => 'hello'))
126
127 instance.register((i, o, n) => {
128 i.decorate('global_2', () => 'world')
129 n()
130 }, p => p.get('/', (req, reply) => {
131 t.ok(p.global_2)
132 reply.send({ hello: p.global_2() })
133 }))
134
135 t.notOk(instance.global)
136 t.notOk(instance.global_2)
137 t.notOk(instance.local)
138
139 // the decoration is added at the end
140 instance.after(() => {
141 t.ok(instance.global)
142 t.strictEqual(instance.global_2(), 'hello')
143 t.notOk(instance.local)
144 })
145
146 next()
147 })
148
149 fastify.ready(() => {
150 t.notOk(fastify.global)
151 })
152
153 fastify.listen(0, err => {
154 t.error(err)
155 fastify.server.unref()
156
157 sget({
158 method: 'GET',
159 url: 'http://localhost:' + fastify.server.address().port
160 }, (err, response, body) => {
161 t.error(err)
162 t.strictEqual(response.statusCode, 200)
163 t.strictEqual(response.headers['content-length'], '' + body.length)
164 t.deepEqual(JSON.parse(body), { hello: 'world' })
165 })
166 })
167})
168
169test('fastify.register with fastify-plugin registers root level plugins', t => {
170 t.plan(15)
171 const fastify = Fastify()
172
173 function rootPlugin (instance, opts, next) {
174 instance.decorate('test', 'first')
175 t.ok(instance.test)
176 next()
177 }
178
179 function innerPlugin (instance, opts, next) {
180 instance.decorate('test2', 'second')
181 next()
182 }
183
184 fastify.register(fp(rootPlugin))
185
186 fastify.register((instance, opts, next) => {
187 t.ok(instance.test)
188 instance.register(fp(innerPlugin))
189
190 instance.get('/test2', (req, reply) => {
191 t.ok(instance.test2)
192 reply.send({ test2: instance.test2 })
193 })
194
195 next()
196 })
197
198 fastify.ready(() => {
199 t.ok(fastify.test)
200 t.notOk(fastify.test2)
201 })
202
203 fastify.get('/', (req, reply) => {
204 t.ok(fastify.test)
205 reply.send({ test: fastify.test })
206 })
207
208 fastify.listen(0, err => {
209 t.error(err)
210 fastify.server.unref()
211
212 sget({
213 method: 'GET',
214 url: 'http://localhost:' + fastify.server.address().port
215 }, (err, response, body) => {
216 t.error(err)
217 t.strictEqual(response.statusCode, 200)
218 t.strictEqual(response.headers['content-length'], '' + body.length)
219 t.deepEqual(JSON.parse(body), { test: 'first' })
220 })
221
222 sget({
223 method: 'GET',
224 url: 'http://localhost:' + fastify.server.address().port + '/test2'
225 }, (err, response, body) => {
226 t.error(err)
227 t.strictEqual(response.statusCode, 200)
228 t.strictEqual(response.headers['content-length'], '' + body.length)
229 t.deepEqual(JSON.parse(body), { test2: 'second' })
230 })
231 })
232})
233
234test('check dependencies - should not throw', t => {
235 t.plan(12)
236 const fastify = Fastify()
237
238 fastify.register((instance, opts, next) => {
239 instance.register(fp((i, o, n) => {
240 i.decorate('test', () => {})
241 t.ok(i.test)
242 n()
243 }))
244
245 instance.register(fp((i, o, n) => {
246 try {
247 i.decorate('otherTest', () => {}, ['test'])
248 t.ok(i.test)
249 t.ok(i.otherTest)
250 n()
251 } catch (e) {
252 t.fail()
253 }
254 }))
255
256 instance.get('/', (req, reply) => {
257 t.ok(instance.test)
258 t.ok(instance.otherTest)
259 reply.send({ hello: 'world' })
260 })
261
262 next()
263 })
264
265 fastify.ready(() => {
266 t.notOk(fastify.test)
267 t.notOk(fastify.otherTest)
268 })
269
270 fastify.listen(0, err => {
271 t.error(err)
272 fastify.server.unref()
273
274 sget({
275 method: 'GET',
276 url: 'http://localhost:' + fastify.server.address().port
277 }, (err, response, body) => {
278 t.error(err)
279 t.strictEqual(response.statusCode, 200)
280 t.strictEqual(response.headers['content-length'], '' + body.length)
281 t.deepEqual(JSON.parse(body), { hello: 'world' })
282 })
283 })
284})
285
286test('check dependencies - should throw', t => {
287 t.plan(12)
288 const fastify = Fastify()
289
290 fastify.register((instance, opts, next) => {
291 instance.register(fp((i, o, n) => {
292 try {
293 i.decorate('otherTest', () => {}, ['test'])
294 t.fail()
295 } catch (e) {
296 t.is(e.code, 'FST_ERR_DEC_MISSING_DEPENDENCY')
297 t.is(e.message, 'FST_ERR_DEC_MISSING_DEPENDENCY: The decorator is missing dependency \'test\'.')
298 }
299 n()
300 }))
301
302 instance.register(fp((i, o, n) => {
303 i.decorate('test', () => {})
304 t.ok(i.test)
305 t.notOk(i.otherTest)
306 n()
307 }))
308
309 instance.get('/', (req, reply) => {
310 t.ok(instance.test)
311 t.notOk(instance.otherTest)
312 reply.send({ hello: 'world' })
313 })
314
315 next()
316 })
317
318 fastify.ready(() => {
319 t.notOk(fastify.test)
320 })
321
322 fastify.listen(0, err => {
323 t.error(err)
324 fastify.server.unref()
325
326 sget({
327 method: 'GET',
328 url: 'http://localhost:' + fastify.server.address().port
329 }, (err, response, body) => {
330 t.error(err)
331 t.strictEqual(response.statusCode, 200)
332 t.strictEqual(response.headers['content-length'], '' + body.length)
333 t.deepEqual(JSON.parse(body), { hello: 'world' })
334 })
335 })
336})
337
338test('set the plugin name based on the plugin displayName symbol', t => {
339 t.plan(5)
340 const fastify = Fastify()
341
342 fastify.register(fp((fastify, opts, next) => {
343 t.strictEqual(fastify.pluginName, 'plugin-A')
344 fastify.register(fp((fastify, opts, next) => {
345 t.strictEqual(fastify.pluginName, 'plugin-A -> plugin-AB')
346 next()
347 }, { name: 'plugin-AB' }))
348 fastify.register(fp((fastify, opts, next) => {
349 t.strictEqual(fastify.pluginName, 'plugin-A -> plugin-AB -> plugin-AC')
350 next()
351 }, { name: 'plugin-AC' }))
352 next()
353 }, { name: 'plugin-A' }))
354
355 fastify.register(fp((fastify, opts, next) => {
356 t.strictEqual(fastify.pluginName, 'plugin-A -> plugin-AB -> plugin-AC -> plugin-B')
357 next()
358 }, { name: 'plugin-B' }))
359
360 fastify.listen(0, err => {
361 t.error(err)
362 fastify.close()
363 })
364})
365
366test('plugin name will change when using no encapsulation', t => {
367 t.plan(5)
368 const fastify = Fastify()
369
370 fastify.register(fp((fastify, opts, next) => {
371 // store it in a different variable will hold the correct name
372 const pluginName = fastify.pluginName
373 fastify.register(fp((fastify, opts, next) => {
374 t.strictEqual(fastify.pluginName, 'plugin-A -> plugin-AB')
375 next()
376 }, { name: 'plugin-AB' }))
377 fastify.register(fp((fastify, opts, next) => {
378 t.strictEqual(fastify.pluginName, 'plugin-A -> plugin-AB -> plugin-AC')
379 next()
380 }, { name: 'plugin-AC' }))
381 setImmediate(() => {
382 // normally we would expect the name plugin-A
383 // but we operate on the same instance in each plugin
384 t.strictEqual(fastify.pluginName, 'plugin-A -> plugin-AB -> plugin-AC')
385 t.strictEqual(pluginName, 'plugin-A')
386 })
387 next()
388 }, { name: 'plugin-A' }))
389
390 fastify.listen(0, err => {
391 t.error(err)
392 fastify.close()
393 })
394})
395
396test('plugin name is undefined when accessing in no plugin context', t => {
397 t.plan(2)
398 const fastify = Fastify()
399
400 t.strictEqual(fastify.pluginName, undefined)
401
402 fastify.listen(0, err => {
403 t.error(err)
404 fastify.close()
405 })
406})
407
408test('set the plugin name based on the plugin function name', t => {
409 t.plan(5)
410 const fastify = Fastify()
411
412 fastify.register(function myPluginA (fastify, opts, next) {
413 t.strictEqual(fastify.pluginName, 'myPluginA')
414 fastify.register(function myPluginAB (fastify, opts, next) {
415 t.strictEqual(fastify.pluginName, 'myPluginAB')
416 next()
417 })
418 setImmediate(() => {
419 // exact name due to encapsulation
420 t.strictEqual(fastify.pluginName, 'myPluginA')
421 })
422 next()
423 })
424
425 fastify.register(function myPluginB (fastify, opts, next) {
426 t.strictEqual(fastify.pluginName, 'myPluginB')
427 next()
428 })
429
430 fastify.listen(0, err => {
431 t.error(err)
432 fastify.close()
433 })
434})
435
436test('approximate a plugin name when no meta data is available', t => {
437 t.plan(7)
438 const fastify = Fastify()
439
440 fastify.register((fastify, opts, next) => {
441 // A
442 t.is(fastify.pluginName.startsWith('(fastify, opts, next)'), true)
443 t.is(fastify.pluginName.includes('// A'), true)
444 fastify.register((fastify, opts, next) => {
445 // B
446 t.is(fastify.pluginName.startsWith('(fastify, opts, next)'), true)
447 t.is(fastify.pluginName.includes('// B'), true)
448 next()
449 })
450 setImmediate(() => {
451 t.is(fastify.pluginName.startsWith('(fastify, opts, next)'), true)
452 t.is(fastify.pluginName.includes('// A'), true)
453 })
454 next()
455 })
456
457 fastify.listen(0, err => {
458 t.error(err)
459 fastify.close()
460 })
461})
462
463test('approximate a plugin name also when fastify-plugin has no meta data', t => {
464 t.plan(4)
465 const fastify = Fastify()
466
467 fastify.register(fp((fastify, opts, next) => {
468 t.is(fastify.pluginName, 'plugin.test')
469 fastify.register(fp(function B (fastify, opts, next) {
470 // function has name
471 t.is(fastify.pluginName, 'plugin.test -> B')
472 next()
473 }))
474 setImmediate(() => {
475 t.is(fastify.pluginName, 'plugin.test -> B')
476 })
477 next()
478 }))
479
480 fastify.listen(0, err => {
481 t.error(err)
482 fastify.close()
483 })
484})
485
486test('plugin incapsulation', t => {
487 t.plan(10)
488 const fastify = Fastify()
489
490 fastify.register((instance, opts, next) => {
491 instance.register(fp((i, o, n) => {
492 i.decorate('test', 'first')
493 n()
494 }))
495
496 instance.get('/first', (req, reply) => {
497 reply.send({ plugin: instance.test })
498 })
499
500 next()
501 })
502
503 fastify.register((instance, opts, next) => {
504 instance.register(fp((i, o, n) => {
505 i.decorate('test', 'second')
506 n()
507 }))
508
509 instance.get('/second', (req, reply) => {
510 reply.send({ plugin: instance.test })
511 })
512
513 next()
514 })
515
516 fastify.ready(() => {
517 t.notOk(fastify.test)
518 })
519
520 fastify.listen(0, err => {
521 t.error(err)
522 fastify.server.unref()
523
524 sget({
525 method: 'GET',
526 url: 'http://localhost:' + fastify.server.address().port + '/first'
527 }, (err, response, body) => {
528 t.error(err)
529 t.strictEqual(response.statusCode, 200)
530 t.strictEqual(response.headers['content-length'], '' + body.length)
531 t.deepEqual(JSON.parse(body), { plugin: 'first' })
532 })
533
534 sget({
535 method: 'GET',
536 url: 'http://localhost:' + fastify.server.address().port + '/second'
537 }, (err, response, body) => {
538 t.error(err)
539 t.strictEqual(response.statusCode, 200)
540 t.strictEqual(response.headers['content-length'], '' + body.length)
541 t.deepEqual(JSON.parse(body), { plugin: 'second' })
542 })
543 })
544})
545
546test('if a plugin raises an error and there is not a callback to handle it, the server must not start', t => {
547 t.plan(2)
548 const fastify = Fastify()
549
550 fastify.register((instance, opts, next) => {
551 next(new Error('err'))
552 })
553
554 fastify.listen(0, err => {
555 t.ok(err instanceof Error)
556 t.is(err.message, 'err')
557 })
558})
559
560test('add hooks after route declaration', t => {
561 t.plan(3)
562 const fastify = Fastify()
563
564 function plugin (instance, opts, next) {
565 instance.decorateRequest('check', {})
566 setImmediate(next)
567 }
568 fastify.register(fp(plugin))
569
570 fastify.register((instance, options, next) => {
571 instance.addHook('preHandler', function b (req, res, next) {
572 req.check.hook2 = true
573 next()
574 })
575
576 instance.get('/', (req, reply) => {
577 reply.send(req.check)
578 })
579
580 instance.addHook('preHandler', function c (req, res, next) {
581 req.check.hook3 = true
582 next()
583 })
584
585 next()
586 })
587
588 fastify.addHook('preHandler', function a (req, res, next) {
589 req.check.hook1 = true
590 next()
591 })
592
593 fastify.listen(0, err => {
594 t.error(err)
595
596 sget({
597 method: 'GET',
598 url: 'http://localhost:' + fastify.server.address().port
599 }, (err, response, body) => {
600 t.error(err)
601 t.deepEqual(JSON.parse(body), { hook1: true, hook2: true, hook3: true })
602 fastify.close()
603 })
604 })
605})
606
607test('nested plugins', t => {
608 t.plan(5)
609
610 const fastify = Fastify()
611
612 t.tearDown(fastify.close.bind(fastify))
613
614 fastify.register(function (fastify, opts, next) {
615 fastify.register((fastify, opts, next) => {
616 fastify.get('/', function (req, reply) {
617 reply.send('I am child 1')
618 })
619 next()
620 }, { prefix: '/child1' })
621
622 fastify.register((fastify, opts, next) => {
623 fastify.get('/', function (req, reply) {
624 reply.send('I am child 2')
625 })
626 next()
627 }, { prefix: '/child2' })
628
629 next()
630 }, { prefix: '/parent' })
631
632 fastify.listen(0, err => {
633 t.error(err)
634
635 sget({
636 method: 'GET',
637 url: 'http://localhost:' + fastify.server.address().port + '/parent/child1'
638 }, (err, response, body) => {
639 t.error(err)
640 t.deepEqual(body.toString(), 'I am child 1')
641 })
642
643 sget({
644 method: 'GET',
645 url: 'http://localhost:' + fastify.server.address().port + '/parent/child2'
646 }, (err, response, body) => {
647 t.error(err)
648 t.deepEqual(body.toString(), 'I am child 2')
649 })
650 })
651})
652
653test('plugin metadata - decorators', t => {
654 t.plan(1)
655 const fastify = Fastify()
656
657 fastify.decorate('plugin1', true)
658 fastify.decorateReply('plugin1', true)
659 fastify.decorateRequest('plugin1', true)
660
661 plugin[Symbol.for('skip-override')] = true
662 plugin[Symbol.for('plugin-meta')] = {
663 decorators: {
664 fastify: ['plugin1'],
665 reply: ['plugin1'],
666 request: ['plugin1']
667 }
668 }
669
670 fastify.register(plugin)
671
672 fastify.ready(() => {
673 t.ok(fastify.plugin)
674 })
675
676 function plugin (instance, opts, next) {
677 instance.decorate('plugin', true)
678 next()
679 }
680})
681
682test('plugin metadata - dependencies', t => {
683 t.plan(1)
684 const fastify = Fastify()
685
686 dependency[Symbol.for('skip-override')] = true
687 dependency[Symbol.for('plugin-meta')] = {
688 name: 'plugin'
689 }
690
691 plugin[Symbol.for('skip-override')] = true
692 plugin[Symbol.for('plugin-meta')] = {
693 dependencies: ['plugin']
694 }
695
696 fastify.register(dependency)
697 fastify.register(plugin)
698
699 fastify.ready(() => {
700 t.pass('everything right')
701 })
702
703 function dependency (instance, opts, next) {
704 next()
705 }
706
707 function plugin (instance, opts, next) {
708 next()
709 }
710})
711
712test('plugin metadata - dependencies (nested)', t => {
713 t.plan(1)
714 const fastify = Fastify()
715
716 dependency[Symbol.for('skip-override')] = true
717 dependency[Symbol.for('plugin-meta')] = {
718 name: 'plugin'
719 }
720
721 nested[Symbol.for('skip-override')] = true
722 nested[Symbol.for('plugin-meta')] = {
723 dependencies: ['plugin']
724 }
725
726 fastify.register(dependency)
727 fastify.register(plugin)
728
729 fastify.ready(() => {
730 t.pass('everything right')
731 })
732
733 function dependency (instance, opts, next) {
734 next()
735 }
736
737 function plugin (instance, opts, next) {
738 instance.register(nested)
739 next()
740 }
741
742 function nested (instance, opts, next) {
743 next()
744 }
745})
746
747test('pluginTimeout', t => {
748 t.plan(2)
749 const fastify = Fastify({
750 pluginTimeout: 10
751 })
752 fastify.register(function (app, opts, next) {
753 // to no call next on purpose
754 })
755 fastify.ready((err) => {
756 t.ok(err)
757 t.equal(err.code, 'ERR_AVVIO_PLUGIN_TIMEOUT')
758 })
759})
760
761test('pluginTimeout default', t => {
762 t.plan(2)
763 const clock = lolex.install()
764
765 const fastify = Fastify()
766 fastify.register(function (app, opts, next) {
767 // default time elapsed without calling next
768 clock.tick(10000)
769 })
770
771 fastify.ready((err) => {
772 t.ok(err)
773 t.equal(err.code, 'ERR_AVVIO_PLUGIN_TIMEOUT')
774 })
775
776 t.tearDown(clock.uninstall)
777})