Skip to content

Commit e8a66ea

Browse files
test(): increase test coverage (unit tests)
1 parent 8bd4fe2 commit e8a66ea

4 files changed

Lines changed: 236 additions & 88 deletions

File tree

packages/core/router/router-explorer.ts

Lines changed: 59 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { STATIC_CONTEXT } from '../injector/constants';
2121
import { NestContainer } from '../injector/container';
2222
import { Injector } from '../injector/injector';
2323
import { ContextId, InstanceWrapper } from '../injector/instance-wrapper';
24+
import { Module } from '../injector/module';
2425
import { InterceptorsConsumer } from '../interceptors/interceptors-consumer';
2526
import { InterceptorsContextCreator } from '../interceptors/interceptors-context-creator';
2627
import { MetadataScanner } from '../metadata-scanner';
@@ -183,52 +184,15 @@ export class RouterExplorer {
183184

184185
const isRequestScoped = !instanceWrapper.isDependencyTreeStatic();
185186
const module = this.container.getModuleByKey(moduleKey);
186-
const collection = module.controllers;
187187

188188
if (isRequestScoped) {
189-
const handler = async <TRequest, TResponse>(
190-
req: TRequest,
191-
res: TResponse,
192-
next: () => void,
193-
) => {
194-
try {
195-
const contextId = createContextId();
196-
this.registerRequestProvider(req, contextId);
197-
198-
const contextInstance = await this.injector.loadPerContext(
199-
instance,
200-
module,
201-
collection,
202-
contextId,
203-
);
204-
this.createCallbackProxy(
205-
contextInstance,
206-
contextInstance[methodName],
207-
methodName,
208-
moduleKey,
209-
requestMethod,
210-
contextId,
211-
instanceWrapper.id,
212-
)(req, res, next);
213-
} catch (err) {
214-
let exceptionFilter = this.exceptionFiltersCache.get(
215-
instance[methodName],
216-
);
217-
if (!exceptionFilter) {
218-
exceptionFilter = this.exceptionsFilter.create(
219-
instance,
220-
instance[methodName],
221-
moduleKey,
222-
);
223-
this.exceptionFiltersCache.set(
224-
instance[methodName],
225-
exceptionFilter,
226-
);
227-
}
228-
const host = new ExecutionContextHost([req, res, next]);
229-
exceptionFilter.next(err, host);
230-
}
231-
};
189+
const handler = this.createRequestScopedHandler(
190+
instanceWrapper,
191+
requestMethod,
192+
module,
193+
moduleKey,
194+
methodName,
195+
);
232196

233197
paths.forEach(path => {
234198
const fullPath = stripSlash(basePath) + path;
@@ -277,6 +241,57 @@ export class RouterExplorer {
277241
return this.routerProxy.createProxy(executionContext, exceptionFilter);
278242
}
279243

244+
public createRequestScopedHandler(
245+
instanceWrapper: InstanceWrapper,
246+
requestMethod: RequestMethod,
247+
module: Module,
248+
moduleKey: string,
249+
methodName: string,
250+
) {
251+
const { instance } = instanceWrapper;
252+
const collection = module.controllers;
253+
return async <TRequest, TResponse>(
254+
req: TRequest,
255+
res: TResponse,
256+
next: () => void,
257+
) => {
258+
try {
259+
const contextId = createContextId();
260+
this.registerRequestProvider(req, contextId);
261+
262+
const contextInstance = await this.injector.loadPerContext(
263+
instance,
264+
module,
265+
collection,
266+
contextId,
267+
);
268+
this.createCallbackProxy(
269+
contextInstance,
270+
contextInstance[methodName],
271+
methodName,
272+
moduleKey,
273+
requestMethod,
274+
contextId,
275+
instanceWrapper.id,
276+
)(req, res, next);
277+
} catch (err) {
278+
let exceptionFilter = this.exceptionFiltersCache.get(
279+
instance[methodName],
280+
);
281+
if (!exceptionFilter) {
282+
exceptionFilter = this.exceptionsFilter.create(
283+
instance,
284+
instance[methodName],
285+
moduleKey,
286+
);
287+
this.exceptionFiltersCache.set(instance[methodName], exceptionFilter);
288+
}
289+
const host = new ExecutionContextHost([req, res, next]);
290+
exceptionFilter.next(err, host);
291+
}
292+
};
293+
}
294+
280295
private registerRequestProvider<T = any>(request: T, contextId: ContextId) {
281296
const coreModuleRef = this.container.getInternalCoreModuleRef();
282297
const wrapper = coreModuleRef.getProviderByKey(REQUEST);

packages/core/test/router/router-explorer.spec.ts

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@ import {
77
Post,
88
} from '../../../common/decorators/http/request-mapping.decorator';
99
import { RequestMethod } from '../../../common/enums/request-method.enum';
10+
import { Injector } from '../../../core/injector/injector';
11+
import { ApplicationConfig } from '../../application-config';
12+
import { ExecutionContextHost } from '../../helpers/execution-context-host';
1013
import { NestContainer } from '../../injector/container';
14+
import { InstanceWrapper } from '../../injector/instance-wrapper';
1115
import { MetadataScanner } from '../../metadata-scanner';
16+
import { RouterExceptionFilters } from '../../router/router-exception-filters';
1217
import { RouterExplorer } from '../../router/router-explorer';
1318

1419
describe('RouterExplorer', () => {
@@ -28,11 +33,24 @@ describe('RouterExplorer', () => {
2833
}
2934

3035
let routerBuilder: RouterExplorer;
36+
let injector: Injector;
37+
let exceptionsFilter: RouterExceptionFilters;
3138

3239
beforeEach(() => {
40+
const container = new NestContainer();
41+
42+
injector = new Injector();
43+
exceptionsFilter = new RouterExceptionFilters(
44+
container,
45+
new ApplicationConfig(),
46+
null,
47+
);
3348
routerBuilder = new RouterExplorer(
3449
new MetadataScanner(),
35-
new NestContainer(),
50+
container,
51+
injector,
52+
null,
53+
exceptionsFilter,
3654
);
3755
});
3856

@@ -115,4 +133,49 @@ describe('RouterExplorer', () => {
115133
expect(() => routerBuilder.validateRoutePath(undefined)).to.throw();
116134
});
117135
});
136+
137+
describe('createRequestScopedHandler', () => {
138+
let nextSpy: sinon.SinonSpy;
139+
140+
beforeEach(() => {
141+
sinon.stub(injector, 'loadPerContext').callsFake(() => {
142+
throw new Error();
143+
});
144+
nextSpy = sinon.spy();
145+
sinon.stub(exceptionsFilter, 'create').callsFake(
146+
() =>
147+
({
148+
next: nextSpy,
149+
} as any),
150+
);
151+
});
152+
153+
describe('when "loadPerContext" throws', () => {
154+
const moduleKey = 'moduleKey';
155+
const methodKey = 'methodKey';
156+
const module = {
157+
controllers: new Map(),
158+
} as any;
159+
const wrapper = new InstanceWrapper({
160+
instance: { [methodKey]: {} },
161+
});
162+
163+
it('should delegete error to exception filters', async () => {
164+
const handler = routerBuilder.createRequestScopedHandler(
165+
wrapper,
166+
RequestMethod.ALL,
167+
module,
168+
moduleKey,
169+
methodKey,
170+
);
171+
await handler(null, null, null);
172+
173+
expect(nextSpy.called).to.be.true;
174+
expect(nextSpy.getCall(0).args[0]).to.be.instanceOf(Error);
175+
expect(nextSpy.getCall(0).args[1]).to.be.instanceOf(
176+
ExecutionContextHost,
177+
);
178+
});
179+
});
180+
});
118181
});

packages/microservices/listeners-controller.ts

Lines changed: 57 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,18 @@ import {
77
ContextId,
88
InstanceWrapper,
99
} from '@nestjs/core/injector/instance-wrapper';
10+
import { Module } from '@nestjs/core/injector/module';
1011
import { MetadataScanner } from '@nestjs/core/metadata-scanner';
1112
import { REQUEST } from '@nestjs/core/router/request/request-constants';
1213
import { IClientProxyFactory } from './client/client-proxy-factory';
1314
import { ClientsContainer } from './container';
1415
import { ExceptionFiltersContext } from './context/exception-filters-context';
1516
import { RpcContextCreator } from './context/rpc-context-creator';
16-
import { CustomTransportStrategy, RequestContext } from './interfaces';
17+
import {
18+
CustomTransportStrategy,
19+
PatternMetadata,
20+
RequestContext,
21+
} from './interfaces';
1722
import { ListenerMetadataExplorer } from './listener-metadata-explorer';
1823
import { Server } from './server/server';
1924

@@ -42,7 +47,6 @@ export class ListenersController {
4247
const isStatic = instanceWrapper.isDependencyTreeStatic();
4348
const patternHandlers = this.metadataExplorer.explore(instance);
4449
const module = this.container.getModuleByKey(moduleKey);
45-
const collection = module.controllers;
4650

4751
patternHandlers.forEach(
4852
({ pattern, targetCallback, methodKey, isEventHandler }) => {
@@ -54,43 +58,13 @@ export class ListenersController {
5458
);
5559
return server.addHandler(pattern, proxy, isEventHandler);
5660
}
57-
const asyncHandler = async (...args: unknown[]) => {
58-
try {
59-
const data = args[0];
60-
const contextId = createContextId();
61-
this.registerRequestProvider({ pattern, data }, contextId);
62-
63-
const contextInstance = await this.injector.loadPerContext(
64-
instance,
65-
module,
66-
collection,
67-
contextId,
68-
);
69-
const proxy = this.contextCreator.create(
70-
contextInstance,
71-
contextInstance[methodKey],
72-
moduleKey,
73-
);
74-
return proxy(data);
75-
} catch (err) {
76-
let exceptionFilter = this.exceptionFiltersCache.get(
77-
instance[methodKey],
78-
);
79-
if (!exceptionFilter) {
80-
exceptionFilter = this.exceptionFiltersContext.create(
81-
instance,
82-
instance[methodKey],
83-
moduleKey,
84-
);
85-
this.exceptionFiltersCache.set(
86-
instance[methodKey],
87-
exceptionFilter,
88-
);
89-
}
90-
const host = new ExecutionContextHost(args);
91-
exceptionFilter.handle(err, host);
92-
}
93-
};
61+
const asyncHandler = this.createRequestScopedHandler(
62+
instance,
63+
pattern,
64+
module,
65+
moduleKey,
66+
methodKey,
67+
);
9468
server.addHandler(pattern, asyncHandler, isEventHandler);
9569
},
9670
);
@@ -116,6 +90,50 @@ export class ListenersController {
11690
Reflect.set(instance, property, client);
11791
}
11892

93+
public createRequestScopedHandler(
94+
instance: Controller,
95+
pattern: PatternMetadata,
96+
module: Module,
97+
moduleKey: string,
98+
methodKey: string,
99+
) {
100+
const collection = module.controllers;
101+
return async (...args: unknown[]) => {
102+
try {
103+
const data = args[0];
104+
const contextId = createContextId();
105+
this.registerRequestProvider({ pattern, data }, contextId);
106+
107+
const contextInstance = await this.injector.loadPerContext(
108+
instance,
109+
module,
110+
collection,
111+
contextId,
112+
);
113+
const proxy = this.contextCreator.create(
114+
contextInstance,
115+
contextInstance[methodKey],
116+
moduleKey,
117+
);
118+
return proxy(data);
119+
} catch (err) {
120+
let exceptionFilter = this.exceptionFiltersCache.get(
121+
instance[methodKey],
122+
);
123+
if (!exceptionFilter) {
124+
exceptionFilter = this.exceptionFiltersContext.create(
125+
instance,
126+
instance[methodKey],
127+
moduleKey,
128+
);
129+
this.exceptionFiltersCache.set(instance[methodKey], exceptionFilter);
130+
}
131+
const host = new ExecutionContextHost(args);
132+
exceptionFilter.handle(err, host);
133+
}
134+
};
135+
}
136+
119137
private registerRequestProvider(
120138
request: RequestContext,
121139
contextId: ContextId,

0 commit comments

Comments
 (0)