UNPKG

nats-micro

Version:

NATS micro compatible extra-lightweight microservice library

331 lines (257 loc) 8.17 kB
/* eslint-disable no-throw-literal */ /* eslint-disable arrow-body-style */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { expect } from 'chai'; import sinon, { SinonSpy } from 'sinon'; import { broker, spyOff, spyOn } from './common.js'; import { Handler, MaybePromise, Microservice, MicroserviceConfig, MicroserviceOptions, Middleware, Request, Response, } from '../src/index.js'; const sleepMs = (ms: number): Promise<unknown> => new Promise((res) => { setTimeout(res, ms); }); const createService = ( data?: Partial<MicroserviceConfig>, options?: MicroserviceOptions, ): Promise<Microservice> => Microservice.create( broker, { name: 'hello', description: 'Hello service', version: '5.5.5', metadata: { key1: 'value1', }, methods: { }, ...data, }, options, ); async function createServiceWithMiddlewareExt( middlewares: Middleware<unknown, unknown>[], handler: Handler<unknown, unknown> | undefined, postMiddlewares: Middleware<unknown, unknown>[], ): Promise<SinonSpy<[Request<unknown>, Response<unknown>], MaybePromise<void>>> { if (!handler) handler = (_req, res) => { res.send('method response'); }; const spy = sinon.spy(handler); await createService({ methods: { method1: { handler: spy, middlewares, postMiddlewares, }, }, }); return spy; } async function createServiceWithMiddleware( ...middlewares: Middleware<unknown, unknown>[] ): Promise<SinonSpy<[Request<unknown>, Response<unknown>], MaybePromise<void>>> { return createServiceWithMiddlewareExt( middlewares, undefined, [], ); } describe('Middleware', function () { afterEach(function () { broker.offAll(); spyOn.resetHistory(); spyOff.resetHistory(); }); describe('Pre-middleware', function () { it('middleware that does nothing', async function () { const middleware = sinon.spy(() => { // res.send() }); const handler = await createServiceWithMiddleware( middleware, ); await expect( broker.request<string, string>('hello.method1', ''), ).to.eventually.have.property('data', 'method response'); expect(middleware.callCount).to.eq(1); expect(handler.callCount).to.eq(1); }); it('same multiple registered multiple times', async function () { const middleware = sinon.spy(() => { // res.send() }); const handler = await createServiceWithMiddleware( middleware, middleware, middleware, ); await expect( broker.request<string, string>('hello.method1', ''), ).to.eventually.have.property('data', 'method response'); expect(middleware.callCount).to.eq(3); expect(handler.callCount).to.eq(1); }); it('middleware that responds', async function () { const middleware = sinon.spy((_req, res) => { res.send('middleware response'); }); const handler = await createServiceWithMiddleware( middleware, ); await expect( broker.request<string, string>('hello.method1', ''), ).to.eventually.have.property('data', 'middleware response'); expect(middleware.callCount).to.eq(1); expect(handler.callCount).to.eq(0); }); it('first middleware that responds', async function () { const middleware1 = sinon.spy((_req, res) => { res.send('middleware1 response'); }); const middleware2 = sinon.spy((_req, res) => { res.send('middleware2 response'); }); const handler = await createServiceWithMiddleware( middleware1, middleware2, ); await expect( broker.request<string, string>('hello.method1', ''), ).to.eventually.have.property('data', 'middleware1 response'); expect(middleware1.callCount).to.eq(1); expect(middleware2.callCount).to.eq(1); expect(handler.callCount).to.eq(0); }); it('middleware that throws', async function () { const middleware = sinon.spy(() => { throw new Error('middleware error'); }); const handler = await createServiceWithMiddleware( middleware, ); await expect( broker.request<string, string>('hello.method1', ''), ).to.eventually.be.rejectedWith('middleware error'); expect(middleware.callCount).to.eq(1); expect(handler.callCount).to.eq(0); }); }); describe('Post-middleware', function () { it('middleware that does nothing', async function () { const middleware = sinon.spy(() => { // nothing }); await createServiceWithMiddlewareExt( [], () => undefined, [middleware], ); await expect( broker.request<string, string>('hello.method1', '', { timeout: 500 }), ).to.eventually.have.property('data', undefined); expect(middleware.callCount).to.eq(1); }); it('middleware that returns', async function () { const middleware = sinon.spy((_req, res) => { res.send('middleware response'); }); await createServiceWithMiddlewareExt( [], () => undefined, [middleware], ); await expect( broker.request<string, string>('hello.method1', ''), ).to.eventually.have.property('data', 'middleware response'); expect(middleware.callCount).to.eq(1); }); it('first middleware that returns', async function () { const middleware1 = sinon.spy((_req, res) => { res.send('middleware1 response'); }); const middleware2 = sinon.spy((_req, res) => { res.send('middleware2 response'); }); await createServiceWithMiddlewareExt( [], () => undefined, [middleware1, middleware2], ); await expect( broker.request<string, string>('hello.method1', ''), ).to.eventually.have.property('data', 'middleware1 response'); expect(middleware1.callCount).to.eq(1); expect(middleware2.callCount).to.eq(1); }); it('not called after handler errors out', async function () { const middleware = sinon.spy((_req, res) => { res.send('middleware response'); }); await createServiceWithMiddlewareExt( [], () => { throw new Error('handler error'); }, [middleware], ); await expect( broker.request<string, string>('hello.method1', ''), ).to.be.eventually.rejectedWith('handler error'); expect(middleware.callCount).to.eq(0); }); }); it('pass-thru middleware', async function () { function middlewarePairGen(): [SinonSpy[], SinonSpy[]] { let passThruVar = 1; return [ [ sinon.spy(() => { expect(passThruVar).to.eq(1); passThruVar = 2; }), ], [ sinon.spy(() => { expect(passThruVar).to.eq(2); }), ], ]; } const pair = middlewarePairGen(); await createServiceWithMiddlewareExt( pair[0], undefined, pair[1], ); await expect( broker.request<string, string>('hello.method1', ''), ).to.eventually.have.property('data', 'method response'); expect(pair[0][0].callCount).to.eq(1); expect(pair[1][0].callCount).to.eq(1); }); it('expect async middlewares to wait for eachother', async function () { let callStack = 0; const middleware = sinon.spy(async () => { if (callStack !== 0) throw new Error('Entered a mw not waiting for the previous mw to finish'); callStack++; await sleepMs(100); callStack--; }); await createServiceWithMiddlewareExt( [middleware, middleware, middleware, middleware], undefined, [middleware, middleware, middleware, middleware], ); await expect( broker.request<string, string>('hello.method1', ''), ).to.eventually.have.property('data', 'method response'); expect(middleware.callCount).to.eq(8); expect(callStack).to.eq(0); }); });