/* global describe, it, beforeEach */ 'use strict'; const Router = require('@arangodb/foxx/router/router'); const Tree = require('@arangodb/foxx/router/tree'); const expect = require('chai').expect; const $_WILDCARD = Symbol.for('@@wildcard'); // catch-all suffix const $_TERMINAL = Symbol.for('@@terminal'); // terminal -- routes be here const $_PARAM = Symbol.for('@@parameter'); // named parameter (no routes here, like static part) const $_ROUTES = Symbol.for('@@routes'); // routes and child routers const $_MIDDLEWARE = Symbol.for('@@middleware'); // middleware (not including router.all) // const _ = require('lodash') // function log(something) { // const seen = new Set() // function objectify(thing) { // if (seen.has(thing)) return thing // seen.add(thing) // if (!thing) return thing // if (Array.isArray(thing)) return thing.map(objectify) // if (typeof thing === 'function') return String(thing) // if (typeof thing === 'symbol') return String(thing) // if (typeof thing !== 'object') return thing // if (thing.handler) { // return `Context(${thing.handler})` // } // if (thing.tree) { // return { // '@@router': objectify(thing.tree.root) // } // } // if (thing.router) { // return { // path: thing.path, // '@@router': objectify(thing.tree.root) // } // } // const obj = {} // if (thing instanceof Map) { // for (const entry of thing.entries()) { // obj[String(entry[0])] = objectify(entry[1]) // } // } else { // _.each(thing, function (value, key) { // obj[String(key)] = objectify(value) // }) // } // return obj // } // console.infoLines(JSON.stringify(objectify(something), null, 2)) // } describe('Tree', function () { let router, childRouter1, childRouter2; let GET_SLASH; let USE_SLASH; let POST_HELLO; let GET_HELLO_WORLD; let USE_HELLO_WORLD; let GET_HELLO_PARAM; let USE_HELLO_PARAM; let GET_WORLD; let GET_SLASH2; let GET_ALL; let GET_HELLO_WORLD2; let GET_POTATO_SALAD1; let GET_POTATO_SALAD2; let CHILD1; let CHILD2; function prepareRouter (router, childRouter1, childRouter2) { router.use('/hello', childRouter1); CHILD1 = router._routes[router._routes.length - 1]; router.use(childRouter2); CHILD2 = router._routes[router._routes.length - 1]; GET_SLASH = router.get('/', function () {}); USE_SLASH = router.use(function () {}); POST_HELLO = router.post('/hello', function () {}); GET_HELLO_WORLD = router.get('/hello/world', function () {}); USE_HELLO_WORLD = router.use('/hello/world', function () {}); GET_HELLO_PARAM = router.get('/hello/:thing', function () {}); USE_HELLO_PARAM = router.use('/hello/:thang', function () {}); GET_WORLD = childRouter1.get('/world', function () {}); GET_SLASH2 = childRouter2.get(function () {}); GET_ALL = childRouter2.get('/*', function () {}); GET_HELLO_WORLD2 = childRouter2.get('/hello/world', function () {}); GET_POTATO_SALAD1 = childRouter2.get('/potato/salad', function () {}); GET_POTATO_SALAD2 = childRouter2.get('/potato/salad', function () {}); } beforeEach(function () { router = new Router(); childRouter1 = new Router(); childRouter2 = new Router(); prepareRouter(router, childRouter1, childRouter2); }); describe('constructor', function () { it('creates the correct tree', function () { const tree = new Tree({}, router).root; expect(tree.size).to.equal(3); expect(tree.get($_TERMINAL).size).to.equal(1); expect(tree.get($_TERMINAL).get($_ROUTES)) .to.eql([GET_SLASH]); expect(tree.get('hello').size).to.equal(4); expect(tree.get('hello').get($_TERMINAL).size).to.equal(1); expect(tree.get('hello').get($_TERMINAL).get($_ROUTES)) .to.eql([POST_HELLO]); expect(tree.get('hello').get('world').size).to.equal(2); expect(tree.get('hello').get('world').get($_TERMINAL).size).to.equal(1); expect(tree.get('hello').get('world').get($_TERMINAL).get($_ROUTES)) .to.eql([GET_HELLO_WORLD]); expect(tree.get('hello').get('world').get($_WILDCARD).size).to.equal(1); expect(tree.get('hello').get('world').get($_WILDCARD).get($_MIDDLEWARE)) .to.eql([USE_HELLO_WORLD]); expect(tree.get('hello').get($_PARAM).size).to.equal(2); expect(tree.get('hello').get($_PARAM).get($_TERMINAL).size).to.equal(1); expect(tree.get('hello').get($_PARAM).get($_TERMINAL).get($_ROUTES)) .to.eql([GET_HELLO_PARAM]); expect(tree.get('hello').get($_PARAM).get($_WILDCARD).size).to.equal(1); expect(tree.get('hello').get($_PARAM).get($_WILDCARD).get($_MIDDLEWARE)) .to.eql([USE_HELLO_PARAM]); expect(tree.get('hello').get($_WILDCARD).size).to.equal(1); expect(tree.get('hello').get($_WILDCARD).get($_ROUTES).length).to.equal(1); const child1 = tree.get('hello').get($_WILDCARD).get($_ROUTES)[0].tree.root; expect(child1.size).to.equal(1); expect(child1.get('world').size).to.equal(1); expect(child1.get('world').get($_TERMINAL).size).to.equal(1); expect(child1.get('world').get($_TERMINAL).get($_ROUTES)) .to.eql([GET_WORLD]); expect(tree.get($_WILDCARD).size).to.equal(2); expect(tree.get($_WILDCARD).get($_MIDDLEWARE)) .to.eql([USE_SLASH]); const child2 = tree.get($_WILDCARD).get($_ROUTES)[0].tree.root; expect(child2.size).to.equal(4); expect(child2.get($_TERMINAL).size).to.equal(1); expect(child2.get($_TERMINAL).get($_ROUTES)) .to.eql([GET_SLASH2]); expect(child2.get('hello').size).to.equal(1); expect(child2.get('hello').get('world').size).to.equal(1); expect(child2.get('hello').get('world').get($_TERMINAL).size).to.equal(1); expect(child2.get('hello').get('world').get($_TERMINAL).get($_ROUTES)) .to.eql([GET_HELLO_WORLD2]); expect(child2.get('potato').size).to.equal(1); expect(child2.get('potato').get('salad').size).to.equal(1); expect(child2.get('potato').get('salad').get($_TERMINAL).size).to.equal(1); expect(child2.get('potato').get('salad').get($_TERMINAL).get($_ROUTES)) .to.eql([GET_POTATO_SALAD1, GET_POTATO_SALAD2]); expect(child2.get($_WILDCARD).size).to.equal(1); expect(child2.get($_WILDCARD).get($_ROUTES)) .to.eql([GET_ALL]); }); }); describe('findRoutes', function () { let tree; beforeEach(function () { tree = new Tree({}, router); }); it('finds all routes for /', function () { const matches = []; for (const route of tree.findRoutes([])) { matches.push(route); } expect(matches.length).to.equal(3); expect(matches.some(function (match) { const i = match.length - 1; if (match[i].endpoint !== GET_SLASH) { return false; } expect(match).to.eql([ {router: router, path: [], suffix: []}, {middleware: USE_SLASH, path: [], suffix: []}, {endpoint: GET_SLASH, path: [], suffix: []} ]); return true; })).to.equal(true); expect(matches.some(function (match) { const i = match.length - 1; if (match[i].endpoint !== GET_SLASH2) { return false; } expect(match).to.eql([ {router: router, path: [], suffix: []}, {middleware: USE_SLASH, path: [], suffix: []}, {router: CHILD2, path: [], suffix: []}, {endpoint: GET_SLASH2, path: [], suffix: []} ]); return true; })).to.equal(true); expect(matches.some(function (match) { const i = match.length - 1; if (match[i].endpoint !== GET_ALL) { return false; } expect(match).to.eql([ {router: router, path: [], suffix: []}, {middleware: USE_SLASH, path: [], suffix: []}, {router: CHILD2, path: [], suffix: []}, {endpoint: GET_ALL, path: [], suffix: []} ]); return true; })).to.equal(true); }); it('finds all routes for /hello', function () { const matches = []; for (const route of tree.findRoutes(['hello'])) { matches.push(route); } expect(matches.length).to.equal(2); expect(matches.some(function (match) { const i = match.length - 1; if (match[i].endpoint !== POST_HELLO) { return false; } expect(match).to.eql([ {router: router, path: [], suffix: ['hello']}, {middleware: USE_SLASH, path: [], suffix: ['hello']}, {endpoint: POST_HELLO, path: ['hello'], suffix: []} ]); return true; })).to.equal(true); expect(matches.some(function (match) { const i = match.length - 1; if (match[i].endpoint !== GET_ALL) { return false; } expect(match).to.eql([ {router: router, path: [], suffix: ['hello']}, {middleware: USE_SLASH, path: [], suffix: ['hello']}, {router: CHILD2, path: [], suffix: ['hello']}, {endpoint: GET_ALL, path: [], suffix: ['hello']} ]); return true; })).to.equal(true); }); it('finds all routes for /hello/world', function () { const matches = []; for (const route of tree.findRoutes(['hello', 'world'])) { matches.push(route); } expect(matches.length).to.equal(5); expect(matches.some(function (match) { const i = match.length - 1; if (match[i].endpoint !== GET_HELLO_WORLD) { return false; } expect(match).to.eql([ {router: router, path: [], suffix: ['hello', 'world']}, {middleware: USE_SLASH, path: [], suffix: ['hello', 'world']}, {middleware: USE_HELLO_WORLD, path: ['hello', 'world'], suffix: []}, {endpoint: GET_HELLO_WORLD, path: ['hello', 'world'], suffix: []} ]); return true; })).to.equal(true); expect(matches.some(function (match) { const i = match.length - 1; if (match[i].endpoint !== GET_HELLO_PARAM) { return false; } expect(match).to.eql([ {router: router, path: [], suffix: ['hello', 'world']}, {middleware: USE_SLASH, path: [], suffix: ['hello', 'world']}, {middleware: USE_HELLO_PARAM, path: ['hello', 'world'], suffix: []}, {endpoint: GET_HELLO_PARAM, path: ['hello', 'world'], suffix: []} ]); return true; })).to.equal(true); expect(matches.some(function (match) { const i = match.length - 1; if (match[i].endpoint !== GET_WORLD) { return false; } expect(match).to.eql([ {router: router, path: [], suffix: ['hello', 'world']}, {middleware: USE_SLASH, path: [], suffix: ['hello', 'world']}, {router: CHILD1, path: ['hello'], suffix: ['world']}, {endpoint: GET_WORLD, path: ['world'], suffix: []} ]); return true; })).to.equal(true); expect(matches.some(function (match) { const i = match.length - 1; if (match[i].endpoint !== GET_HELLO_WORLD2) { return false; } expect(match).to.eql([ {router: router, path: [], suffix: ['hello', 'world']}, {middleware: USE_SLASH, path: [], suffix: ['hello', 'world']}, {router: CHILD2, path: [], suffix: ['hello', 'world']}, {endpoint: GET_HELLO_WORLD2, path: ['hello', 'world'], suffix: []} ]); return true; })).to.equal(true); expect(matches.some(function (match) { const i = match.length - 1; if (match[i].endpoint !== GET_ALL) { return false; } expect(match).to.eql([ {router: router, path: [], suffix: ['hello', 'world']}, {middleware: USE_SLASH, path: [], suffix: ['hello', 'world']}, {router: CHILD2, path: [], suffix: ['hello', 'world']}, {endpoint: GET_ALL, path: [], suffix: ['hello', 'world']} ]); return true; })).to.equal(true); }); it('finds all routes for /hello/mlady', function () { const matches = []; for (const route of tree.findRoutes(['hello', 'mlady'])) { matches.push(route); } expect(matches.length).to.equal(2); expect(matches.some(function (match) { const i = match.length - 1; if (match[i].endpoint !== GET_HELLO_PARAM) { return false; } expect(match).to.eql([ {router: router, path: [], suffix: ['hello', 'mlady']}, {middleware: USE_SLASH, path: [], suffix: ['hello', 'mlady']}, {middleware: USE_HELLO_PARAM, path: ['hello', 'mlady'], suffix: []}, {endpoint: GET_HELLO_PARAM, path: ['hello', 'mlady'], suffix: []} ]); return true; })).to.equal(true); expect(matches.some(function (match) { const i = match.length - 1; if (match[i].endpoint !== GET_ALL) { return false; } expect(match).to.eql([ {router: router, path: [], suffix: ['hello', 'mlady']}, {middleware: USE_SLASH, path: [], suffix: ['hello', 'mlady']}, {router: CHILD2, path: [], suffix: ['hello', 'mlady']}, {endpoint: GET_ALL, path: [], suffix: ['hello', 'mlady']} ]); return true; })).to.equal(true); }); it('finds all routes for /potato/salad', function () { const matches = []; for (const route of tree.findRoutes(['potato', 'salad'])) { matches.push(route); } expect(matches.length).to.equal(3); expect(matches.some(function (match) { const i = match.length - 1; if (match[i].endpoint !== GET_POTATO_SALAD1) { return false; } expect(match).to.eql([ {router: router, path: [], suffix: ['potato', 'salad']}, {middleware: USE_SLASH, path: [], suffix: ['potato', 'salad']}, {router: CHILD2, path: [], suffix: ['potato', 'salad']}, {endpoint: GET_POTATO_SALAD1, path: ['potato', 'salad'], suffix: []} ]); return true; })).to.equal(true); expect(matches.some(function (match) { const i = match.length - 1; if (match[i].endpoint !== GET_POTATO_SALAD2) { return false; } expect(match).to.eql([ {router: router, path: [], suffix: ['potato', 'salad']}, {middleware: USE_SLASH, path: [], suffix: ['potato', 'salad']}, {router: CHILD2, path: [], suffix: ['potato', 'salad']}, {endpoint: GET_POTATO_SALAD2, path: ['potato', 'salad'], suffix: []} ]); return true; })).to.equal(true); expect(matches.some(function (match) { const i = match.length - 1; if (match[i].endpoint !== GET_ALL) { return false; } expect(match).to.eql([ {router: router, path: [], suffix: ['potato', 'salad']}, {middleware: USE_SLASH, path: [], suffix: ['potato', 'salad']}, {router: CHILD2, path: [], suffix: ['potato', 'salad']}, {endpoint: GET_ALL, path: [], suffix: ['potato', 'salad']} ]); return true; })).to.equal(true); }); }); });