| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471 |
- import {describe} from 'mocha';
- import {Route} from './route.js';
- import {expect} from './chai.js';
- import {HttpMethod} from './route.js';
- import {HookName} from './hooks/index.js';
- import {TrieRouter} from './trie-router.js';
- import {HookRegistry} from './hooks/index.js';
- import {DataSender} from './senders/index.js';
- import {ErrorSender} from './senders/index.js';
- import {createRequestMock} from './utils/index.js';
- import {createResponseMock} from './utils/index.js';
- import {RequestContext} from './request-context.js';
- describe('TrieRouter', function () {
- describe('defineRoute', function () {
- it('returns the Route instance', function () {
- const router = new TrieRouter();
- const path = '/path';
- const handler = () => 'ok';
- const res = router.defineRoute({method: HttpMethod.GET, path, handler});
- expect(res).to.be.instanceof(Route);
- expect(res.method).to.be.eq(HttpMethod.GET);
- expect(res.path).to.be.eq(path);
- expect(res.handler).to.be.eq(handler);
- });
- });
- describe('requestListener', function () {
- it('to be a function', function () {
- const router = new TrieRouter();
- expect(typeof router.requestListener).to.be.eq('function');
- });
- it('passes request context to the route handler', function (done) {
- const router = new TrieRouter();
- router.defineRoute({
- method: HttpMethod.GET,
- path: '/test',
- handler: ctx => {
- expect(ctx).to.be.instanceof(RequestContext);
- done();
- },
- });
- const req = createRequestMock({path: '/test'});
- const res = createResponseMock();
- router.requestListener(req, res);
- });
- it('passes path parameters to the request context', function (done) {
- const router = new TrieRouter();
- router.defineRoute({
- method: HttpMethod.GET,
- path: '/:p1-:p2',
- handler: ({params}) => {
- expect(params).to.be.eql({p1: 'foo', p2: 'bar'});
- done();
- },
- });
- const req = createRequestMock({path: '/foo-bar'});
- const res = createResponseMock();
- router.requestListener(req, res);
- });
- it('passes query parameters to the request context', function (done) {
- const router = new TrieRouter();
- router.defineRoute({
- method: HttpMethod.GET,
- path: '/',
- handler: ({query}) => {
- expect(query).to.be.eql({p1: 'foo', p2: 'bar'});
- done();
- },
- });
- const req = createRequestMock({path: '?p1=foo&p2=bar'});
- const res = createResponseMock();
- router.requestListener(req, res);
- });
- it('passes parsed cookie to the request context', function (done) {
- const router = new TrieRouter();
- router.defineRoute({
- method: HttpMethod.GET,
- path: '/',
- handler: ({cookie}) => {
- expect(cookie).to.be.eql({p1: 'foo', p2: 'bar'});
- done();
- },
- });
- const req = createRequestMock({headers: {cookie: 'p1=foo; p2=bar;'}});
- const res = createResponseMock();
- router.requestListener(req, res);
- });
- it('passes plain text body to the request context', function (done) {
- const router = new TrieRouter();
- const body = 'Lorem Ipsum is simply dummy text.';
- router.defineRoute({
- method: HttpMethod.POST,
- path: '/',
- handler: ctx => {
- expect(ctx.body).to.be.eq(body);
- done();
- },
- });
- const req = createRequestMock({method: HttpMethod.POST, body});
- const res = createResponseMock();
- router.requestListener(req, res);
- });
- it('passes parsed JSON body to the request context', function (done) {
- const router = new TrieRouter();
- const data = {p1: 'foo', p2: 'bar'};
- router.defineRoute({
- method: HttpMethod.POST,
- path: '/',
- handler: ({body}) => {
- expect(body).to.be.eql(data);
- done();
- },
- });
- const req = createRequestMock({method: HttpMethod.POST, body: data});
- const res = createResponseMock();
- router.requestListener(req, res);
- });
- it('passes headers to the request context', function (done) {
- const router = new TrieRouter();
- router.defineRoute({
- method: HttpMethod.GET,
- path: '/',
- handler: ({headers}) => {
- expect(headers).to.be.eql({
- host: 'localhost',
- foo: 'bar',
- });
- done();
- },
- });
- const req = createRequestMock({headers: {foo: 'bar'}});
- const res = createResponseMock();
- router.requestListener(req, res);
- });
- it('uses DataSender to send the response', function (done) {
- const router = new TrieRouter();
- const resBody = 'Lorem Ipsum is simply dummy text.';
- router.defineRoute({
- method: HttpMethod.GET,
- path: '/',
- handler: () => resBody,
- });
- const req = createRequestMock();
- const res = createResponseMock();
- router.setService(DataSender, {
- send(response, data) {
- expect(response).to.be.eq(res);
- expect(data).to.be.eq(resBody);
- done();
- },
- });
- router.requestListener(req, res);
- });
- it('uses ErrorSender to send the response', function (done) {
- const router = new TrieRouter();
- const error = new Error();
- router.defineRoute({
- method: HttpMethod.GET,
- path: '/',
- handler: () => {
- throw error;
- },
- });
- const req = createRequestMock();
- const res = createResponseMock();
- router.setService(ErrorSender, {
- send(request, response, err) {
- expect(request).to.be.eq(req);
- expect(response).to.be.eq(res);
- expect(err).to.be.eq(error);
- done();
- },
- });
- router.requestListener(req, res);
- });
- describe('hooks', function () {
- it('invokes entire "preHandler" hooks before the route handler', async function () {
- const router = new TrieRouter();
- const order = [];
- const body = 'OK';
- router.defineRoute({
- method: HttpMethod.GET,
- path: '/',
- preHandler: [
- () => {
- order.push('preHandler1');
- },
- () => {
- order.push('preHandler2');
- },
- ],
- handler: () => {
- order.push('handler');
- return body;
- },
- });
- const req = createRequestMock();
- const res = createResponseMock();
- router.requestListener(req, res);
- const result = await res.getBody();
- expect(result).to.be.eq(body);
- expect(order).to.be.eql(['preHandler1', 'preHandler2', 'handler']);
- });
- it('invokes entire "preHandler" hooks after the route handler', async function () {
- const router = new TrieRouter();
- const order = [];
- const body = 'OK';
- router.defineRoute({
- method: HttpMethod.GET,
- path: '/',
- handler: () => {
- order.push('handler');
- return body;
- },
- postHandler: [
- () => {
- order.push('postHandler1');
- },
- () => {
- order.push('postHandler2');
- },
- ],
- });
- const req = createRequestMock();
- const res = createResponseMock();
- router.requestListener(req, res);
- const result = await res.getBody();
- expect(result).to.be.eq(body);
- expect(order).to.be.eql(['handler', 'postHandler1', 'postHandler2']);
- });
- it('passes the request context to the "preHandler" hooks', async function () {
- const router = new TrieRouter();
- const order = [];
- const body = 'OK';
- router.defineRoute({
- method: HttpMethod.GET,
- path: '/',
- preHandler: [
- ctx => {
- order.push('preHandler1');
- expect(ctx).to.be.instanceof(RequestContext);
- },
- ctx => {
- order.push('preHandler2');
- expect(ctx).to.be.instanceof(RequestContext);
- },
- ],
- handler: ctx => {
- order.push('handler');
- expect(ctx).to.be.instanceof(RequestContext);
- return body;
- },
- });
- const req = createRequestMock();
- const res = createResponseMock();
- router.requestListener(req, res);
- const result = await res.getBody();
- expect(result).to.be.eq(body);
- expect(order).to.be.eql(['preHandler1', 'preHandler2', 'handler']);
- });
- it('passes the request context and return value from the route handler to the "postHandler" hooks', async function () {
- const router = new TrieRouter();
- const order = [];
- const body = 'OK';
- let requestContext;
- router.defineRoute({
- method: HttpMethod.GET,
- path: '/',
- handler: ctx => {
- order.push('handler');
- expect(ctx).to.be.instanceof(RequestContext);
- requestContext = ctx;
- return body;
- },
- postHandler: [
- (ctx, data) => {
- order.push('postHandler1');
- expect(ctx).to.be.eq(requestContext);
- expect(data).to.be.eq(body);
- },
- (ctx, data) => {
- order.push('postHandler2');
- expect(ctx).to.be.eq(requestContext);
- expect(data).to.be.eq(body);
- },
- ],
- });
- const req = createRequestMock();
- const res = createResponseMock();
- router.requestListener(req, res);
- const result = await res.getBody();
- expect(result).to.be.eq(body);
- expect(order).to.be.eql(['handler', 'postHandler1', 'postHandler2']);
- });
- it('invokes the route handler if entire "preHandler" hooks returns undefined or null', async function () {
- const router = new TrieRouter();
- const order = [];
- const body = 'OK';
- router.defineRoute({
- method: HttpMethod.GET,
- path: '/',
- preHandler: [
- () => {
- order.push('preHandler1');
- return undefined;
- },
- () => {
- order.push('preHandler2');
- return null;
- },
- ],
- handler: () => {
- order.push('handler');
- return body;
- },
- });
- const req = createRequestMock();
- const res = createResponseMock();
- router.requestListener(req, res);
- const result = await res.getBody();
- expect(result).to.be.eq(body);
- expect(order).to.be.eql(['preHandler1', 'preHandler2', 'handler']);
- });
- it('sends a returns value from the route handler if entire "postHandler" hooks returns undefined or null', async function () {
- const router = new TrieRouter();
- const order = [];
- const body = 'OK';
- router.defineRoute({
- method: HttpMethod.GET,
- path: '/',
- handler: () => {
- order.push('handler');
- return body;
- },
- postHandler: [
- () => {
- order.push('postHandler1');
- return undefined;
- },
- () => {
- order.push('postHandler2');
- return null;
- },
- ],
- });
- const req = createRequestMock();
- const res = createResponseMock();
- router.requestListener(req, res);
- const result = await res.getBody();
- expect(result).to.be.eq(body);
- expect(order).to.be.eql(['handler', 'postHandler1', 'postHandler2']);
- });
- it('sends a return value from the "preHandler" hook in the first priority', async function () {
- const router = new TrieRouter();
- const order = [];
- const preHandlerBody = 'foo';
- const handlerBody = 'bar';
- const postHandlerBody = 'baz';
- router.defineRoute({
- method: HttpMethod.GET,
- path: '/',
- preHandler() {
- order.push('preHandler');
- return preHandlerBody;
- },
- handler: () => {
- order.push('handler');
- return handlerBody;
- },
- postHandler() {
- order.push('postHandler');
- return postHandlerBody;
- },
- });
- const req = createRequestMock();
- const res = createResponseMock();
- router.requestListener(req, res);
- const result = await res.getBody();
- expect(result).to.be.eq(preHandlerBody);
- expect(result).not.to.be.eq(handlerBody);
- expect(result).not.to.be.eq(postHandlerBody);
- expect(order).to.be.eql(['preHandler']);
- });
- it('sends a return value from the "postHandler" hook in the second priority', async function () {
- const router = new TrieRouter();
- const order = [];
- const handlerBody = 'foo';
- const postHandlerBody = 'bar';
- router.defineRoute({
- method: HttpMethod.GET,
- path: '/',
- preHandler() {
- order.push('preHandler');
- },
- handler: () => {
- order.push('handler');
- return handlerBody;
- },
- postHandler() {
- order.push('postHandler');
- return postHandlerBody;
- },
- });
- const req = createRequestMock();
- const res = createResponseMock();
- router.requestListener(req, res);
- const result = await res.getBody();
- expect(result).not.to.be.eq(handlerBody);
- expect(result).to.be.eq(postHandlerBody);
- expect(order).to.be.eql(['preHandler', 'handler', 'postHandler']);
- });
- it('sends a return value from the root handler in the third priority', async function () {
- const router = new TrieRouter();
- const order = [];
- const body = 'OK';
- router.defineRoute({
- method: HttpMethod.GET,
- path: '/',
- preHandler() {
- order.push('preHandler');
- },
- handler: () => {
- order.push('handler');
- return body;
- },
- postHandler() {
- order.push('postHandler');
- },
- });
- const req = createRequestMock();
- const res = createResponseMock();
- router.requestListener(req, res);
- const result = await res.getBody();
- expect(result).to.be.eq(body);
- expect(order).to.be.eql(['preHandler', 'handler', 'postHandler']);
- });
- });
- });
- describe('addHook', function () {
- it('adds the given hook to the HookRegistry and returns itself', function () {
- const router = new TrieRouter();
- const reg = router.getService(HookRegistry);
- const name = HookName.PRE_HANDLER;
- const hook = () => undefined;
- expect(reg.hasHook(name, hook)).to.be.false;
- const res = router.addHook(name, hook);
- expect(res).to.be.eq(router);
- expect(reg.hasHook(name, hook)).to.be.true;
- });
- });
- });
|