UNPKG

find-my-way

Version:

Crazy fast http radix based router

681 lines (597 loc) 23.1 kB
'use strict' const { test } = require('node:test') const FindMyWay = require('../') test('pretty print - empty tree', t => { t.plan(2) const findMyWay = FindMyWay() const tree = findMyWay.prettyPrint() const expected = '(empty tree)' t.assert.equal(typeof tree, 'string') t.assert.equal(tree, expected) }) test('pretty print - static routes', t => { t.plan(2) const findMyWay = FindMyWay() findMyWay.on('GET', '/test', () => {}) findMyWay.on('GET', '/test/hello', () => {}) findMyWay.on('GET', '/hello/world', () => {}) const tree = findMyWay.prettyPrint() const expected = `\ └── / ├── test (GET) │ └── /hello (GET) └── hello/world (GET) ` t.assert.equal(typeof tree, 'string') t.assert.equal(tree, expected) }) test('pretty print - parametric routes', t => { t.plan(2) const findMyWay = FindMyWay() findMyWay.on('GET', '/test', () => {}) findMyWay.on('GET', '/test/:hello', () => {}) findMyWay.on('GET', '/hello/:world', () => {}) const tree = findMyWay.prettyPrint() const expected = `\ └── / ├── test (GET) │ └── / │ └── :hello (GET) └── hello/ └── :world (GET) ` t.assert.equal(typeof tree, 'string') t.assert.equal(tree, expected) }) test('pretty print - parametric routes', t => { t.plan(2) const findMyWay = FindMyWay() findMyWay.on('GET', '/static', () => {}) findMyWay.on('GET', '/static/:param/suffix1', () => {}) findMyWay.on('GET', '/static/:param(123)/suffix2', () => {}) findMyWay.on('GET', '/static/:param(123).end/suffix3', () => {}) findMyWay.on('GET', '/static/:param1(123).:param2(456)/suffix4', () => {}) const tree = findMyWay.prettyPrint() const expected = `\ └── / └── static (GET) └── / ├── :param(123).end │ └── /suffix3 (GET) ├── :param(123) │ └── /suffix2 (GET) ├── :param1(123).:param2(456) │ └── /suffix4 (GET) └── :param └── /suffix1 (GET) ` t.assert.equal(typeof tree, 'string') t.assert.equal(tree, expected) }) test('pretty print - parametric routes', t => { t.plan(2) const findMyWay = FindMyWay() findMyWay.on('GET', '/static', () => {}) findMyWay.on('GET', '/static/:param/suffix1', () => {}) findMyWay.on('GET', '/static/:param(123)/suffix2', () => {}) findMyWay.on('GET', '/static/:param(123).end/suffix3', () => {}) findMyWay.on('GET', '/static/:param1(123).:param2(456)/suffix4', () => {}) const tree = findMyWay.prettyPrint({ commonPrefix: false }) const expected = `\ └── /static (GET) ├── /:param(123).end/suffix3 (GET) ├── /:param(123)/suffix2 (GET) ├── /:param1(123).:param2(456)/suffix4 (GET) └── /:param/suffix1 (GET) ` t.assert.equal(typeof tree, 'string') t.assert.equal(tree, expected) }) test('pretty print - mixed parametric routes', t => { t.plan(2) const findMyWay = FindMyWay() findMyWay.on('GET', '/test', () => {}) findMyWay.on('GET', '/test/:hello', () => {}) findMyWay.on('POST', '/test/:hello', () => {}) findMyWay.on('GET', '/test/:hello/world', () => {}) const tree = findMyWay.prettyPrint() const expected = `\ └── / └── test (GET) └── / └── :hello (GET, POST) └── /world (GET) ` t.assert.equal(typeof tree, 'string') t.assert.equal(tree, expected) }) test('pretty print - wildcard routes', t => { t.plan(2) const findMyWay = FindMyWay() findMyWay.on('GET', '/test', () => {}) findMyWay.on('GET', '/test/*', () => {}) findMyWay.on('GET', '/hello/*', () => {}) const tree = findMyWay.prettyPrint() const expected = `\ └── / ├── test (GET) │ └── / │ └── * (GET) └── hello/ └── * (GET) ` t.assert.equal(typeof tree, 'string') t.assert.equal(tree, expected) }) test('pretty print - parametric routes with same parent and followed by a static route which has the same prefix with the former routes', t => { t.plan(2) const findMyWay = FindMyWay() findMyWay.on('GET', '/test', () => {}) findMyWay.on('GET', '/test/hello/:id', () => {}) findMyWay.on('POST', '/test/hello/:id', () => {}) findMyWay.on('GET', '/test/helloworld', () => {}) const tree = findMyWay.prettyPrint() const expected = `\ └── / └── test (GET) └── /hello ├── / │ └── :id (GET, POST) └── world (GET) ` t.assert.equal(typeof tree, 'string') t.assert.equal(tree, expected) }) test('pretty print - constrained parametric routes', t => { t.plan(2) const findMyWay = FindMyWay() findMyWay.on('GET', '/test', () => {}) findMyWay.on('GET', '/test', { constraints: { host: 'auth.fastify.io' } }, () => {}) findMyWay.on('GET', '/test/:hello', () => {}) findMyWay.on('GET', '/test/:hello', { constraints: { version: '1.1.2' } }, () => {}) findMyWay.on('GET', '/test/:hello', { constraints: { version: '2.0.0' } }, () => {}) const tree = findMyWay.prettyPrint() const expected = `\ └── / └── test (GET) test (GET) {"host":"auth.fastify.io"} └── / └── :hello (GET) :hello (GET) {"version":"1.1.2"} :hello (GET) {"version":"2.0.0"} ` t.assert.equal(typeof tree, 'string') t.assert.equal(tree, expected) }) test('pretty print - multiple parameters are drawn appropriately', t => { t.plan(2) const findMyWay = FindMyWay() findMyWay.on('GET', '/test', () => {}) // routes with a nested parameter (i.e. no handler for the /:param) were breaking the display findMyWay.on('GET', '/test/:hello/there/:ladies', () => {}) findMyWay.on('GET', '/test/:hello/there/:ladies/and/:gents', () => {}) findMyWay.on('GET', '/test/are/:you/:ready/to/:rock', () => {}) const tree = findMyWay.prettyPrint({ commonPrefix: false }) const expected = `\ └── /test (GET) ├── /are/:you/:ready/to/:rock (GET) └── /:hello/there/:ladies (GET) └── /and/:gents (GET) ` t.assert.equal(typeof tree, 'string') t.assert.equal(tree, expected) }) test('pretty print - multiple parameters are drawn appropriately', t => { t.plan(2) const findMyWay = FindMyWay() findMyWay.on('GET', '/test', () => {}) // routes with a nested parameter (i.e. no handler for the /:param) were breaking the display findMyWay.on('GET', '/test/:hello/there/:ladies', () => {}) findMyWay.on('GET', '/test/:hello/there/:ladies/and/:gents', () => {}) findMyWay.on('GET', '/test/are/:you/:ready/to/:rock', () => {}) const tree = findMyWay.prettyPrint({ commonPrefix: false }) const expected = `\ └── /test (GET) ├── /are/:you/:ready/to/:rock (GET) └── /:hello/there/:ladies (GET) └── /and/:gents (GET) ` t.assert.equal(typeof tree, 'string') t.assert.equal(tree, expected) }) test('pretty print commonPrefix - use routes array to draw flattened routes', t => { t.plan(4) const findMyWay = FindMyWay() findMyWay.on('GET', '/test', () => {}) findMyWay.on('GET', '/test/hello', () => {}) findMyWay.on('GET', '/testing', () => {}) findMyWay.on('GET', '/testing/:param', () => {}) findMyWay.on('PUT', '/update', () => {}) const radixTree = findMyWay.prettyPrint({ commonPrefix: true }) const arrayTree = findMyWay.prettyPrint({ commonPrefix: false }) const radixExpected = `\ └── / ├── test (GET) │ ├── /hello (GET) │ └── ing (GET) │ └── / │ └── :param (GET) └── update (PUT) ` const arrayExpected = `\ ├── /test (GET) │ ├── /hello (GET) │ └── ing (GET) │ └── /:param (GET) └── /update (PUT) ` t.assert.equal(typeof radixTree, 'string') t.assert.equal(radixTree, radixExpected) t.assert.equal(typeof arrayTree, 'string') t.assert.equal(arrayTree, arrayExpected) }) test('pretty print commonPrefix - handle wildcard root', t => { t.plan(2) const findMyWay = FindMyWay() findMyWay.on('OPTIONS', '*', () => {}) findMyWay.on('GET', '/test/hello', () => {}) findMyWay.on('GET', '/testing', () => {}) findMyWay.on('GET', '/testing/:param', () => {}) findMyWay.on('PUT', '/update', () => {}) const arrayTree = findMyWay.prettyPrint({ commonPrefix: false }) const arrayExpected = `\ ├── /test/hello (GET) ├── /testing (GET) │ └── /:param (GET) ├── /update (PUT) └── * (OPTIONS) ` t.assert.equal(typeof arrayTree, 'string') t.assert.equal(arrayTree, arrayExpected) }) test('pretty print commonPrefix - handle wildcard root', t => { t.plan(2) const findMyWay = FindMyWay() findMyWay.on('GET', '*', () => {}) findMyWay.on('GET', '/test/hello', () => {}) findMyWay.on('GET', '/testing', () => {}) findMyWay.on('GET', '/testing/:param', () => {}) findMyWay.on('PUT', '/update', () => {}) const radixTree = findMyWay.prettyPrint() const radixExpected = `\ └── (empty root node) ├── / │ ├── test │ │ ├── /hello (GET) │ │ └── ing (GET) │ │ └── / │ │ └── :param (GET) │ └── update (PUT) └── * (GET) ` t.assert.equal(typeof radixTree, 'string') t.assert.equal(radixTree, radixExpected) }) test('pretty print commonPrefix - handle constrained routes', t => { t.plan(2) const findMyWay = FindMyWay() findMyWay.on('GET', '/test', () => {}) findMyWay.on('GET', '/test', { constraints: { host: 'auth.fastify.io' } }, () => {}) findMyWay.on('GET', '/test/:hello', () => {}) findMyWay.on('PUT', '/test/:hello', () => {}) findMyWay.on('GET', '/test/:hello', { constraints: { version: '1.1.2' } }, () => {}) findMyWay.on('GET', '/test/:hello', { constraints: { version: '2.0.0' } }, () => {}) const arrayTree = findMyWay.prettyPrint({ commonPrefix: false }) const arrayExpected = `\ └── /test (GET) /test (GET) {"host":"auth.fastify.io"} └── /:hello (GET, PUT) /:hello (GET) {"version":"1.1.2"} /:hello (GET) {"version":"2.0.0"} ` t.assert.equal(typeof arrayTree, 'string') t.assert.equal(arrayTree, arrayExpected) }) test('pretty print commonPrefix - handle method constraint', t => { t.plan(2) const findMyWay = FindMyWay() findMyWay.addConstraintStrategy({ name: 'method', storage: function () { const handlers = {} return { get: (type) => { return handlers[type] || null }, set: (type, store) => { handlers[type] = store } } }, deriveConstraint: (req) => req.headers['x-method'], mustMatchWhenDerived: true }) findMyWay.on('GET', '/test', () => {}) findMyWay.on('GET', '/test', { constraints: { method: 'foo' } }, () => {}) findMyWay.on('GET', '/test/:hello', () => {}) findMyWay.on('PUT', '/test/:hello', () => {}) findMyWay.on('GET', '/test/:hello', { constraints: { method: 'bar' } }, () => {}) findMyWay.on('GET', '/test/:hello', { constraints: { method: 'baz' } }, () => {}) const arrayTree = findMyWay.prettyPrint({ commonPrefix: false, methodConstraintName: 'methodOverride' }) const arrayExpected = `\ └── /test (GET) /test (GET) {"method":"foo"} └── /:hello (GET, PUT) /:hello (GET) {"method":"bar"} /:hello (GET) {"method":"baz"} ` t.assert.equal(typeof arrayTree, 'string') t.assert.equal(arrayTree, arrayExpected) }) test('pretty print includeMeta - commonPrefix: true', t => { t.plan(6) const findMyWay = FindMyWay() const namedFunction = () => {} const store = { onRequest: [() => {}, namedFunction], onTimeout: [() => {}], genericMeta: 'meta', mixedMeta: ['mixed items', { an: 'object' }], objectMeta: { one: '1', two: 2 }, functionMeta: namedFunction } store[Symbol('symbolKey')] = Symbol('symbolValue') findMyWay.on('GET', '/test', () => {}, store) findMyWay.on('GET', '/test', { constraints: { host: 'auth.fastify.io' } }, () => {}, store) findMyWay.on('GET', '/testing/:hello', () => {}, store) findMyWay.on('PUT', '/tested/:hello', () => {}, store) findMyWay.on('GET', '/test/:hello', { constraints: { version: '1.1.2' } }, () => {}) findMyWay.on('GET', '/test/:hello', { constraints: { version: '2.0.0' } }, () => {}) const radixTree = findMyWay.prettyPrint({ commonPrefix: true, includeMeta: true }) const radixTreeExpected = `\ └── / └── test (GET) • (onRequest) ["anonymous()","namedFunction()"] • (onTimeout) ["anonymous()"] • (genericMeta) "meta" • (mixedMeta) ["mixed items",{"an":"object"}] • (objectMeta) {"one":"1","two":2} • (functionMeta) "namedFunction()" • (Symbol(symbolKey)) "Symbol(symbolValue)" test (GET) {"host":"auth.fastify.io"} • (onRequest) ["anonymous()","namedFunction()"] • (onTimeout) ["anonymous()"] • (genericMeta) "meta" • (mixedMeta) ["mixed items",{"an":"object"}] • (objectMeta) {"one":"1","two":2} • (functionMeta) "namedFunction()" • (Symbol(symbolKey)) "Symbol(symbolValue)" ├── ing/ │ └── :hello (GET) │ • (onRequest) ["anonymous()","namedFunction()"] │ • (onTimeout) ["anonymous()"] │ • (genericMeta) "meta" │ • (mixedMeta) ["mixed items",{"an":"object"}] │ • (objectMeta) {"one":"1","two":2} │ • (functionMeta) "namedFunction()" │ • (Symbol(symbolKey)) "Symbol(symbolValue)" ├── ed/ │ └── :hello (PUT) │ • (onRequest) ["anonymous()","namedFunction()"] │ • (onTimeout) ["anonymous()"] │ • (genericMeta) "meta" │ • (mixedMeta) ["mixed items",{"an":"object"}] │ • (objectMeta) {"one":"1","two":2} │ • (functionMeta) "namedFunction()" │ • (Symbol(symbolKey)) "Symbol(symbolValue)" └── / └── :hello (GET) {"version":"1.1.2"} :hello (GET) {"version":"2.0.0"} ` const radixTreeSpecific = findMyWay.prettyPrint({ commonPrefix: true, includeMeta: ['onTimeout', 'objectMeta', 'nonExistent'] }) const radixTreeSpecificExpected = `\ └── / └── test (GET) • (onTimeout) ["anonymous()"] • (objectMeta) {"one":"1","two":2} test (GET) {"host":"auth.fastify.io"} • (onTimeout) ["anonymous()"] • (objectMeta) {"one":"1","two":2} ├── ing/ │ └── :hello (GET) │ • (onTimeout) ["anonymous()"] │ • (objectMeta) {"one":"1","two":2} ├── ed/ │ └── :hello (PUT) │ • (onTimeout) ["anonymous()"] │ • (objectMeta) {"one":"1","two":2} └── / └── :hello (GET) {"version":"1.1.2"} :hello (GET) {"version":"2.0.0"} ` const radixTreeNoMeta = findMyWay.prettyPrint({ commonPrefix: true, includeMeta: false }) const radixTreeNoMetaExpected = `\ └── / └── test (GET) test (GET) {"host":"auth.fastify.io"} ├── ing/ │ └── :hello (GET) ├── ed/ │ └── :hello (PUT) └── / └── :hello (GET) {"version":"1.1.2"} :hello (GET) {"version":"2.0.0"} ` t.assert.equal(typeof radixTree, 'string') t.assert.equal(radixTree, radixTreeExpected) t.assert.equal(typeof radixTreeSpecific, 'string') t.assert.equal(radixTreeSpecific, radixTreeSpecificExpected) t.assert.equal(typeof radixTreeNoMeta, 'string') t.assert.equal(radixTreeNoMeta, radixTreeNoMetaExpected) }) test('pretty print includeMeta - commonPrefix: false', t => { t.plan(6) const findMyWay = FindMyWay() const namedFunction = () => {} const store = { onRequest: [() => {}, namedFunction], onTimeout: [() => {}], onError: null, onRegister: undefined, genericMeta: 'meta', mixedMeta: ['mixed items', { an: 'object' }], objectMeta: { one: '1', two: 2 }, functionMeta: namedFunction } store[Symbol('symbolKey')] = Symbol('symbolValue') findMyWay.on('GET', '/test', () => {}, store) findMyWay.on('GET', '/test', { constraints: { host: 'auth.fastify.io' } }, () => {}, store) findMyWay.on('GET', '/testing/:hello', () => {}, store) findMyWay.on('PUT', '/tested/:hello', () => {}, store) findMyWay.on('GET', '/test/:hello', { constraints: { version: '1.1.2' } }, () => {}) findMyWay.on('GET', '/test/:hello', { constraints: { version: '2.0.0' } }, () => {}) const arrayTree = findMyWay.prettyPrint({ commonPrefix: false, includeMeta: true }) const arrayExpected = `\ └── /test (GET) • (onRequest) ["anonymous()","namedFunction()"] • (onTimeout) ["anonymous()"] • (genericMeta) "meta" • (mixedMeta) ["mixed items",{"an":"object"}] • (objectMeta) {"one":"1","two":2} • (functionMeta) "namedFunction()" • (Symbol(symbolKey)) "Symbol(symbolValue)" /test (GET) {"host":"auth.fastify.io"} • (onRequest) ["anonymous()","namedFunction()"] • (onTimeout) ["anonymous()"] • (genericMeta) "meta" • (mixedMeta) ["mixed items",{"an":"object"}] • (objectMeta) {"one":"1","two":2} • (functionMeta) "namedFunction()" • (Symbol(symbolKey)) "Symbol(symbolValue)" ├── ing/:hello (GET) │ • (onRequest) ["anonymous()","namedFunction()"] │ • (onTimeout) ["anonymous()"] │ • (genericMeta) "meta" │ • (mixedMeta) ["mixed items",{"an":"object"}] │ • (objectMeta) {"one":"1","two":2} │ • (functionMeta) "namedFunction()" │ • (Symbol(symbolKey)) "Symbol(symbolValue)" ├── ed/:hello (PUT) │ • (onRequest) ["anonymous()","namedFunction()"] │ • (onTimeout) ["anonymous()"] │ • (genericMeta) "meta" │ • (mixedMeta) ["mixed items",{"an":"object"}] │ • (objectMeta) {"one":"1","two":2} │ • (functionMeta) "namedFunction()" │ • (Symbol(symbolKey)) "Symbol(symbolValue)" └── /:hello (GET) {"version":"1.1.2"} /:hello (GET) {"version":"2.0.0"} ` const arraySpecific = findMyWay.prettyPrint({ commonPrefix: false, includeMeta: ['onRequest', 'mixedMeta', 'nonExistent'] }) const arraySpecificExpected = `\ └── /test (GET) • (onRequest) ["anonymous()","namedFunction()"] • (mixedMeta) ["mixed items",{"an":"object"}] /test (GET) {"host":"auth.fastify.io"} • (onRequest) ["anonymous()","namedFunction()"] • (mixedMeta) ["mixed items",{"an":"object"}] ├── ing/:hello (GET) │ • (onRequest) ["anonymous()","namedFunction()"] │ • (mixedMeta) ["mixed items",{"an":"object"}] ├── ed/:hello (PUT) │ • (onRequest) ["anonymous()","namedFunction()"] │ • (mixedMeta) ["mixed items",{"an":"object"}] └── /:hello (GET) {"version":"1.1.2"} /:hello (GET) {"version":"2.0.0"} ` const arrayNoMeta = findMyWay.prettyPrint({ commonPrefix: false, includeMeta: false }) const arrayNoMetaExpected = `\ └── /test (GET) /test (GET) {"host":"auth.fastify.io"} ├── ing/:hello (GET) ├── ed/:hello (PUT) └── /:hello (GET) {"version":"1.1.2"} /:hello (GET) {"version":"2.0.0"} ` t.assert.equal(typeof arrayTree, 'string') t.assert.equal(arrayTree, arrayExpected) t.assert.equal(typeof arraySpecific, 'string') t.assert.equal(arraySpecific, arraySpecificExpected) t.assert.equal(typeof arrayNoMeta, 'string') t.assert.equal(arrayNoMeta, arrayNoMetaExpected) }) test('pretty print includeMeta - buildPrettyMeta function', t => { t.plan(4) const findMyWay = FindMyWay({ buildPrettyMeta: route => { return { metaKey: route.method === 'PUT' ? 'Hide PUT route path' : route.path } } }) const namedFunction = () => {} const store = { onRequest: [() => {}, namedFunction], onTimeout: [() => {}], genericMeta: 'meta', mixedMeta: ['mixed items', { an: 'object' }], objectMeta: { one: '1', two: 2 }, functionMeta: namedFunction } store[Symbol('symbolKey')] = Symbol('symbolValue') findMyWay.on('GET', '/test', () => {}, store) findMyWay.on('GET', '/test', { constraints: { host: 'auth.fastify.io' } }, () => {}, store) findMyWay.on('GET', '/test/:hello', () => {}, store) findMyWay.on('PUT', '/test/:hello', () => {}, store) findMyWay.on('POST', '/test/:hello', () => {}, store) findMyWay.on('GET', '/test/:hello', { constraints: { version: '1.1.2' } }, () => {}) findMyWay.on('GET', '/test/:hello', { constraints: { version: '2.0.0' } }, () => {}) const arrayTree = findMyWay.prettyPrint({ commonPrefix: false, includeMeta: true }) const arrayExpected = `\ └── /test (GET) • (metaKey) "/test" /test (GET) {"host":"auth.fastify.io"} • (metaKey) "/test" └── /:hello (GET, POST) • (metaKey) "/test/:hello" /:hello (PUT) • (metaKey) "Hide PUT route path" /:hello (GET) {"version":"1.1.2"} • (metaKey) "/test/:hello" /:hello (GET) {"version":"2.0.0"} • (metaKey) "/test/:hello" ` const radixTree = findMyWay.prettyPrint({ includeMeta: true }) const radixExpected = `\ └── / └── test (GET) • (metaKey) "/test" test (GET) {"host":"auth.fastify.io"} • (metaKey) "/test" └── / └── :hello (GET, POST) • (metaKey) "/test/:hello" :hello (PUT) • (metaKey) "Hide PUT route path" :hello (GET) {"version":"1.1.2"} • (metaKey) "/test/:hello" :hello (GET) {"version":"2.0.0"} • (metaKey) "/test/:hello" ` t.assert.equal(typeof arrayTree, 'string') t.assert.equal(arrayTree, arrayExpected) t.assert.equal(typeof radixTree, 'string') t.assert.equal(radixTree, radixExpected) }) test('pretty print - print all methods', t => { t.plan(2) const findMyWay = FindMyWay() findMyWay.all('/test', () => {}) const tree = findMyWay.prettyPrint() const expected = `\ └── / └── test (ACL, BIND, CHECKOUT, CONNECT, COPY, DELETE, GET, HEAD, LINK, LOCK, \ M-SEARCH, MERGE, MKACTIVITY, MKCALENDAR, MKCOL, MOVE, NOTIFY, OPTIONS, PATCH, \ POST, PROPFIND, PROPPATCH, PURGE, PUT, QUERY, REBIND, REPORT, SEARCH, SOURCE, \ SUBSCRIBE, TRACE, UNBIND, UNLINK, UNLOCK, UNSUBSCRIBE) ` t.assert.equal(typeof tree, 'string') t.assert.equal(tree, expected) })