3 const assert
= require('node:assert');
4 const sinon
= require('sinon');
5 const Router
= require('../../lib/router');
6 const PathParameter
= require('../../lib/router/path-parameter');
7 const { DingusError
, RouterNoPathError
, RouterNoMethodError
} = require('../../lib/errors');
9 const noExpectedException
= 'did not get expected exception';
11 describe('Router', function () {
12 const router
= new Router();
13 let _its
; // Save and restore ignoreTrailingSlash
15 beforeEach(function () {
16 _its
= router
.ignoreTrailingSlash
;
19 afterEach(function () {
21 router
.ignoreTrailingSlash
= _its
;
24 describe('_pathToRoutePath', function () {
25 it('defines a simple path', function () {
27 const expected
= ['', 'a', 'b', 'c'];
28 expected
[Router
.kPathMethods
] = {};
29 const r
= router
._pathToRoutePath(p
);
30 assert
.deepStrictEqual(r
, expected
);
32 it('defines a path with parameter', function () {
34 const expected
= ['', 'a', 'b', new PathParameter('c')];
35 expected
[Router
.kPathMethods
] = {};
36 const r
= router
._pathToRoutePath(p
);
37 assert
.deepStrictEqual(r
, expected
);
39 it('defines a path with trailing slash', function () {
40 router
.ignoreTrailingSlash
= true;
42 const expected
= ['', 'a', 'b', new PathParameter('c')];
43 expected
[Router
.kPathMethods
] = {};
44 const r
= router
._pathToRoutePath(p
);
45 assert
.deepStrictEqual(r
, expected
);
47 it('defines a path with escaped parameter', function () {
48 const p
= '/a/\\:b/c';
49 const expected
= ['', 'a', ':b', 'c'];
50 expected
[Router
.kPathMethods
] = {};
51 const r
= router
._pathToRoutePath(p
);
52 assert
.deepStrictEqual(r
, expected
);
54 }); // _pathToRoutePath
56 describe('_pathCompareExact', function () {
57 let fixedPath
, checkPath
;
59 it('compares static paths which match', function () {
60 fixedPath
= router
._pathToRoutePath('/a/b/c');
61 checkPath
= router
._pathToRoutePath('/a/b/c');
62 const r
= Router
._pathCompareExact(fixedPath
, checkPath
);
63 assert
.strictEqual(r
, true);
65 it('compares static paths which do not match', function () {
66 fixedPath
= router
._pathToRoutePath('/a/b/c');
67 checkPath
= router
._pathToRoutePath('/a/b/d');
68 const r
= Router
._pathCompareExact(fixedPath
, checkPath
);
69 assert
.strictEqual(r
, false);
71 it('compares unequal static paths', function () {
72 fixedPath
= router
._pathToRoutePath('/a/b/c');
73 checkPath
= router
._pathToRoutePath('/a/b');
74 const r
= Router
._pathCompareExact(fixedPath
, checkPath
);
75 assert
.strictEqual(r
, false);
77 it('compares param paths which match', function () {
78 fixedPath
= router
._pathToRoutePath('/a/:b/c');
79 checkPath
= router
._pathToRoutePath('/a/:b/c');
80 const r
= Router
._pathCompareExact(fixedPath
, checkPath
);
81 assert
.strictEqual(r
, true);
83 it('compares param paths which do not match', function () {
84 fixedPath
= router
._pathToRoutePath('/a/:b/c');
85 checkPath
= router
._pathToRoutePath('/a/:g/c');
86 const r
= Router
._pathCompareExact(fixedPath
, checkPath
);
87 assert
.strictEqual(r
, false);
89 }); // _pathCompareExact
91 describe('_pathCompareParam', function () {
92 let fixedPath
, checkPath
;
94 it('compares static paths which match', function () {
96 const expectedParams
= {};
97 fixedPath
= router
._pathToRoutePath('/a/b/c');
98 checkPath
= router
._pathToRoutePath('/a/b/c');
99 const r
= Router
._pathCompareParam(fixedPath
, checkPath
);
100 assert
.strictEqual(r
, true);
101 assert
.deepStrictEqual(params
, expectedParams
);
103 it('compares static paths which do not match', function () {
105 const expectedParams
= {};
106 fixedPath
= router
._pathToRoutePath('/a/b/c');
107 checkPath
= router
._pathToRoutePath('/a/b/d');
108 const r
= Router
._pathCompareParam(fixedPath
, checkPath
, params
);
109 assert
.strictEqual(r
, false);
110 assert
.deepStrictEqual(params
, expectedParams
);
112 it('compares unequal static paths', function () {
114 const expectedParams
= {};
115 fixedPath
= router
._pathToRoutePath('/a/b/c');
116 checkPath
= router
._pathToRoutePath('/a/b');
117 const r
= Router
._pathCompareParam(fixedPath
, checkPath
, params
);
118 assert
.strictEqual(r
, false);
119 assert
.deepStrictEqual(params
, expectedParams
);
121 it('compares param paths which match', function () {
123 const expectedParams
= {
126 fixedPath
= router
._pathToRoutePath('/a/:b/c');
127 checkPath
= router
._pathToRoutePath('/a/bar/c');
128 const r
= Router
._pathCompareParam(fixedPath
, checkPath
, params
);
129 assert
.strictEqual(r
, true);
130 assert
.deepStrictEqual(params
, expectedParams
);
132 it('compares multi param path which match', function () {
134 const expectedParams
= {
138 fixedPath
= router
._pathToRoutePath('/a/:b/:c');
139 checkPath
= router
._pathToRoutePath('/a/gaz/123');
140 const r
= Router
._pathCompareParam(fixedPath
, checkPath
, params
);
141 assert
.strictEqual(r
, true);
142 assert
.deepStrictEqual(params
, expectedParams
);
144 }); // _pathCompareParam
146 describe('_pathFind / _pathFindExact', function () {
147 let pathsByLengthOrig
;
149 beforeEach(function () {
150 pathsByLengthOrig
= router
.pathsByLength
;
151 router
.pathsByLength
= {
152 2: [ router
._pathToRoutePath('/:id') ],
153 3: [ router
._pathToRoutePath('/a/b') ],
156 afterEach(function () {
157 router
.pathsByLength
= pathsByLengthOrig
;
160 describe('_pathFind', function () {
161 it('finds a path', function () {
162 const pathParts
= ['', '46123f1e-bdca-40ee-9f62-93ad38647aa1'];
163 const { matchedPath
, pathParams
} = router
._pathFind(pathParts
);
164 assert
.strictEqual(matchedPath
, router
.pathsByLength
[2][0]);
165 assert
.deepStrictEqual(pathParams
, { id: pathParts
[1] });
167 it('does not find a path', function () {
168 const pathParts
= ['', 'flarp', 'baz'];
169 const { matchedPath
} = router
._pathFind(pathParts
);
170 assert
.strictEqual(matchedPath
, undefined);
174 describe('_pathFindExact', function () {
175 it('finds a path', function () {
176 const pathParts
= ['', new PathParameter('id')];
177 const r
= router
._pathFindExact(pathParts
);
178 assert
.strictEqual(r
, router
.pathsByLength
[2][0]);
180 it('does not find a path', function () {
181 const pathParts
= ['', 'flarp', 'baz'];
182 const r
= router
._pathFindExact(pathParts
);
183 assert
.strictEqual(r
, undefined);
185 }); // _pathFindExact
187 }); // _pathFind / _pathFindExact
189 describe('on', function () {
190 let pathsByLengthOrig
;
191 const stubHandler
= () => {};
193 handler: stubHandler
,
197 beforeEach(function () {
198 pathsByLengthOrig
= router
.pathsByLength
;
199 router
.pathsByLength
= {
200 2: [ router
._pathToRoutePath('/:id') ],
203 afterEach(function () {
204 router
.pathsByLength
= pathsByLengthOrig
;
207 it('adds new path', function () {
208 const urlPath
= '/a/:id';
209 const expected
= router
._pathToRoutePath(urlPath
);
210 expected
[Router
.kPathMethods
]['GET'] = stubEntry
;
211 router
.on('GET', urlPath
, stubHandler
);
212 assert
.deepStrictEqual(router
.pathsByLength
[3][0], expected
);
215 it('adds new method to path', function () {
216 const urlPath
= '/a/:id';
217 const expected
= router
._pathToRoutePath(urlPath
);
218 expected
[Router
.kPathMethods
]['GET'] = stubEntry
;
219 expected
[Router
.kPathMethods
]['POST'] = stubEntry
;
220 router
.on('GET', urlPath
, stubHandler
);
222 router
.on('POST', urlPath
, stubHandler
);
223 assert
.deepStrictEqual(router
.pathsByLength
[3][0], expected
);
226 it('add some more paths', function () {
227 let urlPath
= '/a/b/c/d';
228 const expected
= router
._pathToRoutePath(urlPath
);
229 expected
[Router
.kPathMethods
]['GET'] = stubEntry
;
230 router
.on('GET', urlPath
, stubHandler
);
231 urlPath
= '/a/b/x/y';
232 router
.on('GET', urlPath
, stubHandler
);
234 assert
.strictEqual(router
.pathsByLength
[5].length
, 2);
237 it('adds multiple methods', function () {
238 const urlPath
= '/:id';
239 const expected
= router
._pathToRoutePath(urlPath
);
240 expected
[Router
.kPathMethods
]['GET'] = stubEntry
;
241 expected
[Router
.kPathMethods
]['HEAD'] = stubEntry
;
243 router
.on(['GET', 'HEAD'], urlPath
, stubHandler
);
244 assert
.deepStrictEqual(router
.pathsByLength
[2][0], expected
);
247 it('adds new wildcard path', function () {
248 const urlPath
= '/a/:id';
249 const expected
= router
._pathToRoutePath(urlPath
);
250 expected
[Router
.kPathMethods
]['*'] = stubEntry
;
251 router
.on('*', urlPath
, stubHandler
);
252 assert
.deepStrictEqual(router
.pathsByLength
[3][0], expected
);
255 it('fails to add unknown method path', function () {
256 const urlPath
= '/a/:id';
258 router
.on('FLARP', urlPath
, stubHandler
);
259 assert
.fail('expected an exception');
261 assert(e
instanceof DingusError
);
262 assert
.strictEqual(e
.message
, 'invalid method \'FLARP\'');
266 it('requires args to be array', function () {
267 const urlPath
= '/a';
269 router
.on('GET', urlPath
, stubHandler
, {});
270 assert
.fail('expected an exception');
272 assert(e
instanceof TypeError
);
277 describe('lookup', function () {
278 let pathsByLengthOrig
;
282 beforeEach(function () {
284 pathsByLengthOrig
= router
.pathsByLength
;
285 stubHandler
= sinon
.stub();
287 afterEach(function () {
288 router
.pathsByLength
= pathsByLengthOrig
;
291 it('finds handler', function () {
292 const urlPath
= '/:id';
293 const method
= 'GET';
294 router
.on(method
, urlPath
, stubHandler
);
297 const { handler
} = router
.lookup(method
, path
, ctx
);
298 assert
.strictEqual(handler
, stubHandler
);
299 assert
.strictEqual(ctx
.matchedPath
, urlPath
);
301 it('does not find handler with trailing slash', function () {
302 router
.ignoreTrailingSlash
= false;
303 const urlPath
= '/:id';
304 const method
= 'GET';
305 router
.on(method
, urlPath
, stubHandler
);
306 const path
= '/abc/';
309 router
.lookup(method
, path
, ctx
);
310 assert
.fail(noExpectedException
);
312 assert(e
instanceof RouterNoPathError
);
315 it('finds handler', function () {
316 router
.ignoreTrailingSlash
= true;
317 const urlPath
= '/:id';
318 const method
= 'GET';
319 router
.on(method
, urlPath
, stubHandler
);
320 const path
= '/abc/';
322 const { handler
} = router
.lookup(method
, path
, ctx
);
323 assert
.strictEqual(handler
, stubHandler
);
325 it('finds handler without context', async
function () {
326 const urlPath
= '/:id';
327 const method
= 'GET';
328 router
.on(method
, urlPath
, stubHandler
);
331 const { handler
} = router
.lookup(method
, path
);
332 assert
.strictEqual(handler
, stubHandler
);
334 it('finds fallback handler', async
function () {
335 const urlPath
= '/abc/:id';
336 const method
= 'GET';
337 router
.on('*', urlPath
, stubHandler
);
338 const path
= '/abc/def';
340 const { handler
} = router
.lookup(method
, path
, ctx
);
341 assert
.strictEqual(handler
, stubHandler
);
343 it('calls unsupported method', async
function () {
344 const urlPath
= '/:id';
345 const method
= 'POST';
346 router
.on('GET', urlPath
, stubHandler
);
350 router
.lookup(method
, path
, ctx
);
351 assert
.fail(noExpectedException
);
353 assert(e
instanceof RouterNoMethodError
);
356 it('does not lookup non-existent path', async
function () {
357 const path
= '/foo/bar';
358 const method
= 'GET';
361 router
.lookup(method
, path
, ctx
);
362 assert
.fail(noExpectedException
);
364 assert(e
instanceof RouterNoPathError
);