f9a2401eba045bb3b0972acac39287a07a907a01
[squeep-api-dingus] / test / lib / router.js
1 /* eslint-disable capitalized-comments */
2 /* eslint-env mocha */
3 'use strict';
4
5 const assert = require('assert');
6 const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require
7 const Router = require('../../lib/router');
8 const PathParameter = require('../../lib/router/path-parameter')
9 const { DingusError } = require('../../lib/errors');
10
11 const noExpectedException = 'did not get expected exception';
12
13 describe('Router', function () {
14 const router = new Router();
15 let _its; // Save and restore ignoreTrailingSlash
16
17 beforeEach(function () {
18 _its = router.ignoreTrailingSlash;
19 });
20
21 afterEach(function () {
22 sinon.restore();
23 router.ignoreTrailingSlash = _its;
24 });
25
26 describe('_pathToRoutePath', function () {
27 it('defines a simple path', function () {
28 const p = '/a/b/c';
29 const expected = ['', 'a', 'b', 'c'];
30 expected[Router.kPathMethods] = {};
31 const r = router._pathToRoutePath(p);
32 assert.deepStrictEqual(r, expected);
33 });
34 it('defines a path with parameter', function () {
35 const p = '/a/b/:c';
36 const expected = ['', 'a', 'b', new PathParameter('c')];
37 expected[Router.kPathMethods] = {};
38 const r = router._pathToRoutePath(p);
39 assert.deepStrictEqual(r, expected);
40 });
41 it('defines a path with trailing slash', function () {
42 router.ignoreTrailingSlash = true;
43 const p = '/a/b/:c/';
44 const expected = ['', 'a', 'b', new PathParameter('c')];
45 expected[Router.kPathMethods] = {};
46 const r = router._pathToRoutePath(p);
47 assert.deepStrictEqual(r, expected);
48 });
49 it('defines a path with escaped parameter', function () {
50 const p = '/a/\\:b/c';
51 const expected = ['', 'a', ':b', 'c'];
52 expected[Router.kPathMethods] = {};
53 const r = router._pathToRoutePath(p);
54 assert.deepStrictEqual(r, expected);
55 });
56 }); // _pathToRoutePath
57
58 describe('_pathCompareExact', function () {
59 let fixedPath, checkPath;
60
61 it('compares static paths which match', function () {
62 fixedPath = router._pathToRoutePath('/a/b/c');
63 checkPath = router._pathToRoutePath('/a/b/c');
64 const r = Router._pathCompareExact(fixedPath, checkPath);
65 assert.strictEqual(r, true);
66 });
67 it('compares static paths which do not match', function () {
68 fixedPath = router._pathToRoutePath('/a/b/c');
69 checkPath = router._pathToRoutePath('/a/b/d');
70 const r = Router._pathCompareExact(fixedPath, checkPath);
71 assert.strictEqual(r, false);
72 });
73 it('compares unequal static paths', function () {
74 fixedPath = router._pathToRoutePath('/a/b/c');
75 checkPath = router._pathToRoutePath('/a/b');
76 const r = Router._pathCompareExact(fixedPath, checkPath);
77 assert.strictEqual(r, false);
78 });
79 it('compares param paths which match', function () {
80 fixedPath = router._pathToRoutePath('/a/:b/c');
81 checkPath = router._pathToRoutePath('/a/:b/c');
82 const r = Router._pathCompareExact(fixedPath, checkPath);
83 assert.strictEqual(r, true);
84 });
85 it('compares param paths which do not match', function () {
86 fixedPath = router._pathToRoutePath('/a/:b/c');
87 checkPath = router._pathToRoutePath('/a/:g/c');
88 const r = Router._pathCompareExact(fixedPath, checkPath);
89 assert.strictEqual(r, false);
90 });
91 }); // _pathCompareExact
92
93 describe('_pathCompareParam', function () {
94 let fixedPath, checkPath;
95
96 it('compares static paths which match', function () {
97 const params = {};
98 const expectedParams = {};
99 fixedPath = router._pathToRoutePath('/a/b/c');
100 checkPath = router._pathToRoutePath('/a/b/c');
101 const r = Router._pathCompareParam(fixedPath, checkPath);
102 assert.strictEqual(r, true);
103 assert.deepStrictEqual(params, expectedParams);
104 });
105 it('compares static paths which do not match', function () {
106 const params = {};
107 const expectedParams = {};
108 fixedPath = router._pathToRoutePath('/a/b/c');
109 checkPath = router._pathToRoutePath('/a/b/d');
110 const r = Router._pathCompareParam(fixedPath, checkPath, params);
111 assert.strictEqual(r, false);
112 assert.deepStrictEqual(params, expectedParams);
113 });
114 it('compares unequal static paths', function () {
115 const params = {};
116 const expectedParams = {};
117 fixedPath = router._pathToRoutePath('/a/b/c');
118 checkPath = router._pathToRoutePath('/a/b');
119 const r = Router._pathCompareParam(fixedPath, checkPath, params);
120 assert.strictEqual(r, false);
121 assert.deepStrictEqual(params, expectedParams);
122 });
123 it('compares param paths which match', function () {
124 const params = {};
125 const expectedParams = {
126 b: 'bar',
127 };
128 fixedPath = router._pathToRoutePath('/a/:b/c');
129 checkPath = router._pathToRoutePath('/a/bar/c');
130 const r = Router._pathCompareParam(fixedPath, checkPath, params);
131 assert.strictEqual(r, true);
132 assert.deepStrictEqual(params, expectedParams);
133 });
134 it('compares multi param path which match', function () {
135 const params = {};
136 const expectedParams = {
137 b: 'gaz',
138 c: '123',
139 };
140 fixedPath = router._pathToRoutePath('/a/:b/:c');
141 checkPath = router._pathToRoutePath('/a/gaz/123');
142 const r = Router._pathCompareParam(fixedPath, checkPath, params);
143 assert.strictEqual(r, true);
144 assert.deepStrictEqual(params, expectedParams);
145 });
146 }); // _pathCompareParam
147
148 describe('_pathFind / _pathFindExact', function () {
149 let pathsByLengthOrig;
150
151 beforeEach(function () {
152 pathsByLengthOrig = router.pathsByLength;
153 router.pathsByLength = {
154 2: [ router._pathToRoutePath('/:id') ],
155 3: [ router._pathToRoutePath('/a/b') ],
156 };
157 });
158 afterEach(function () {
159 router.pathsByLength = pathsByLengthOrig;
160 });
161
162 describe('_pathFind', function () {
163 it('finds a path', function () {
164 const pathParts = ['', '46123f1e-bdca-40ee-9f62-93ad38647aa1'];
165 const { matchedPath, pathParams } = router._pathFind(pathParts);
166 assert.strictEqual(matchedPath, router.pathsByLength[2][0]);
167 assert.deepStrictEqual(pathParams, { id: pathParts[1] });
168 });
169 it('does not find a path', function () {
170 const pathParts = ['', 'flarp', 'baz'];
171 const { matchedPath } = router._pathFind(pathParts);
172 assert.strictEqual(matchedPath, undefined);
173 });
174 }); // _pathFind
175
176 describe('_pathFindExact', function () {
177 it('finds a path', function () {
178 const pathParts = ['', new PathParameter('id')];
179 const r = router._pathFindExact(pathParts);
180 assert.strictEqual(r, router.pathsByLength[2][0]);
181 });
182 it('does not find a path', function () {
183 const pathParts = ['', 'flarp', 'baz'];
184 const r = router._pathFindExact(pathParts);
185 assert.strictEqual(r, undefined);
186 });
187 }); // _pathFindExact
188
189 }); // _pathFind / _pathFindExact
190
191 describe('on', function () {
192 let pathsByLengthOrig;
193 const stubHandler = () => {};
194 const stubEntry = {
195 handler: stubHandler,
196 handlerArgs: [],
197 };
198
199 beforeEach(function () {
200 pathsByLengthOrig = router.pathsByLength;
201 router.pathsByLength = {
202 2: [ router._pathToRoutePath('/:id') ],
203 };
204 });
205 afterEach(function () {
206 router.pathsByLength = pathsByLengthOrig;
207 });
208
209 it('adds new path', function () {
210 const urlPath = '/a/:id';
211 const expected = router._pathToRoutePath(urlPath);
212 expected[Router.kPathMethods]['GET'] = stubEntry;
213 router.on('GET', urlPath, stubHandler);
214 assert.deepStrictEqual(router.pathsByLength[3][0], expected);
215 });
216
217 it('adds new method to path', function () {
218 const urlPath = '/a/:id';
219 const expected = router._pathToRoutePath(urlPath);
220 expected[Router.kPathMethods]['GET'] = stubEntry;
221 expected[Router.kPathMethods]['POST'] = stubEntry;
222 router.on('GET', urlPath, stubHandler);
223
224 router.on('POST', urlPath, stubHandler);
225 assert.deepStrictEqual(router.pathsByLength[3][0], expected);
226 });
227
228 it('add some more paths', function () {
229 let urlPath = '/a/b/c/d';
230 const expected = router._pathToRoutePath(urlPath);
231 expected[Router.kPathMethods]['GET'] = stubEntry;
232 router.on('GET', urlPath, stubHandler);
233 urlPath = '/a/b/x/y';
234 router.on('GET', urlPath, stubHandler);
235
236 assert.strictEqual(router.pathsByLength[5].length, 2);
237 });
238
239 it('adds multiple methods', function () {
240 const urlPath = '/:id';
241 const expected = router._pathToRoutePath(urlPath);
242 expected[Router.kPathMethods]['GET'] = stubEntry;
243 expected[Router.kPathMethods]['HEAD'] = stubEntry;
244
245 router.on(['GET', 'HEAD'], urlPath, stubHandler);
246 assert.deepStrictEqual(router.pathsByLength[2][0], expected);
247 });
248
249 it('adds new wildcard path', function () {
250 const urlPath = '/a/:id';
251 const expected = router._pathToRoutePath(urlPath);
252 expected[Router.kPathMethods]['*'] = stubEntry;
253 router.on('*', urlPath, stubHandler);
254 assert.deepStrictEqual(router.pathsByLength[3][0], expected);
255 });
256
257 it('fails to add unknown method path', function () {
258 const urlPath = '/a/:id';
259 try {
260 router.on('FLARP', urlPath, stubHandler);
261 assert.fail('expected an exception');
262 } catch (e) {
263 assert.strictEqual(e.name, 'DingusError');
264 assert.strictEqual(e.message, 'invalid method \'FLARP\'');
265 }
266 });
267
268 it('requires args to be array', function () {
269 const urlPath = '/a';
270 try {
271 router.on('GET', urlPath, stubHandler, {});
272 assert.fail('expected an exception');
273 } catch (e) {
274 assert(e instanceof TypeError);
275 }
276 });
277 }); // on
278
279 describe('lookup', function () {
280 let pathsByLengthOrig;
281 let ctx;
282 let stubHandler;
283
284 beforeEach(function () {
285 ctx = {};
286 pathsByLengthOrig = router.pathsByLength;
287 stubHandler = sinon.stub();
288 });
289 afterEach(function () {
290 router.pathsByLength = pathsByLengthOrig;
291 });
292
293 it('finds handler', function () {
294 const urlPath = '/:id';
295 const method = 'GET';
296 router.on(method, urlPath, stubHandler);
297 const path = '/abc';
298
299 const { handler } = router.lookup(method, path, ctx);
300 assert.strictEqual(handler, stubHandler);
301 });
302 it('does not find handler with trailing slash', function () {
303 router.ignoreTrailingSlash = false;
304 const urlPath = '/:id';
305 const method = 'GET';
306 router.on(method, urlPath, stubHandler);
307 const path = '/abc/';
308
309 try {
310 router.lookup(method, path, ctx);
311 assert.fail(noExpectedException);
312 } catch (e) {
313 assert(e instanceof DingusError);
314 assert.strictEqual(e.message, 'NoPath');
315 }
316 });
317 it('finds handler', function () {
318 router.ignoreTrailingSlash = true;
319 const urlPath = '/:id';
320 const method = 'GET';
321 router.on(method, urlPath, stubHandler);
322 const path = '/abc/';
323
324 const { handler } = router.lookup(method, path, ctx);
325 assert.strictEqual(handler, stubHandler);
326 });
327 it('finds handler without context', async function () {
328 const urlPath = '/:id';
329 const method = 'GET';
330 router.on(method, urlPath, stubHandler);
331 const path = '/abc';
332
333 const { handler } = router.lookup(method, path);
334 assert.strictEqual(handler, stubHandler);
335 });
336 it('finds fallback handler', async function () {
337 const urlPath = '/abc/:id';
338 const method = 'GET';
339 router.on('*', urlPath, stubHandler);
340 const path = '/abc/def';
341
342 const { handler } = router.lookup(method, path, ctx);
343 assert.strictEqual(handler, stubHandler);
344 });
345 it('calls unsupported method', async function () {
346 const urlPath = '/:id';
347 const method = 'POST';
348 router.on('GET', urlPath, stubHandler);
349 const path = '/abc';
350
351 try {
352 router.lookup(method, path, ctx);
353 assert.fail(noExpectedException);
354 } catch (e) {
355 assert(e instanceof DingusError);
356 assert.strictEqual(e.message, 'NoMethod');
357 }
358 });
359 it('does not lookup non-existent path', async function () {
360 const path = '/foo/bar';
361 const method = 'GET';
362
363 try {
364 router.lookup(method, path, ctx);
365 assert.fail(noExpectedException);
366 } catch (e) {
367 assert(e instanceof DingusError);
368 assert.strictEqual(e.message, 'NoPath');
369 }
370 });
371
372 }); // lookup
373
374 });