1 /* eslint-disable capitalized-comments */
5 const assert
= require('assert');
6 const sinon
= require('sinon'); // eslint-disable-line node/no-unpublished-require
7 const fs
= require('fs');
9 const Dingus
= require('../../lib/dingus');
10 const { DingusError
} = require('../../lib/errors');
11 const Enum
= require('../../lib/enum');
13 const noExpectedException
= 'did not get expected exception';
15 describe('Dingus', function () {
16 const dingus
= new Dingus();
18 afterEach(function () {
22 describe('constructor', function () {
23 it('covers', function () {
24 const d
= new Dingus({}, {});
26 assert('log' in d
.logger
);
30 describe('_normalizePath', function () {
31 it('returns normal path', function () {
33 const r
= dingus
._normalizePath(p
);
34 assert
.strictEqual(r
, p
);
36 it('returns normal path', function () {
37 const p
= '////a///b/./bar/..///c';
38 const expected
= '/a/b/c'
39 const r
= dingus
._normalizePath(p
);
40 assert
.strictEqual(r
, expected
);
44 describe('_splitUrl', function () {
45 const nullObject
= Object
.create(null);
47 it('splits a simple path', function () {
51 queryParams: nullObject
,
53 const r
= dingus
._splitUrl(p
);
54 assert
.deepStrictEqual(r
, expected
);
56 it('splits a path with trailing slash preserved', function () {
60 queryParams: nullObject
,
62 const r
= dingus
._splitUrl(p
);
63 assert
.deepStrictEqual(r
, expected
);
65 it('splits a path with trailing slash ignored', function () {
69 queryParams: nullObject
,
71 dingus
.ignoreTrailingSlash
= true;
72 const r
= dingus
._splitUrl(p
);
73 assert
.deepStrictEqual(r
, expected
);
75 it('splits a path with empty query string', function () {
79 queryParams: nullObject
,
81 const r
= dingus
._splitUrl(p
);
82 assert
.deepStrictEqual(r
, expected
);
84 it('splits a path with query string', function () {
85 const p
= '/a/b/c?x=1&y=2&z';
88 queryParams: Object
.assign(Object
.create(null), {
91 z: '', // Subjective Editorial: disagree with the default querystring parser behavior here: null would be better than empty string, esp as result is null-prototyped object.
94 const r
= dingus
._splitUrl(p
);
95 assert
.deepStrictEqual(r
, expected
);
99 describe('tagContext', function () {
101 beforeEach(function () {
103 getHeader: sinon
.stub(),
104 setHeader: sinon
.stub(),
107 getHeader: sinon
.stub(),
108 setHeader: sinon
.stub(),
112 it ('sets id in context', function () {
113 const result
= Dingus
.tagContext(req
, res
, ctx
);
114 assert
.strictEqual(ctx
.requestId
, result
);
115 assert(res
.setHeader
.called
);
117 it ('sets provided header', function () {
118 req
.getHeader
.onCall(0).returns('abc'); // X-Request-ID
119 const result
= Dingus
.tagContext(req
, res
, ctx
);
120 assert
.strictEqual(ctx
.requestId
, result
);
121 assert
.strictEqual(res
.setHeader
.getCall(0).args
[0], 'Request-ID');
122 assert
.strictEqual(res
.setHeader
.getCall(1).args
[0], 'X-Request-ID');
123 assert
.strictEqual(res
.setHeader
.getCall(1).args
[1], 'abc');
124 assert
.strictEqual(res
.setHeader
.callCount
, 2);
128 describe('clientAddressContext', function () {
132 _tp
= dingus
.trustProxy
;
135 dingus
.trustProxy
= _tp
;
137 beforeEach(function () {
139 getHeader: sinon
.stub(),
140 setHeader: sinon
.stub(),
144 getHeader: sinon
.stub(),
145 setHeader: sinon
.stub(),
149 it ('covers untrusted proxy', function () {
150 dingus
.trustProxy
= false;
153 clientProtocol: 'http',
155 dingus
.clientAddressContext(req
, res
, ctx
);
156 assert
.deepStrictEqual(ctx
, expected
);
157 assert(!req
.getHeader
.called
);
159 it ('covers missing', function () {
160 dingus
.trustProxy
= true;
162 clientAddress: '::1',
163 clientProtocol: 'https',
165 req
.connection
.remoteAddress
= '::1';
166 req
.connection
.encrypted
= true;
167 dingus
.clientAddressContext(req
, res
, ctx
);
168 assert(req
.getHeader
.called
);
169 assert
.deepStrictEqual(ctx
, expected
);
171 }); // clientAddressContext
173 describe('getRequestContentType', function () {
175 beforeEach(function () {
177 getHeader: sinon
.stub(),
178 setHeader: sinon
.stub(),
181 it('handles missing header', function () {
182 const result
= Dingus
.getRequestContentType(req
);
183 assert
.strictEqual(result
, '');
185 it('parses simple type', function () {
186 req
.getHeader
.onCall(0).returns(Enum
.ContentType
.ApplicationJson
);
187 const result
= Dingus
.getRequestContentType(req
);
188 assert
.strictEqual(result
, Enum
.ContentType
.ApplicationJson
);
190 it('parses complex type', function () {
191 req
.getHeader
.onCall(0).returns('application/json ; charset=UTF-8');
192 const result
= Dingus
.getRequestContentType(req
);
193 assert
.strictEqual(result
, Enum
.ContentType
.ApplicationJson
);
195 }); // getRequestContentType
197 describe('setResponseContentType', function () {
198 let req
, responseTypes
;
199 beforeEach(function () {
202 setHeader: sinon
.stub(),
203 getHeader: sinon
.stub(),
206 it('handles missing header', function () {
207 const result
= Dingus
.getResponseContentType(responseTypes
, req
);
208 assert
.strictEqual(result
, undefined);
210 it('behaves as expected', function () {
211 responseTypes
.push(Enum
.ContentType
.ApplicationJson
);
212 req
.getHeader
.onCall(0).returns('text, image/png;q=0.5, application/*;q=0.2, audio;q=0.1');
213 const result
= Dingus
.getResponseContentType(responseTypes
, req
);
214 assert
.strictEqual(result
, Enum
.ContentType
.ApplicationJson
);
216 }); // setResponseContentType
218 describe('on', function () {
220 beforeEach(function () {
221 stubOn
= sinon
.stub(dingus
.router
, 'on');
223 it('covers', function () {
224 dingus
.on('GET', '/', () => {});
225 assert(stubOn
.called
);
229 describe('setEndBodyHandler', function () {
230 let req
, res
, ctx
, handler
, origEnd
, origWrite
;
231 beforeEach(function () {
232 origEnd
= sinon
.stub();
233 origWrite
= sinon
.stub();
240 handler
= sinon
.stub();
242 it('collects body and handles', function () {
243 Dingus
.setEndBodyHandler(req
, res
, ctx
, handler
);
244 res
.write(Buffer
.from('foo'));
248 assert(origWrite
.called
);
249 assert(origEnd
.called
);
250 assert
.deepStrictEqual(ctx
.responseBody
, Buffer
.from('foobazquux'));
251 assert(handler
.called
);
253 }); // setEndBodyHandler
255 describe('setHeadHandler', function () {
256 let req
, res
, ctx
, origEnd
, origWrite
;
257 beforeEach(function () {
258 origEnd
= sinon
.stub();
259 origWrite
= sinon
.stub();
266 setHeader: sinon
.stub(),
270 it('collects body without writing', function () {
271 Dingus
.setHeadHandler(req
, res
, ctx
);
272 res
.write(Buffer
.from('foo'));
276 assert(!origWrite
.called
);
277 assert(origEnd
.called
);
278 assert
.deepStrictEqual(ctx
.responseBody
, Buffer
.from('foobazquux'));
280 it('ignores non-head method', function () {
282 Dingus
.setHeadHandler(req
, res
, ctx
);
283 res
.write(Buffer
.from('foo'));
285 assert(origWrite
.called
);
286 assert(origEnd
.called
);
288 }); // setHeadHandler
290 describe('addEncodingHeader', function () {
292 beforeEach(function () {
295 // eslint-disable-next-line security/detect-object-injection
296 getHeader: (h
) => res
._headers
[h
],
297 // eslint-disable-next-line security/detect-object-injection
298 setHeader: (h
, v
) => res
._headers
[h
] = v
,
301 it('adds', function () {
303 Dingus
.addEncodingHeader(res
, encoding
);
304 assert
.strictEqual(res
._headers
[Enum
.Header
.ContentEncoding
], 'gzip');
306 it('extends', function () {
308 Dingus
.addEncodingHeader(res
, encoding
);
309 assert
.strictEqual(res
._headers
[Enum
.Header
.ContentEncoding
], 'utf8');
311 Dingus
.addEncodingHeader(res
, encoding
);
312 assert
.strictEqual(res
._headers
[Enum
.Header
.ContentEncoding
], 'gzip, utf8');
314 }); // addEncodingHeader
316 describe('dispatch', function () {
317 let pathsByLengthOrig
;
321 beforeEach(function () {
325 setHeader: sinon
.stub(),
326 getHeader: sinon
.stub(),
331 setHeader: sinon
.stub(),
332 hasHeader: sinon
.stub(),
333 getHeader: sinon
.stub(),
334 getHeaders: sinon
.stub(),
337 pathsByLengthOrig
= dingus
.pathsByLength
;
338 sinon
.spy(dingus
, 'handlerMethodNotAllowed');
339 sinon
.spy(dingus
, 'handlerNotFound');
340 sinon
.spy(dingus
, 'handlerBadRequest');
341 sinon
.spy(dingus
, 'handlerInternalServerError');
342 stubHandler
= sinon
.stub();
344 afterEach(function () {
345 dingus
.pathsByLength
= pathsByLengthOrig
;
348 it('calls handler', async
function () {
349 const urlPath
= '/:id';
350 const method
= 'GET';
351 dingus
.on(method
, urlPath
, stubHandler
);
355 await dingus
.dispatch(req
, res
, ctx
);
356 assert(stubHandler
.called
);
357 assert(!dingus
.handlerMethodNotAllowed
.called
);
358 assert(!dingus
.handlerNotFound
.called
);
360 it('calls handler without context', async
function () {
361 const urlPath
= '/:id';
362 const method
= 'GET';
363 dingus
.on(method
, urlPath
, stubHandler
);
367 await dingus
.dispatch(req
, res
);
368 assert(stubHandler
.called
);
369 assert(!dingus
.handlerMethodNotAllowed
.called
);
370 assert(!dingus
.handlerNotFound
.called
);
372 it('calls fallback handler', async
function () {
373 const urlPath
= '/abc/:id';
374 const method
= 'GET';
375 dingus
.on('*', urlPath
, stubHandler
);
376 req
.url
= '/abc/def';
379 await dingus
.dispatch(req
, res
, ctx
);
380 assert(stubHandler
.called
);
381 assert(!dingus
.handlerMethodNotAllowed
.called
);
382 assert(!dingus
.handlerNotFound
.called
);
384 it('handles error in handler', async
function () {
385 const urlPath
= '/:id';
386 const method
= 'GET';
387 dingus
.on(method
, urlPath
, stubHandler
);
390 stubHandler
.rejects(new Error('blah'));
392 await dingus
.dispatch(req
, res
, ctx
);
393 assert(stubHandler
.called
);
394 assert(!dingus
.handlerMethodNotAllowed
.called
);
395 assert(!dingus
.handlerNotFound
.called
);
397 it('calls unsupported method', async
function () {
398 const urlPath
= '/:id';
399 const method
= 'POST';
400 dingus
.on('GET', urlPath
, stubHandler
);
404 await dingus
.dispatch(req
, res
, ctx
);
405 assert(!stubHandler
.called
);
406 assert(dingus
.handlerMethodNotAllowed
.called
);
407 assert(!dingus
.handlerNotFound
.called
);
409 it('does not lookup nonexistent path', async
function () {
410 req
.url
= '/foo/bar';
413 await dingus
.dispatch(req
, res
, ctx
);
414 assert(!stubHandler
.called
);
415 assert(!dingus
.handlerMethodNotAllowed
.called
);
416 assert(dingus
.handlerNotFound
.called
);
418 it('covers unhandled dingus exception', async
function () {
419 const expectedException
= new DingusError('blah');
420 sinon
.stub(dingus
.router
, 'lookup').throws(expectedException
);
422 await dingus
.dispatch(req
, res
, ctx
);
423 assert(!stubHandler
.called
);
424 assert(dingus
.handlerInternalServerError
.called
);
426 it('covers other exception', async
function () {
427 const expectedException
= new Error('blah');
428 sinon
.stub(dingus
.router
, 'lookup').throws(expectedException
);
430 await dingus
.dispatch(req
, res
, ctx
);
431 assert(!stubHandler
.called
);
432 assert(dingus
.handlerInternalServerError
.called
);
434 it('covers bad uri', async
function () {
437 await dingus
.dispatch(req
, res
, ctx
);
438 assert(dingus
.handlerBadRequest
.called
);
443 describe('parseBody', function () {
445 beforeEach(function () {
448 it('does not parse unknown type', function () {
450 dingus
.parseBody('unknown/type', ctx
);
451 assert
.fail(noExpectedException
);
453 assert
.strictEqual(e
.statusCode
, 415);
456 it('parses json', function () {
457 const src
= { foo: 'bar' };
458 ctx
.rawBody
= JSON
.stringify(src
);
459 dingus
.parseBody(Enum
.ContentType
.ApplicationJson
, ctx
);
460 assert
.deepStrictEqual(ctx
.parsedBody
, src
);
462 it('handles unparsable json', function () {
463 ctx
.rawBody
= 'not json';
465 dingus
.parseBody(Enum
.ContentType
.ApplicationJson
, ctx
);
466 assert
.fail(noExpectedException
);
468 assert
.strictEqual(e
.statusCode
, 400);
471 it('parses form', function () {
472 const expected
= Object
.assign(Object
.create(null), {
475 ctx
.rawBody
= 'foo=bar';
476 dingus
.parseBody('application/x-www-form-urlencoded', ctx
);
477 assert
.deepStrictEqual(ctx
.parsedBody
, expected
);
482 describe('bodyData', function () {
484 beforeEach(function () {
487 // eslint-disable-next-line security/detect-object-injection
488 on: (ev
, fn
) => resEvents
[ev
] = fn
,
491 it('provides data', async
function () {
492 const p
= dingus
.bodyData(res
);
493 resEvents
['data'](Buffer
.from('foo'));
494 resEvents
['data'](Buffer
.from('bar'));
496 const result
= await p
;
497 assert
.strictEqual(result
, 'foobar');
499 it('handles error', async
function () {
500 const p
= dingus
.bodyData(res
);
501 resEvents
['error']('foo');
504 assert
.fail(noExpectedException
);
506 assert
.strictEqual(e
, 'foo');
511 describe('ingestBody', function () {
512 it('covers', async
function () {
516 sinon
.stub(dingus
, 'bodyData').resolves('{"foo":"bar"}')
517 sinon
.stub(Dingus
, 'getRequestContentType').returns(Enum
.ContentType
.ApplicationJson
);
518 await dingus
.ingestBody(req
, res
, ctx
);
519 assert
.deepStrictEqual(ctx
.parsedBody
, { foo: 'bar' });
523 describe('setResponseType', function () {
525 let _sa
; // Preserve strictAccept
527 _sa
= dingus
.strictAccept
;
530 dingus
.strictAccept
= _sa
;
532 beforeEach(function () {
536 setHeader: sinon
.stub(),
538 sinon
.stub(Dingus
, 'getResponseContentType').returns();
540 it('rejects missing', function () {
541 dingus
.strictAccept
= true;
543 dingus
.setResponseType(['my/type'], req
, res
, ctx
);
544 assert
.fail(noExpectedException
);
546 assert
.strictEqual(e
.statusCode
, 406, 'did not get expected status code');
549 it('accepts missing', function () {
550 dingus
.strictAccept
= false;
551 dingus
.setResponseType(['my/type'], req
, res
, ctx
);
552 assert
.strictEqual(ctx
.responseType
, 'my/type');
555 }); // setResponseType
557 describe('_readFileInfo', function () {
558 let stat
, data
, statRes
, dataRes
, filename
;
559 beforeEach(function () {
560 sinon
.stub(fs
.promises
, 'stat');
561 sinon
.stub(fs
.promises
, 'readFile');
563 mtimeMs:1612553697186,
566 filename
= 'dummy.txt';
568 it('succeeds', async
function () {
569 fs
.promises
.stat
.resolves(statRes
);
570 fs
.promises
.readFile
.resolves('data');
571 [stat
, data
] = await dingus
._readFileInfo(filename
);
572 assert
.deepStrictEqual(stat
, statRes
);
573 assert
.deepStrictEqual(data
, dataRes
);
575 it('returns null for non-existant file', async
function () {
579 fs
.promises
.stat
.rejects(noEnt
);
580 fs
.promises
.readFile
.rejects(noEnt
);
581 [stat
, data
] = await dingus
._readFileInfo(filename
);
582 assert
.strictEqual(stat
, null);
583 assert
.strictEqual(data
, null);
585 it('throws unexpected error', async
function () {
586 const expectedException
= new Error('blah');
587 fs
.promises
.stat
.rejects(expectedException
);
588 await assert
.rejects(async () => {
589 await dingus
._readFileInfo(filename
);
590 }, expectedException
);
594 describe('serveFile', function () {
595 const path
= require('path');
596 let ctx
, req
, res
, directory
, fileName
, filestats
;
597 beforeEach(function () {
598 directory
= path
.join(__dirname
, '..', 'test-data');
599 fileName
= 'example.html';
603 [Enum
.Header
.Accept
]: undefined,
604 [Enum
.Header
.IfModifiedSince
]: undefined,
605 [Enum
.Header
.AcceptEncoding
]: undefined,
606 [Enum
.Header
.IfNoneMatch
]: undefined,
608 getHeader: (header
) => {
609 if (header
in req
._headers
) {
610 // eslint-disable-next-line security/detect-object-injection
611 return req
._headers
[header
];
613 assert
.fail(`unexpected getHeader ${header}`);
618 getHeader: sinon
.stub(),
619 getHeaders: sinon
.stub(),
620 hasHeader: sinon
.stub().returns(true),
621 setHeader: sinon
.stub(),
634 atimeMs: 1613253436842.815,
635 mtimeMs: 1603485933192.8610,
636 ctimeMs: 1603485933192.8610,
638 atime: '2021-02-13T21:57:16.843Z',
639 mtime: '2020-10-23T13:45:33.193Z',
640 ctime: '2020-10-23T13:45:33.193Z',
641 birthtime: '1970-01-01T00:00:00.000Z',
643 sinon
.stub(dingus
, 'handlerNotFound');
644 sinon
.stub(fs
.promises
, 'stat').resolves(filestats
);
645 sinon
.spy(fs
.promises
, 'readFile');
647 it('serves a file', async
function () {
648 await dingus
.serveFile(req
, res
, ctx
, directory
, fileName
);
649 assert(fs
.promises
.readFile
.called
);
650 assert(!dingus
.handlerNotFound
.called
);
652 it('does not serve dot-file', async
function () {
653 fileName
= '.example';
654 await dingus
.serveFile(req
, res
, ctx
, directory
, fileName
);
655 assert(!fs
.promises
.readFile
.called
);
656 assert(dingus
.handlerNotFound
.called
);
658 it('does not serve encoded navigation', async
function () {
659 fileName
= '/example.html';
660 await dingus
.serveFile(req
, res
, ctx
, directory
, fileName
);
661 assert(!fs
.promises
.readFile
.called
);
662 assert(dingus
.handlerNotFound
.called
);
664 it('does not serve missing file', async
function () {
665 fileName
= 'no-file.here';
666 await dingus
.serveFile(req
, res
, ctx
, directory
, fileName
);
667 assert(dingus
.handlerNotFound
.called
);
669 it('covers fs error', async
function () {
670 const expectedException
= new Error('blah');
671 fs
.promises
.stat
.restore();
672 sinon
.stub(fs
.promises
, 'stat').rejects(expectedException
);
674 await dingus
.serveFile(req
, res
, ctx
, directory
, fileName
);
675 assert
.fail('should have thrown');
677 assert
.strictEqual(e
, expectedException
);
680 it('caches by modified', async
function () {
681 req
._headers
[Enum
.Header
.IfModifiedSince
] = 'Fri, 23 Oct 2020 23:11:16 GMT';
682 await dingus
.serveFile(req
, res
, ctx
, directory
, fileName
);
683 assert
.strictEqual(res
.statusCode
, 304);
685 it('does not cache old modified', async
function () {
686 req
._headers
[Enum
.Header
.IfModifiedSince
] = 'Fri, 23 Oct 2020 01:11:16 GMT';
687 await dingus
.serveFile(req
, res
, ctx
, directory
, fileName
);
688 assert
.notStrictEqual(res
.statusCode
, 304);
689 assert(!dingus
.handlerNotFound
.called
);
691 it('caches ETag match', async
function () {
692 req
._headers
[Enum
.Header
.IfNoneMatch
] = '"zPPQVfXV36sgXq4fRLdsm+7rRMb8IUfb/eJ6N6mnwWs"';
693 await dingus
.serveFile(req
, res
, ctx
, directory
, fileName
);
694 assert
.strictEqual(res
.statusCode
, 304);
696 it('does not cache ETag non-match', async
function () {
697 req
._headers
[Enum
.Header
.IfNoneMatch
] = '"foo", "bar"';
698 await dingus
.serveFile(req
, res
, ctx
, directory
, fileName
);
699 assert
.notStrictEqual(res
.statusCode
, 304);
700 assert(!dingus
.handlerNotFound
.called
);
702 it('handles no possible encodings', async
function () {
703 req
._headers
[Enum
.Header
.AcceptEncoding
] = '*;q=0';
704 await assert
.rejects(async () => {
705 await dingus
.serveFile(req
, res
, ctx
, directory
, fileName
);
707 name: 'ResponseError',
710 it('handles a valid encoding', async
function () {
711 req
._headers
[Enum
.Header
.AcceptEncoding
] = 'gzip';
712 await dingus
.serveFile(req
, res
, ctx
, directory
, fileName
);
713 assert(res
.end
.called
);
715 it('handles a valid encoding among others', async
function () {
716 req
._headers
[Enum
.Header
.AcceptEncoding
] = 'flarp, br, gzip';
717 fs
.promises
.stat
.restore();
718 sinon
.stub(fs
.promises
, 'stat')
719 .onCall(0).resolves(filestats
) // identity file
720 .onCall(1).resolves(null) // br encoding
721 .onCall(2).resolves(filestats
); // gzip encoding
722 await dingus
.serveFile(req
, res
, ctx
, directory
, fileName
);
723 assert(res
.end
.called
);
727 describe('renderError', function () {
729 beforeEach(function () {
733 details: 'hunkydorey',
736 it('renders unknown type', function () {
737 const contentType
= 'unknown/type';
738 const result
= dingus
.renderError(contentType
, err
);
739 assert
.deepStrictEqual(result
, 'OK\r\nhunkydorey');
741 it('renders text', function () {
742 const contentType
= 'text/plain';
743 const result
= dingus
.renderError(contentType
, err
);
744 assert
.deepStrictEqual(result
, 'OK\r\nhunkydorey');
746 it('renders json', function () {
747 const contentType
= Enum
.ContentType
.ApplicationJson
;
748 const result
= dingus
.renderError(contentType
, err
);
749 assert
.deepStrictEqual(result
, JSON
.stringify(err
));
751 it('renders html without details', function () {
754 errorMessage: 'Created',
756 const contentType
= 'text/html';
757 const result
= dingus
.renderError(contentType
, err
);
758 assert
.deepStrictEqual(result
, `<!DOCTYPE html>
761 <title>${err.statusCode} ${err.errorMessage}</title>
764 <h1>${err.errorMessage}</h1>
768 it('renders html', function () {
769 const contentType
= 'text/html';
770 const result
= dingus
.renderError(contentType
, err
);
771 assert
.deepStrictEqual(result
, `<!DOCTYPE html>
774 <title>${err.statusCode} ${err.errorMessage}</title>
777 <h1>${err.errorMessage}</h1>
778 <p>${err.details}</p>
782 it('renders html, multiple details', function () {
783 const contentType
= 'text/html';
784 err
.details
= ['one detail', 'two detail'];
785 const result
= dingus
.renderError(contentType
, err
);
786 assert
.deepStrictEqual(result
, `<!DOCTYPE html>
789 <title>${err.statusCode} ${err.errorMessage}</title>
792 <h1>${err.errorMessage}</h1>
800 describe('sendErrorResponse', function () {
802 beforeEach(function () {
807 getHeader: sinon
.stub(),
808 getHeaders: sinon
.stub(),
809 hasHeader: sinon
.stub().returns(true),
810 setHeader: sinon
.stub(),
812 sinon
.stub(dingus
, 'renderError');
814 it('covers', function () {
818 dingus
.sendErrorResponse(err
, req
, res
, ctx
);
819 assert(res
.end
.called
);
821 }); // sendErrorResponse
823 describe('proxyPrefix', function () {
824 let req
, res
, ctx
, stubHandler
, pfxDingus
;
827 beforeEach(function () {
828 pfxDingus
= new Dingus(console
, { proxyPrefix: pfx
});
830 setHeader: sinon
.stub(),
831 getHeader: sinon
.stub(),
836 setHeader: sinon
.stub(),
837 getHeader: sinon
.stub(),
840 sinon
.stub(pfxDingus
, 'handlerMethodNotAllowed');
841 sinon
.stub(pfxDingus
, 'handlerNotFound');
842 stubHandler
= sinon
.stub();
844 afterEach(function () {
848 it('handles prefixed route', async
function () {
849 const urlPath
= '/:id';
850 const method
= 'GET';
851 pfxDingus
.on(method
, urlPath
, stubHandler
);
852 req
.url
= pfx
+ '/abc';
855 await pfxDingus
.dispatch(req
, res
, ctx
);
856 assert(stubHandler
.called
);
857 assert(!pfxDingus
.handlerMethodNotAllowed
.called
);
858 assert(!pfxDingus
.handlerNotFound
.called
);
860 it('does not handle prefixed route', async
function () {
861 const urlPath
= '/:id';
862 const method
= 'GET';
863 pfxDingus
.on(method
, urlPath
, stubHandler
);
864 req
.url
= '/wrongpfx/abc';
867 await pfxDingus
.dispatch(req
, res
, ctx
);
868 assert(!stubHandler
.called
);
869 assert(!pfxDingus
.handlerMethodNotAllowed
.called
);
870 assert(pfxDingus
.handlerNotFound
.called
);