| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462 |
- import {expect} from '../chai.js';
- import {Route} from '../route.js';
- import {HttpMethod} from '../route.js';
- import {format} from '@e22m4u/js-format';
- import {HookType} from './hook-registry.js';
- import {HookInvoker} from './hook-invoker.js';
- import {HookRegistry} from './hook-registry.js';
- import {createResponseMock} from '../utils/index.js';
- describe('HookInvoker', function () {
- describe('invokeAndContinueUntilValueReceived', function () {
- it('requires the parameter "route" to be a Route instance', function () {
- const s = new HookInvoker();
- const res = createResponseMock();
- const throwable = v => () =>
- s.invokeAndContinueUntilValueReceived(v, HookType.PRE_HANDLER, res);
- const error = v =>
- format(
- 'The parameter "route" of ' +
- 'the HookInvoker.invokeAndContinueUntilValueReceived ' +
- 'should be a Route instance, but %s given.',
- v,
- );
- expect(throwable('str')).to.throw(error('"str"'));
- expect(throwable('')).to.throw(error('""'));
- expect(throwable(10)).to.throw(error('10'));
- expect(throwable(0)).to.throw(error('0'));
- expect(throwable(true)).to.throw(error('true'));
- expect(throwable(false)).to.throw(error('false'));
- expect(throwable(null)).to.throw(error('null'));
- expect(throwable({})).to.throw(error('Object'));
- expect(throwable([])).to.throw(error('Array'));
- expect(throwable(undefined)).to.throw(error('undefined'));
- throwable(
- new Route({
- method: HttpMethod.GET,
- path: '/',
- handler: () => undefined,
- }),
- )();
- });
- it('requires the parameter "hookType" to be a non-empty String', function () {
- const s = new HookInvoker();
- const route = new Route({
- method: HttpMethod.GET,
- path: '/',
- handler: () => undefined,
- });
- const res = createResponseMock();
- const throwable = v => () =>
- s.invokeAndContinueUntilValueReceived(route, v, res);
- const error = v =>
- format(
- 'The parameter "hookType" of ' +
- 'the HookInvoker.invokeAndContinueUntilValueReceived ' +
- 'should be a non-empty String, but %s given.',
- v,
- );
- expect(throwable('')).to.throw(error('""'));
- expect(throwable(10)).to.throw(error('10'));
- expect(throwable(0)).to.throw(error('0'));
- expect(throwable(true)).to.throw(error('true'));
- expect(throwable(false)).to.throw(error('false'));
- expect(throwable(null)).to.throw(error('null'));
- expect(throwable({})).to.throw(error('Object'));
- expect(throwable([])).to.throw(error('Array'));
- expect(throwable(undefined)).to.throw(error('undefined'));
- throwable(HookType.PRE_HANDLER)();
- });
- it('requires the parameter "hookType" to be a supported hook', function () {
- const s = new HookInvoker();
- const route = new Route({
- method: HttpMethod.GET,
- path: '/',
- handler: () => undefined,
- });
- const res = createResponseMock();
- Object.values(HookType).forEach(type =>
- s.invokeAndContinueUntilValueReceived(route, type, res),
- );
- const throwable = () =>
- s.invokeAndContinueUntilValueReceived(route, 'unknown', res);
- expect(throwable).to.throw('The hook type "unknown" is not supported.');
- });
- it('requires the parameter "response" to be an instance of ServerResponse', function () {
- const s = new HookInvoker();
- const route = new Route({
- method: HttpMethod.GET,
- path: '/',
- handler: () => undefined,
- });
- const throwable = v => () =>
- s.invokeAndContinueUntilValueReceived(route, HookType.PRE_HANDLER, v);
- const error = v =>
- format(
- 'The parameter "response" of ' +
- 'the HookInvoker.invokeAndContinueUntilValueReceived ' +
- 'should be a ServerResponse instance, but %s given.',
- v,
- );
- expect(throwable('str')).to.throw(error('"str"'));
- expect(throwable('')).to.throw(error('""'));
- expect(throwable(10)).to.throw(error('10'));
- expect(throwable(0)).to.throw(error('0'));
- expect(throwable(true)).to.throw(error('true'));
- expect(throwable(false)).to.throw(error('false'));
- expect(throwable(null)).to.throw(error('null'));
- expect(throwable({})).to.throw(error('Object'));
- expect(throwable([])).to.throw(error('Array'));
- expect(throwable(undefined)).to.throw(error('undefined'));
- throwable(createResponseMock())();
- });
- it('invokes global hooks in priority', function () {
- const s = new HookInvoker();
- const order = [];
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook1');
- });
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook2');
- });
- const route = new Route({
- method: HttpMethod.GET,
- path: '/',
- preHandler: [
- () => {
- order.push('routeHook1');
- },
- () => {
- order.push('routeHook2');
- },
- ],
- handler: () => undefined,
- });
- s.invokeAndContinueUntilValueReceived(
- route,
- HookType.PRE_HANDLER,
- createResponseMock(),
- );
- expect(order).to.be.eql([
- 'globalHook1',
- 'globalHook2',
- 'routeHook1',
- 'routeHook2',
- ]);
- });
- it('stops global hooks invocation if any of them returns a value', function () {
- const s = new HookInvoker();
- const order = [];
- const ret = 'OK';
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook1');
- });
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook2');
- return ret;
- });
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook3');
- });
- const route = new Route({
- method: HttpMethod.GET,
- path: '/',
- preHandler: [
- () => {
- order.push('routeHook1');
- },
- () => {
- order.push('routeHook2');
- },
- ],
- handler: () => undefined,
- });
- const result = s.invokeAndContinueUntilValueReceived(
- route,
- HookType.PRE_HANDLER,
- createResponseMock(),
- );
- expect(result).to.be.eq(ret);
- expect(order).to.be.eql(['globalHook1', 'globalHook2']);
- });
- it('stops route hooks invocation if any of them returns a value', function () {
- const s = new HookInvoker();
- const order = [];
- const ret = 'OK';
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook1');
- });
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook2');
- });
- const route = new Route({
- method: HttpMethod.GET,
- path: '/',
- preHandler: [
- () => {
- order.push('routeHook1');
- },
- () => {
- order.push('routeHook2');
- return ret;
- },
- () => {
- order.push('routeHook3');
- },
- ],
- handler: () => undefined,
- });
- const result = s.invokeAndContinueUntilValueReceived(
- route,
- HookType.PRE_HANDLER,
- createResponseMock(),
- );
- expect(result).to.be.eq(ret);
- expect(order).to.be.eql([
- 'globalHook1',
- 'globalHook2',
- 'routeHook1',
- 'routeHook2',
- ]);
- });
- it('stops global hooks invocation and returns the given response if it was sent', function () {
- const s = new HookInvoker();
- const order = [];
- const res = createResponseMock();
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook1');
- });
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook2');
- res._headersSent = true;
- });
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook3');
- });
- const route = new Route({
- method: HttpMethod.GET,
- path: '/',
- preHandler: [
- () => {
- order.push('routeHook1');
- },
- () => {
- order.push('routeHook2');
- },
- ],
- handler: () => undefined,
- });
- const result = s.invokeAndContinueUntilValueReceived(
- route,
- HookType.PRE_HANDLER,
- res,
- );
- expect(result).to.be.eq(res);
- expect(order).to.be.eql(['globalHook1', 'globalHook2']);
- });
- it('stops route hooks invocation and returns the given response if it was sent', function () {
- const s = new HookInvoker();
- const order = [];
- const res = createResponseMock();
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook1');
- });
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook2');
- });
- const route = new Route({
- method: HttpMethod.GET,
- path: '/',
- preHandler: [
- () => {
- order.push('routeHook1');
- },
- () => {
- order.push('routeHook2');
- res._headersSent = true;
- },
- () => {
- order.push('routeHook3');
- },
- ],
- handler: () => undefined,
- });
- const result = s.invokeAndContinueUntilValueReceived(
- route,
- HookType.PRE_HANDLER,
- res,
- );
- expect(result).to.be.eq(res);
- expect(order).to.be.eql([
- 'globalHook1',
- 'globalHook2',
- 'routeHook1',
- 'routeHook2',
- ]);
- });
- it('returns a Promise if any global hook is asynchronous', async function () {
- const s = new HookInvoker();
- const order = [];
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook1');
- });
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, async () => {
- order.push('globalHook2');
- });
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook3');
- });
- const route = new Route({
- method: HttpMethod.GET,
- path: '/',
- preHandler: [
- () => {
- order.push('routeHook1');
- },
- () => {
- order.push('routeHook2');
- },
- ],
- handler: () => undefined,
- });
- const promise = s.invokeAndContinueUntilValueReceived(
- route,
- HookType.PRE_HANDLER,
- createResponseMock(),
- );
- expect(promise).to.be.instanceof(Promise);
- await expect(promise).to.eventually.be.undefined;
- expect(order).to.be.eql([
- 'globalHook1',
- 'globalHook2',
- 'globalHook3',
- 'routeHook1',
- 'routeHook2',
- ]);
- });
- it('returns a Promise if entire global hooks are asynchronous', async function () {
- const s = new HookInvoker();
- const order = [];
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, async () => {
- order.push('globalHook1');
- });
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, async () => {
- order.push('globalHook2');
- });
- const route = new Route({
- method: HttpMethod.GET,
- path: '/',
- preHandler: [
- () => {
- order.push('routeHook1');
- },
- () => {
- order.push('routeHook2');
- },
- ],
- handler: () => undefined,
- });
- const promise = s.invokeAndContinueUntilValueReceived(
- route,
- HookType.PRE_HANDLER,
- createResponseMock(),
- );
- expect(promise).to.be.instanceof(Promise);
- await expect(promise).to.eventually.be.undefined;
- expect(order).to.be.eql([
- 'globalHook1',
- 'globalHook2',
- 'routeHook1',
- 'routeHook2',
- ]);
- });
- it('returns a Promise if any route hook is asynchronous', async function () {
- const s = new HookInvoker();
- const order = [];
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook1');
- });
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook2');
- });
- const route = new Route({
- method: HttpMethod.GET,
- path: '/',
- preHandler: [
- () => {
- order.push('routeHook1');
- },
- async () => {
- order.push('routeHook2');
- },
- () => {
- order.push('routeHook3');
- },
- ],
- handler: () => undefined,
- });
- const promise = s.invokeAndContinueUntilValueReceived(
- route,
- HookType.PRE_HANDLER,
- createResponseMock(),
- );
- expect(promise).to.be.instanceof(Promise);
- await expect(promise).to.eventually.be.undefined;
- expect(order).to.be.eql([
- 'globalHook1',
- 'globalHook2',
- 'routeHook1',
- 'routeHook2',
- 'routeHook3',
- ]);
- });
- it('returns a Promise if entire route hooks are asynchronous', async function () {
- const s = new HookInvoker();
- const order = [];
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook1');
- });
- s.getService(HookRegistry).addHook(HookType.PRE_HANDLER, () => {
- order.push('globalHook2');
- });
- const route = new Route({
- method: HttpMethod.GET,
- path: '/',
- preHandler: [
- async () => {
- order.push('routeHook1');
- },
- async () => {
- order.push('routeHook2');
- },
- ],
- handler: () => undefined,
- });
- const promise = s.invokeAndContinueUntilValueReceived(
- route,
- HookType.PRE_HANDLER,
- createResponseMock(),
- );
- expect(promise).to.be.instanceof(Promise);
- await expect(promise).to.eventually.be.undefined;
- expect(order).to.be.eql([
- 'globalHook1',
- 'globalHook2',
- 'routeHook1',
- 'routeHook2',
- ]);
- });
- });
- });
|