UNPKG

@fastify/middie

Version:

Middleware engine for Fastify

520 lines (436 loc) 10.8 kB
'use strict' const middie = require('../lib/engine') const t = require('tap') const http = require('node:http') const { join } = require('node:path') const serveStatic = require('serve-static') const test = t.test test('use no function', t => { t.plan(3) const instance = middie(function (err, a, b) { t.error(err) t.equal(a, req) t.equal(b, res) }) const req = { url: '/test' } const res = {} instance.run(req, res) }) test('use a function', t => { t.plan(5) const instance = middie(function (err, a, b) { t.error(err) t.equal(a, req) t.equal(b, res) }) const req = { url: '/test' } const res = {} t.equal(instance.use(function (_req, _res, next) { t.pass('function called') next() }), instance) instance.run(req, res) }) test('use two functions', t => { t.plan(5) const instance = middie(function (err, a, b) { t.error(err) t.equal(a, req) t.equal(b, res) }) const req = { url: '/test' } const res = {} let counter = 0 instance.use(function (_req, _res, next) { t.equal(counter++, 0, 'first function called') next() }).use(function (_req, _res, next) { t.equal(counter++, 1, 'second function called') next() }) instance.run(req, res) }) test('stop the middleware chain if one errors', t => { t.plan(1) const instance = middie(function (err) { t.ok(err, 'error is forwarded') }) const req = { url: '/test' } const res = {} instance.use(function (_req, _res, next) { next(new Error('kaboom')) }).use(function (_req, _res, next) { t.fail('this should never be called') next() }) instance.run(req, res) }) test('run restricted by path', t => { t.plan(11) const instance = middie(function (err, a, b) { t.error(err) t.equal(a, req) t.equal('/test', req.url) t.equal(b, res) }) const req = { url: '/test' } const res = {} t.equal(instance.use(function (_req, _res, next) { t.ok('function called') next() }), instance) t.equal(instance.use('/test', function (_req, _res, next) { t.ok('function called') next() }), instance) t.equal(instance.use('/test', function (req, _res, next) { t.equal('/', req.url) next() }), instance) t.equal(instance.use('/no-call', function (_req, _res, next) { t.fail('should not call this function') next() }), instance) instance.run(req, res) }) test('run restricted by path - prefix override', t => { t.plan(10) const instance = middie(function (err, a, b) { t.error(err) t.equal(a, req) t.equal('/test/other/one', req.url) t.equal(b, res) }) const req = { url: '/test/other/one' } const res = {} t.equal(instance.use(function (_req, _res, next) { t.ok('function called') next() }), instance) t.equal(instance.use('/test', function (_req, _res, next) { t.ok('function called') next() }), instance) t.equal(instance.use('/test', function (req, _res, next) { t.equal('/other/one', req.url) next() }), instance) instance.run(req, res) }) test('run restricted by path - prefix override 2', t => { t.plan(10) const instance = middie(function (err, a, b) { t.error(err) t.equal(a, req) t.equal('/tasks-api/task', req.url) t.equal(b, res) }) const req = { url: '/tasks-api/task' } const res = {} t.equal(instance.use(function (_req, _res, next) { t.ok('function called') next() }), instance) t.equal(instance.use('/tasks-api', function (_req, _res, next) { t.ok('function called') next() }), instance) t.equal(instance.use('/tasks-api', function (req, _res, next) { t.equal('/task', req.url) next() }), instance) instance.run(req, res) }) test('run restricted by array path', t => { t.plan(9) const instance = middie(function (err, a, b) { t.error(err) t.equal(a, req) t.equal('/test', req.url) t.equal(b, res) }) const req = { url: '/test' } const res = {} t.equal(instance.use(function (_req, _res, next) { t.ok('function called') next() }), instance) t.equal(instance.use(['/test', '/other-path'], function (_req, _res, next) { t.ok('function called') next() }), instance) t.equal(instance.use(['/no-call', 'other-path'], function (_req, _res, next) { t.fail('should not call this function') next() }), instance) instance.run(req, res) }) test('run array of middleware restricted by path', t => { t.plan(10) const instance = middie(function (err, a, b) { t.error(err) t.equal(a, req) t.equal('/test', req.url) t.equal(b, res) }) const req = { url: '/test' } const res = {} t.equal(instance.use([function (_req, _res, next) { t.ok('function called') next() }, function (_req, _res, next) { t.ok('function called') next() }]), instance) t.equal(instance.use('/test', [function (_req, _res, next) { t.ok('function called') next() }, function (_req, _res, next) { t.ok('function called') next() }]), instance) instance.run(req, res) }) test('run array of middleware restricted by array path', t => { t.plan(10) const instance = middie(function (err, a, b) { t.error(err) t.equal(a, req) t.equal('/test', req.url) t.equal(b, res) }) const req = { url: '/test' } const res = {} t.equal(instance.use([function (_req, _res, next) { t.ok('function called') next() }, function (_req, _res, next) { t.ok('function called') next() }]), instance) t.equal(instance.use(['/test', '/other-path'], [function (_req, _res, next) { t.ok('function called') next() }, function (_req, _res, next) { t.ok('function called') next() }]), instance) instance.run(req, res) }) test('Should strip the url to only match the pathname', t => { t.plan(6) const instance = middie(function (err, a, b) { t.error(err) t.equal(a, req) t.equal(req.url, '/test#foo?bin=baz') t.equal(b, res) }) const req = { url: '/test#foo?bin=baz' } const res = {} t.equal(instance.use('/test', function (_req, _res, next) { t.pass('function called') next() }), instance) instance.run(req, res) }) test('should keep the context', t => { t.plan(6) const instance = middie(function (err, a, b, ctx) { t.error(err) t.equal(a, req) t.equal(b, res) t.ok(ctx.key) }) const req = { url: '/test' } const res = {} t.equal(instance.use(function (_req, _res, next) { t.pass('function called') next() }), instance) instance.run(req, res, { key: true }) }) test('should add `originalUrl` property to req', t => { t.plan(2) const instance = middie(function (err) { t.error(err) }) const req = { url: '/test' } const res = {} instance.use(function (req, _res, next) { t.equal(req.originalUrl, '/test') next() }) instance.run(req, res) }) test('basic serve static', t => { const instance = middie(function () { t.fail('the default route should never be called') }) instance.use(serveStatic(join(__dirname, '..'))) const server = http.createServer(instance.run.bind(instance)) server.listen(0, function () { http.get(`http://localhost:${server.address().port}/README.md`, function (res) { t.equal(res.statusCode, 200) res.resume() server.close() server.unref() t.end() }) }) }) test('limit serve static to a specific folder', t => { const instance = middie(function () { t.fail('the default route should never be called') req.destroy() server.close() server.unref() }) instance.use('/assets', serveStatic(join(__dirname, '..'))) const server = http.createServer(instance.run.bind(instance)) let req server.listen(0, function () { req = http.get(`http://localhost:${server.address().port}/assets/README.md`, function (res) { t.equal(res.statusCode, 200) res.resume() server.close() server.unref() t.end() }) }) }) test('should match all chain', t => { t.plan(2) const instance = middie(function (err, req) { t.error(err) t.same(req, { url: '/inner/in/depth', originalUrl: '/inner/in/depth', undefined: true, null: true, empty: true, root: true, inner: true, innerSlashed: true, innerIn: true, innerInSlashed: true, innerInDepth: true, innerInDepthSlashed: true }) }) const req = { url: '/inner/in/depth' } const res = {} const prefixes = [ { prefix: undefined, name: 'undefined' }, { prefix: null, name: 'null' }, { prefix: '', name: 'empty' }, { prefix: '/', name: 'root' }, { prefix: '/inner', name: 'inner' }, { prefix: '/inner/', name: 'innerSlashed' }, { prefix: '/inner/in', name: 'innerIn' }, { prefix: '/inner/in/', name: 'innerInSlashed' }, { prefix: '/inner/in/depth', name: 'innerInDepth' }, { prefix: '/inner/in/depth/', name: 'innerInDepthSlashed' } ] prefixes.forEach(function (o) { instance.use(o.prefix, function (req, _res, next) { if (req[o.name]) throw new Error('Called twice!') req[o.name] = true next() }) }) instance.run(req, res) }) test('should match the same slashed path', t => { t.plan(3) const instance = middie(function (err, req) { t.error(err) t.same(req, { url: '/path', originalUrl: '/path' }) }) const req = { url: '/path' } const res = {} instance.use('/path/', function (_req, _res, next) { t.pass('function called') next() }) instance.use('/path/inner', function (_req, _res, next) { t.fail() next() }) instance.run(req, res) }) test('if the function calls res.end the iterator should stop / 1 (with deprecated finished flag)', t => { t.plan(1) const instance = middie(function () { t.fail('we should not be here') }) const req = { url: '/test' } const res = { finished: false, end: function () { t.pass('res.end') this.finished = true } } instance .use(function (_req, res, next) { res.end('hello') next() }) .use(function () { t.fail('we should not be here') }) instance.run(req, res) }) test('if the function calls res.end the iterator should stop / 2', t => { t.plan(0) const instance = middie(function () { t.fail('we should not be here') }) const req = new http.IncomingMessage(null) req.url = '/test' const res = new http.ServerResponse(req) instance .use(function (_req, res, next) { res.end('bye') next() }) .use(function () { t.fail('we should not be here') }) instance.run(req, res) })