From 6f439834cdeee2abb5d8c91f7987b8755f68851b Mon Sep 17 00:00:00 2001 From: mag123c Date: Wed, 21 May 2025 14:11:09 +0900 Subject: [PATCH] fix(microservices): support custom strategy in async usefactory config --- packages/microservices/nest-microservice.ts | 25 ++- .../test/nest-microservice.spec.ts | 154 ++++++++++++++++++ 2 files changed, 170 insertions(+), 9 deletions(-) create mode 100644 packages/microservices/test/nest-microservice.spec.ts diff --git a/packages/microservices/nest-microservice.ts b/packages/microservices/nest-microservice.ts index ea299121c6e..dc943aaf8d5 100644 --- a/packages/microservices/nest-microservice.ts +++ b/packages/microservices/nest-microservice.ts @@ -78,22 +78,29 @@ export class NestMicroservice public createServer(config: CompleteMicroserviceOptions) { try { if ('useFactory' in config) { - this.microserviceConfig = this.resolveAsyncOptions(config); + const resolvedConfig = this.resolveAsyncOptions(config); + this.microserviceConfig = resolvedConfig; + + // Inject custom strategy + if ('strategy' in resolvedConfig) { + this.serverInstance = resolvedConfig.strategy as Server; + return; + } } else { this.microserviceConfig = { transport: Transport.TCP, ...config, } as MicroserviceOptions; - } - if ('strategy' in config) { - this.serverInstance = config.strategy as Server; - return; - } else { - this.serverInstance = ServerFactory.create( - this.microserviceConfig, - ) as Server; + if ('strategy' in config) { + this.serverInstance = config.strategy as Server; + return; + } } + + this.serverInstance = ServerFactory.create( + this.microserviceConfig, + ) as Server; } catch (e) { this.logger.error(e); throw e; diff --git a/packages/microservices/test/nest-microservice.spec.ts b/packages/microservices/test/nest-microservice.spec.ts new file mode 100644 index 00000000000..1ee8277167a --- /dev/null +++ b/packages/microservices/test/nest-microservice.spec.ts @@ -0,0 +1,154 @@ +import { NestMicroservice } from '@nestjs/microservices/nest-microservice'; +import { Server, ServerTCP } from '@nestjs/microservices/server'; +import { GraphInspector } from '@nestjs/core/inspector/graph-inspector'; +import { ApplicationConfig } from '@nestjs/core/application-config'; +import { Transport } from '@nestjs/microservices/enums'; +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import { AsyncMicroserviceOptions } from '@nestjs/microservices/interfaces'; + +const createMockGraphInspector = (): GraphInspector => + ({ + insertOrphanedEnhancer: sinon.stub(), + }) as unknown as GraphInspector; + +const createMockAppConfig = (): ApplicationConfig => + ({ + useGlobalFilters: sinon.stub(), + useGlobalPipes: sinon.stub(), + useGlobalGuards: sinon.stub(), + useGlobalInterceptors: sinon.stub(), + setIoAdapter: sinon.stub(), + }) as unknown as ApplicationConfig; + +const mockContainer = { + getModuleCompiler: sinon.stub(), + getModules: () => new Map(), + get: () => null, + getHttpAdapterHost: () => undefined, +} as any; + +describe('NestMicroservice', () => { + let mockGraphInspector: GraphInspector; + let mockAppConfig: ApplicationConfig; + + beforeEach(() => { + mockGraphInspector = createMockGraphInspector(); + mockAppConfig = createMockAppConfig(); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('should use ServerFactory if no strategy is provided', () => { + const instance = new NestMicroservice( + mockContainer, + { transport: Transport.TCP }, + mockGraphInspector, + mockAppConfig, + ); + + expect((instance as any).serverInstance).to.be.instanceOf(ServerTCP); + }); + + it('should use provided strategy if present in config', () => { + const strategy = new (class extends Server { + listen = sinon.spy(); + close = sinon.spy(); + on = sinon.stub(); + unwrap = sinon.stub(); + })(); + + const instance = new NestMicroservice( + mockContainer, + { strategy }, + mockGraphInspector, + mockAppConfig, + ); + + expect((instance as any).serverInstance).to.equal(strategy); + }); + + it('should use strategy resolved from useFactory config', () => { + const strategy = new (class extends Server { + listen = sinon.spy(); + close = sinon.spy(); + on = sinon.stub(); + unwrap = sinon.stub(); + })(); + const asyncConfig: AsyncMicroserviceOptions = { + useFactory: () => ({ strategy }), + inject: [], + }; + + const instance = new NestMicroservice( + mockContainer, + asyncConfig, + mockGraphInspector, + mockAppConfig, + ); + + expect((instance as any).serverInstance).to.equal(strategy); + }); + + it('should call listen() on server when listen() is called', async () => { + const listenSpy = sinon.spy((cb: () => void) => cb()); + const strategy = new (class extends Server { + listen = listenSpy; + close = sinon.spy(); + on = sinon.stub(); + unwrap = sinon.stub(); + })(); + + const instance = new NestMicroservice( + mockContainer, + { strategy }, + mockGraphInspector, + mockAppConfig, + ); + + await instance.listen(); + expect(listenSpy.calledOnce).to.be.true; + }); + + it('should delegate unwrap() to server', () => { + const unwrapStub = sinon.stub().returns('core'); + const strategy = new (class extends Server { + listen = sinon.spy(); + close = sinon.spy(); + on = sinon.stub(); + unwrap = unwrapStub; + })(); + + const instance = new NestMicroservice( + mockContainer, + { strategy }, + mockGraphInspector, + mockAppConfig, + ); + + expect(instance.unwrap()).to.equal('core'); + }); + + it('should delegate on() to server', () => { + const onStub = sinon.stub(); + const strategy = new (class extends Server { + listen = sinon.spy(); + close = sinon.spy(); + on = onStub; + unwrap = sinon.stub(); + })(); + + const instance = new NestMicroservice( + mockContainer, + { strategy }, + mockGraphInspector, + mockAppConfig, + ); + + const cb = () => {}; + instance.on('test:event', cb); + expect(onStub.calledWith('test:event', cb)).to.be.true; + }); +});