mirror of
https://github.com/nestjs/nest.git
synced 2026-02-21 23:11:44 +00:00
fix(core): fix race condition in class dependency resolution
Fix race condition in class dependency resolution, which could otherwise lead to undefined or null injection. Split the resolution process in 2 parts with barrier synchronization in between to ensure all dependencies are present in dependant's instance wrapper and the staticity of its dependency tree is evaluated correctly. Closes #4873
This commit is contained in:
130
integration/injector/e2e/many-global-modules.spec.ts
Normal file
130
integration/injector/e2e/many-global-modules.spec.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { Global, Inject, Injectable, Module, Scope } from '@nestjs/common';
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule1 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule2 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule3 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule4 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule5 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule6 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule7 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule8 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule9 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule10 {}
|
||||
|
||||
@Injectable()
|
||||
class TransientProvider {}
|
||||
|
||||
@Injectable()
|
||||
class RequestProvider {}
|
||||
|
||||
@Injectable()
|
||||
export class Dependant {
|
||||
constructor(
|
||||
private readonly transientProvider: TransientProvider,
|
||||
|
||||
@Inject(RequestProvider)
|
||||
private readonly requestProvider: RequestProvider,
|
||||
) {}
|
||||
|
||||
public checkDependencies() {
|
||||
expect(this.transientProvider).to.be.instanceOf(TransientProvider);
|
||||
expect(this.requestProvider).to.be.instanceOf(RequestProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [
|
||||
{
|
||||
provide: TransientProvider,
|
||||
scope: Scope.TRANSIENT,
|
||||
useClass: TransientProvider,
|
||||
},
|
||||
{
|
||||
provide: Dependant,
|
||||
scope: Scope.DEFAULT,
|
||||
useClass: Dependant,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class GlobalModuleWithTransientProviderAndDependant {}
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [
|
||||
{
|
||||
provide: RequestProvider,
|
||||
scope: Scope.REQUEST,
|
||||
useFactory: () => {
|
||||
return new RequestProvider();
|
||||
},
|
||||
},
|
||||
],
|
||||
exports: [RequestProvider],
|
||||
})
|
||||
export class GlobalModuleWithRequestProvider {}
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
GlobalModule1,
|
||||
GlobalModule2,
|
||||
GlobalModule3,
|
||||
GlobalModule4,
|
||||
GlobalModule5,
|
||||
GlobalModule6,
|
||||
GlobalModule7,
|
||||
GlobalModule8,
|
||||
GlobalModule9,
|
||||
GlobalModule10,
|
||||
GlobalModuleWithTransientProviderAndDependant,
|
||||
GlobalModuleWithRequestProvider,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
describe('Many global modules', () => {
|
||||
it('should inject request-scoped useFactory provider and transient-scoped useClass provider from different modules', async () => {
|
||||
const moduleBuilder = Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
});
|
||||
const moduleRef = await moduleBuilder.compile();
|
||||
|
||||
const dependant = await moduleRef.resolve(Dependant);
|
||||
const checkDependenciesSpy = sinon.spy(dependant, 'checkDependencies');
|
||||
dependant.checkDependencies();
|
||||
|
||||
expect(checkDependenciesSpy.called).to.be.true;
|
||||
});
|
||||
});
|
||||
@@ -2197,22 +2197,6 @@
|
||||
},
|
||||
"id": "1976848738"
|
||||
},
|
||||
"-2105726668": {
|
||||
"source": "-1803759743",
|
||||
"target": "1010833816",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "PropertiesModule",
|
||||
"sourceClassName": "PropertiesService",
|
||||
"targetClassName": "token",
|
||||
"sourceClassToken": "PropertiesService",
|
||||
"targetClassToken": "token",
|
||||
"targetModuleName": "PropertiesModule",
|
||||
"keyOrIndex": "token",
|
||||
"injectionType": "property"
|
||||
},
|
||||
"id": "-2105726668"
|
||||
},
|
||||
"-21463590": {
|
||||
"source": "-1378706112",
|
||||
"target": "1004276345",
|
||||
@@ -2229,6 +2213,22 @@
|
||||
},
|
||||
"id": "-21463590"
|
||||
},
|
||||
"-2105726668": {
|
||||
"source": "-1803759743",
|
||||
"target": "1010833816",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "PropertiesModule",
|
||||
"sourceClassName": "PropertiesService",
|
||||
"targetClassName": "token",
|
||||
"sourceClassToken": "PropertiesService",
|
||||
"targetClassToken": "token",
|
||||
"targetModuleName": "PropertiesModule",
|
||||
"keyOrIndex": "token",
|
||||
"injectionType": "property"
|
||||
},
|
||||
"id": "-2105726668"
|
||||
},
|
||||
"-1657371464": {
|
||||
"source": "-1673986099",
|
||||
"target": "1919157847",
|
||||
|
||||
Reference in New Issue
Block a user