UNPKG

fastify

Version:

Fast and low overhead web framework, for Node.js

663 lines (573 loc) 14.4 kB
'use strict' const t = require('tap') const test = t.test const Fastify = require('../fastify') test('Should register a host constrained route', t => { t.plan(7) const fastify = Fastify() fastify.route({ method: 'GET', url: '/', constraints: { host: 'fastify.io' }, handler: (req, reply) => { reply.send({ hello: 'world' }) } }) fastify.inject({ method: 'GET', url: '/', headers: { host: 'fastify.io' } }, (err, res) => { t.error(err) t.same(JSON.parse(res.payload), { hello: 'world' }) t.equal(res.statusCode, 200) }) fastify.inject({ method: 'GET', url: '/', headers: { host: 'example.com' } }, (err, res) => { t.error(err) t.equal(res.statusCode, 404) }) fastify.inject({ method: 'GET', url: '/' }, (err, res) => { t.error(err) t.equal(res.statusCode, 404) }) }) test('Should register the same route with host constraints', t => { t.plan(8) const fastify = Fastify() fastify.route({ method: 'GET', url: '/', constraints: { host: 'fastify.io' }, handler: (req, reply) => { reply.send('fastify.io') } }) fastify.route({ method: 'GET', url: '/', constraints: { host: 'example.com' }, handler: (req, reply) => { reply.send('example.com') } }) fastify.inject({ method: 'GET', url: '/', headers: { host: 'fastify.io' } }, (err, res) => { t.error(err) t.equal(res.statusCode, 200) t.equal(res.payload, 'fastify.io') }) fastify.inject({ method: 'GET', url: '/', headers: { host: 'example.com' } }, (err, res) => { t.error(err) t.equal(res.statusCode, 200) t.equal(res.payload, 'example.com') }) fastify.inject({ method: 'GET', url: '/', headers: { host: 'fancy.ca' } }, (err, res) => { t.error(err) t.equal(res.statusCode, 404) }) }) test('Should allow registering custom constrained routes', t => { t.plan(8) const constraint = { name: 'secret', storage: function () { const secrets = {} return { get: (secret) => { return secrets[secret] || null }, set: (secret, store) => { secrets[secret] = store } } }, deriveConstraint: (req, ctx) => { return req.headers['x-secret'] }, validate () { return true } } const fastify = Fastify({ constraints: { secret: constraint } }) fastify.route({ method: 'GET', url: '/', constraints: { secret: 'alpha' }, handler: (req, reply) => { reply.send({ hello: 'from alpha' }) } }) fastify.route({ method: 'GET', url: '/', constraints: { secret: 'beta' }, handler: (req, reply) => { reply.send({ hello: 'from beta' }) } }) fastify.inject({ method: 'GET', url: '/', headers: { 'X-Secret': 'alpha' } }, (err, res) => { t.error(err) t.same(JSON.parse(res.payload), { hello: 'from alpha' }) t.equal(res.statusCode, 200) }) fastify.inject({ method: 'GET', url: '/', headers: { 'X-Secret': 'beta' } }, (err, res) => { t.error(err) t.same(JSON.parse(res.payload), { hello: 'from beta' }) t.equal(res.statusCode, 200) }) fastify.inject({ method: 'GET', url: '/', headers: { 'X-Secret': 'gamma' } }, (err, res) => { t.error(err) t.equal(res.statusCode, 404) }) }) test('Should allow registering custom constrained routes outside constructor', t => { t.plan(8) const constraint = { name: 'secret', storage: function () { const secrets = {} return { get: (secret) => { return secrets[secret] || null }, set: (secret, store) => { secrets[secret] = store } } }, deriveConstraint: (req, ctx) => { return req.headers['x-secret'] }, validate () { return true } } const fastify = Fastify() fastify.addConstraintStrategy(constraint) fastify.route({ method: 'GET', url: '/', constraints: { secret: 'alpha' }, handler: (req, reply) => { reply.send({ hello: 'from alpha' }) } }) fastify.route({ method: 'GET', url: '/', constraints: { secret: 'beta' }, handler: (req, reply) => { reply.send({ hello: 'from beta' }) } }) fastify.inject({ method: 'GET', url: '/', headers: { 'X-Secret': 'alpha' } }, (err, res) => { t.error(err) t.same(JSON.parse(res.payload), { hello: 'from alpha' }) t.equal(res.statusCode, 200) }) fastify.inject({ method: 'GET', url: '/', headers: { 'X-Secret': 'beta' } }, (err, res) => { t.error(err) t.same(JSON.parse(res.payload), { hello: 'from beta' }) t.equal(res.statusCode, 200) }) fastify.inject({ method: 'GET', url: '/', headers: { 'X-Secret': 'gamma' } }, (err, res) => { t.error(err) t.equal(res.statusCode, 404) }) }) test('Add a constraint strategy after fastify instance was started', t => { t.plan(4) const constraint = { name: 'secret', storage: function () { const secrets = {} return { get: (secret) => { return secrets[secret] || null }, set: (secret, store) => { secrets[secret] = store } } }, deriveConstraint: (req, ctx) => { return req.headers['x-secret'] }, validate () { return true } } const fastify = Fastify() fastify.route({ method: 'GET', url: '/', handler: (req, reply) => { reply.send('ok') } }) fastify.inject({ method: 'GET', url: '/' }, (err, res) => { t.error(err) t.same(res.payload, 'ok') t.equal(res.statusCode, 200) t.throws( () => fastify.addConstraintStrategy(constraint), 'Cannot add constraint strategy when fastify instance is already started!' ) }) }) test('Add a constraint strategy should throw an error if there already exist custom strategy with the same name', t => { t.plan(1) const constraint = { name: 'secret', storage: function () { const secrets = {} return { get: (secret) => { return secrets[secret] || null }, set: (secret, store) => { secrets[secret] = store } } }, deriveConstraint: (req, ctx) => { return req.headers['x-secret'] }, validate () { return true } } const fastify = Fastify() fastify.addConstraintStrategy(constraint) t.throws( () => fastify.addConstraintStrategy(constraint), 'There already exists a custom constraint with the name secret.' ) }) test('Add a constraint strategy shouldn\'t throw an error if default constraint with the same name isn\'t used', t => { t.plan(1) const constraint = { name: 'version', storage: function () { const secrets = {} return { get: (secret) => { return secrets[secret] || null }, set: (secret, store) => { secrets[secret] = store } } }, deriveConstraint: (req, ctx) => { return req.headers['x-secret'] }, validate () { return true } } const fastify = Fastify() fastify.addConstraintStrategy(constraint) t.pass() }) test('Add a constraint strategy should throw an error if default constraint with the same name is used', t => { t.plan(1) const constraint = { name: 'version', storage: function () { const secrets = {} return { get: (secret) => { return secrets[secret] || null }, set: (secret, store) => { secrets[secret] = store } } }, deriveConstraint: (req, ctx) => { return req.headers['x-secret'] }, validate () { return true } } const fastify = Fastify() fastify.route({ method: 'GET', url: '/', constraints: { version: '1.0.0' }, handler: (req, reply) => { reply.send('ok') } }) t.throws( () => fastify.addConstraintStrategy(constraint), 'There already exists a route with version constraint.' ) }) test('The hasConstraintStrategy should return false for default constraints until they are used', t => { t.plan(6) const fastify = Fastify() t.equal(fastify.hasConstraintStrategy('version'), false) t.equal(fastify.hasConstraintStrategy('host'), false) fastify.route({ method: 'GET', url: '/', constraints: { host: 'fastify.io' }, handler: (req, reply) => { reply.send({ hello: 'from any other domain' }) } }) t.equal(fastify.hasConstraintStrategy('version'), false) t.equal(fastify.hasConstraintStrategy('host'), true) fastify.route({ method: 'GET', url: '/', constraints: { version: '1.0.0' }, handler: (req, reply) => { reply.send({ hello: 'from any other domain' }) } }) t.equal(fastify.hasConstraintStrategy('version'), true) t.equal(fastify.hasConstraintStrategy('host'), true) }) test('The hasConstraintStrategy should return true if there already exist a custom constraint with the same name', t => { t.plan(2) const constraint = { name: 'secret', storage: function () { const secrets = {} return { get: (secret) => { return secrets[secret] || null }, set: (secret, store) => { secrets[secret] = store } } }, deriveConstraint: (req, ctx) => { return req.headers['x-secret'] }, validate () { return true } } const fastify = Fastify() t.equal(fastify.hasConstraintStrategy('secret'), false) fastify.addConstraintStrategy(constraint) t.equal(fastify.hasConstraintStrategy('secret'), true) }) test('Should allow registering an unconstrained route after a constrained route', t => { t.plan(6) const fastify = Fastify() fastify.route({ method: 'GET', url: '/', constraints: { host: 'fastify.io' }, handler: (req, reply) => { reply.send({ hello: 'from fastify.io' }) } }) fastify.route({ method: 'GET', url: '/', handler: (req, reply) => { reply.send({ hello: 'from any other domain' }) } }) fastify.inject({ method: 'GET', url: '/', headers: { host: 'fastify.io' } }, (err, res) => { t.error(err) t.same(JSON.parse(res.payload), { hello: 'from fastify.io' }) t.equal(res.statusCode, 200) }) fastify.inject({ method: 'GET', url: '/', headers: { host: 'example.com' } }, (err, res) => { t.error(err) t.same(JSON.parse(res.payload), { hello: 'from any other domain' }) t.equal(res.statusCode, 200) }) }) test('Should allow registering constrained routes in a prefixed plugin', t => { t.plan(3) const fastify = Fastify() fastify.register(async (scope, opts) => { scope.route({ method: 'GET', constraints: { host: 'fastify.io' }, path: '/route', handler: (req, reply) => { reply.send({ ok: true }) } }) }, { prefix: '/prefix' }) fastify.inject({ method: 'GET', url: '/prefix/route', headers: { host: 'fastify.io' } }, (err, res) => { t.error(err) t.same(JSON.parse(res.payload), { ok: true }) t.equal(res.statusCode, 200) }) }) test('Should allow registering a constrained GET route after a constrained HEAD route', t => { t.plan(3) const fastify = Fastify() fastify.route({ method: 'HEAD', url: '/', constraints: { host: 'fastify.io' }, handler: (req, reply) => { reply.header('content-type', 'text/plain') reply.send('custom HEAD response') } }) fastify.route({ method: 'GET', url: '/', constraints: { host: 'fastify.io' }, handler: (req, reply) => { reply.send({ hello: 'from any other domain' }) } }) fastify.inject({ method: 'HEAD', url: '/', headers: { host: 'fastify.io' } }, (err, res) => { t.error(err) t.same(res.payload, 'custom HEAD response') t.equal(res.statusCode, 200) }) }) test('Should allow registering a constrained GET route after an unconstrained HEAD route', t => { t.plan(3) const fastify = Fastify() fastify.route({ method: 'HEAD', url: '/', handler: (req, reply) => { reply.header('content-type', 'text/plain') reply.send('custom HEAD response') } }) fastify.route({ method: 'GET', url: '/', constraints: { host: 'fastify.io' }, handler: (req, reply) => { reply.send({ hello: 'from any other domain' }) } }) fastify.inject({ method: 'HEAD', url: '/', headers: { host: 'fastify.io' } }, (err, res) => { t.error(err) t.same(res.payload, 'custom HEAD response') t.equal(res.statusCode, 200) }) }) test('Will not try to re-createprefixed HEAD route if it already exists and exposeHeadRoutes is true for constrained routes', async (t) => { t.plan(1) const fastify = Fastify({ exposeHeadRoutes: true }) fastify.register((scope, opts, next) => { scope.route({ method: 'HEAD', path: '/route', constraints: { host: 'fastify.io' }, handler: (req, reply) => { reply.header('content-type', 'text/plain') reply.send('custom HEAD response') } }) scope.route({ method: 'GET', path: '/route', constraints: { host: 'fastify.io' }, handler: (req, reply) => { reply.send({ ok: true }) } }) next() }, { prefix: '/prefix' }) await fastify.ready() t.ok(true) }) test('allows separate constrained and unconstrained HEAD routes', async (t) => { t.plan(1) const fastify = Fastify({ exposeHeadRoutes: true }) fastify.register((scope, opts, next) => { scope.route({ method: 'HEAD', path: '/route', handler: (req, reply) => { reply.header('content-type', 'text/plain') reply.send('unconstrained HEAD response') } }) scope.route({ method: 'HEAD', path: '/route', constraints: { host: 'fastify.io' }, handler: (req, reply) => { reply.header('content-type', 'text/plain') reply.send('constrained HEAD response') } }) scope.route({ method: 'GET', path: '/route', constraints: { host: 'fastify.io' }, handler: (req, reply) => { reply.send({ ok: true }) } }) next() }, { prefix: '/prefix' }) await fastify.ready() t.ok(true) })