From 6f1eddb601e3d8823a36e713654823c88eb77940 Mon Sep 17 00:00:00 2001 From: Himanshu Gupta Date: Sun, 28 Dec 2025 20:46:04 +0530 Subject: [PATCH] test(core): add regression test for request scope bubbling This adds an integration test to ensure that a Singleton service injected with a Request-Scoped dependency is correctly downgraded to Request-Scoped. This prevents future regressions in scope bubbling behavior. --- .../e2e/request-scope-bubbling.spec.ts | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 integration/injector/e2e/request-scope-bubbling.spec.ts diff --git a/integration/injector/e2e/request-scope-bubbling.spec.ts b/integration/injector/e2e/request-scope-bubbling.spec.ts new file mode 100644 index 000000000..ae0378d98 --- /dev/null +++ b/integration/injector/e2e/request-scope-bubbling.spec.ts @@ -0,0 +1,48 @@ +import { Injectable, Scope, Module } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { ContextIdFactory, REQUEST } from '@nestjs/core'; +import { expect } from 'chai'; + +describe('Request Scope Bubbling', () => { + // 1. Define the "Poison" (Request Scoped Service) + @Injectable({ scope: Scope.REQUEST }) + class ChildService { + // A random ID to verify uniqueness + public readonly id = Math.random(); + } + + // 2. Define the "Victim" (Singleton that depends on Request Scoped) + @Injectable() + class ParentService { + constructor(public readonly child: ChildService) {} + } + + @Module({ + providers: [ChildService, ParentService], + }) + class TestModule {} + + it('should downgrade a Singleton to Request-Scoped if it depends on a Request-Scoped provider', async () => { + // 3. Bootstrap the Module (using Test.createTestingModule instead of NestFactory) + const moduleRef: TestingModule = await Test.createTestingModule({ + imports: [TestModule], + }).compile(); + + // 4. Simulate Request 1 + const contextId1 = ContextIdFactory.create(); + const parent1 = await moduleRef.resolve(ParentService, contextId1); + + // 5. Simulate Request 2 + const contextId2 = ContextIdFactory.create(); + const parent2 = await moduleRef.resolve(ParentService, contextId2); + + // 6. Assertions (The "Moment of Truth") + + // The Child IDs should be different (Proof of Request Scope) + expect(parent1.child.id).to.not.equal(parent2.child.id); + + // The Parent instances should ALSO be different (Proof of Bubbling) + // If Parent was a true Singleton, these would be equal. + expect(parent1).to.not.equal(parent2); + }); +});