Files
nest/packages/core/test/middleware/middleware-module.spec.ts
2022-11-21 11:00:11 +01:00

272 lines
8.5 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { RoutePathFactory } from '@nestjs/core/router/route-path-factory';
import * as chai from 'chai';
import { expect } from 'chai';
import * as chaiAsPromised from 'chai-as-promised';
import * as sinon from 'sinon';
import { Controller } from '../../../common/decorators/core/controller.decorator';
import { RequestMapping } from '../../../common/decorators/http/request-mapping.decorator';
import { RequestMethod } from '../../../common/enums/request-method.enum';
import { NestMiddleware } from '../../../common/interfaces/middleware/nest-middleware.interface';
import { ApplicationConfig } from '../../application-config';
import { InvalidMiddlewareException } from '../../errors/exceptions/invalid-middleware.exception';
import { RuntimeException } from '../../errors/exceptions/runtime.exception';
import { NestContainer } from '../../injector/container';
import { InstanceWrapper } from '../../injector/instance-wrapper';
import { Module } from '../../injector/module';
import { GraphInspector } from '../../inspector/graph-inspector';
import { MiddlewareBuilder } from '../../middleware/builder';
import { MiddlewareContainer } from '../../middleware/container';
import { MiddlewareModule } from '../../middleware/middleware-module';
import { RouterExceptionFilters } from '../../router/router-exception-filters';
import { NoopHttpAdapter } from '../utils/noop-adapter.spec';
chai.use(chaiAsPromised);
describe('MiddlewareModule', () => {
let middlewareModule: MiddlewareModule;
let graphInspector: GraphInspector;
@Controller('test')
class BasicController {}
@Controller('test')
class BaseController {
@RequestMapping({ path: 'test' })
public getTest() {}
@RequestMapping({ path: 'another', method: RequestMethod.DELETE })
public getAnother() {}
}
@Injectable()
class TestMiddleware implements NestMiddleware {
public use(req, res, next) {}
}
beforeEach(() => {
const container = new NestContainer();
const appConfig = new ApplicationConfig();
graphInspector = new GraphInspector(container);
middlewareModule = new MiddlewareModule(new RoutePathFactory(appConfig));
middlewareModule['routerExceptionFilter'] = new RouterExceptionFilters(
container,
appConfig,
new NoopHttpAdapter({}),
);
middlewareModule['config'] = appConfig;
middlewareModule['graphInspector'] = graphInspector;
});
describe('loadConfiguration', () => {
it('should call "configure" method if method is implemented', async () => {
const stubContainer = new NestContainer();
stubContainer
.getModules()
.set('Test', new Module(class {}, stubContainer));
const configureSpy = sinon.spy();
const mockModule = {
instance: {
configure: configureSpy,
},
};
(middlewareModule as any).container = stubContainer;
await middlewareModule.loadConfiguration(
new MiddlewareContainer(stubContainer),
mockModule as any,
'Test',
);
expect(configureSpy.calledOnce).to.be.true;
expect(
configureSpy.calledWith(
new MiddlewareBuilder(
(middlewareModule as any).routesMapper,
undefined,
),
),
).to.be.true;
});
});
describe('registerRouteMiddleware', () => {
class TestModule {}
let nestContainer: NestContainer;
beforeEach(() => {
nestContainer = new NestContainer();
nestContainer
.getModules()
.set('Test', new Module(TestModule, nestContainer));
});
it('should throw "RuntimeException" exception when middleware is not stored in container', () => {
const route = { path: 'Test' };
const configuration = {
middleware: [TestMiddleware],
forRoutes: [BaseController],
};
const useSpy = sinon.spy();
const app = { use: useSpy };
middlewareModule['container'] = nestContainer;
expect(
middlewareModule.registerRouteMiddleware(
new MiddlewareContainer(nestContainer),
route as any,
configuration,
'Test',
app,
),
).to.eventually.be.rejectedWith(RuntimeException);
});
it('should throw "InvalidMiddlewareException" exception when middleware does not have "use" method', () => {
@Injectable()
class InvalidMiddleware {}
const route = { path: 'Test' };
const configuration = {
middleware: [InvalidMiddleware],
forRoutes: [BaseController],
};
const useSpy = sinon.spy();
const app = { use: useSpy };
const container = new MiddlewareContainer(nestContainer);
const moduleKey = 'Test';
container.insertConfig([configuration], moduleKey);
const instance = new InvalidMiddleware();
container.getMiddlewareCollection(moduleKey).set('InvalidMiddleware', {
metatype: InvalidMiddleware,
instance,
} as any);
expect(
middlewareModule.registerRouteMiddleware(
container,
route as any,
configuration,
moduleKey,
app,
),
).to.be.rejectedWith(InvalidMiddlewareException);
});
it('should mount middleware when is stored in container', async () => {
const route = 'testPath';
const configuration = {
middleware: [TestMiddleware],
forRoutes: ['test', BasicController, BaseController],
};
const createMiddlewareFactoryStub = sinon
.stub()
.callsFake(() => () => null);
const app = {
createMiddlewareFactory: createMiddlewareFactoryStub,
};
const stubContainer = new NestContainer();
stubContainer
.getModules()
.set('Test', new Module(TestModule, stubContainer));
const container = new MiddlewareContainer(stubContainer);
const moduleKey = 'Test';
container.insertConfig([configuration], moduleKey);
const instance = new TestMiddleware();
container.getMiddlewareCollection(moduleKey).set(
TestMiddleware,
new InstanceWrapper({
metatype: TestMiddleware,
instance,
}),
);
sinon
.stub(stubContainer, 'getModuleByKey')
.callsFake(() => new Module(class {}, stubContainer));
middlewareModule['container'] = stubContainer;
await middlewareModule.registerRouteMiddleware(
container,
{ path: route, method: RequestMethod.ALL },
configuration,
moduleKey,
app,
);
expect(createMiddlewareFactoryStub.calledOnce).to.be.true;
});
it('should insert the expected middleware definition', async () => {
const route = 'testPath';
const configuration = {
middleware: [TestMiddleware],
forRoutes: ['test', BasicController, BaseController],
};
const instance = new TestMiddleware();
const instanceWrapper = new InstanceWrapper({
metatype: TestMiddleware,
instance,
name: TestMiddleware.name,
});
const createMiddlewareFactoryStub = sinon
.stub()
.callsFake(() => () => null);
const app = {
createMiddlewareFactory: createMiddlewareFactoryStub,
};
const stubContainer = new NestContainer();
stubContainer
.getModules()
.set('Test', new Module(TestModule, stubContainer));
const container = new MiddlewareContainer(stubContainer);
const moduleKey = 'Test';
container.insertConfig([configuration], moduleKey);
container
.getMiddlewareCollection(moduleKey)
.set(TestMiddleware, instanceWrapper);
sinon
.stub(stubContainer, 'getModuleByKey')
.callsFake(() => new Module(class {}, stubContainer));
middlewareModule['container'] = stubContainer;
const insertEntrypointDefinitionSpy = sinon.spy(
graphInspector,
'insertEntrypointDefinition',
);
await middlewareModule.registerRouteMiddleware(
container,
{ path: route, method: RequestMethod.ALL },
configuration,
moduleKey,
app,
);
expect(createMiddlewareFactoryStub.calledOnce).to.be.true;
expect(
insertEntrypointDefinitionSpy.calledWith({
type: 'middleware',
methodName: 'use',
className: instanceWrapper.name,
classNodeId: instanceWrapper.id,
metadata: {
path: route,
requestMethod: 'ALL',
version: undefined,
},
}),
).to.be.true;
});
});
});