From 69a53b3dedeeae54afb322172fb98915fdf3d0a6 Mon Sep 17 00:00:00 2001 From: Kamil Kisiela Date: Thu, 2 Nov 2023 14:41:10 +0100 Subject: [PATCH 1/2] Test Execution Context in context of parallel GraphQL requests --- .../tests/execution-context.spec.ts | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/packages/graphql-modules/tests/execution-context.spec.ts b/packages/graphql-modules/tests/execution-context.spec.ts index 5804d22db1..88b475331d 100644 --- a/packages/graphql-modules/tests/execution-context.spec.ts +++ b/packages/graphql-modules/tests/execution-context.spec.ts @@ -8,6 +8,8 @@ import { gql, InjectionToken, testkit, + CONTEXT, + Inject, } from '../src'; import { @@ -510,3 +512,138 @@ test('accessing a singleton provider with execution context in another singleton getDependencyName: expectedName, }); }); + +test('accessing a singleton provider with execution context in another singleton provider (parallel requests)', async () => { + const spies = { + foo: jest.fn(), + bar: jest.fn(), + baz: jest.fn(), + }; + + const Name = new InjectionToken>('name'); + + @Injectable({ + scope: Scope.Singleton, + }) + class Foo { + @ExecutionContext() + public context!: ExecutionContext; + + constructor() { + spies.foo(); + } + + async getName() { + return this.context.injector.get(Name); + } + } + + @Injectable({ + scope: Scope.Singleton, + }) + class Bar { + constructor(private foo: Foo) { + spies.bar(); + } + + async getName() { + return this.foo.getName(); + } + } + + @Injectable({ + scope: Scope.Operation, + }) + class Baz { + constructor( + @Inject(Name) + private name: Promise + ) { + spies.baz(); + } + + async getName() { + return this.name; + } + } + + const mod = createModule({ + id: 'mod', + providers: [Foo, Bar, Baz], + typeDefs: gql` + type Query { + getName: String + getDependencyName: String + getNameFromContext: String + } + `, + resolvers: { + Query: { + getName: async (_a: {}, _b: {}, { injector }: GraphQLModules.Context) => + injector.get(Foo).getName(), + getDependencyName: async ( + _a: {}, + _b: {}, + { injector }: GraphQLModules.Context + ) => injector.get(Bar).getName(), + getNameFromContext: async ( + _a: {}, + _b: {}, + { injector }: GraphQLModules.Context + ) => injector.get(Baz).getName(), + }, + }, + }); + + const app = createApplication({ + modules: [mod], + providers: [ + { + provide: Name, + scope: Scope.Operation, + useFactory(ctx) { + return new Promise((resolve) => { + setTimeout(() => { + console.log('resolved'); + resolve(`request-${ctx.requestId}`); + }, Math.random() * 200); + }); + }, + deps: [CONTEXT], + }, + ], + }); + + const requests = new Array({ length: 5 }).map((_, i) => i); + + const results = await Promise.all( + requests.map((i) => + testkit.execute(app, { + contextValue: { + requestId: i, + }, + document: gql` + { + getName + getDependencyName + getNameFromContext + } + `, + }) + ) + ); + + expect(spies.bar).toHaveBeenCalledTimes(results.length); + expect(spies.foo).toHaveBeenCalledTimes(results.length); + + for (let i = 0; i < results.length; i++) { + const result = results[i]; + const expectedName = `request-${i}`; + expect(result.errors).not.toBeDefined(); + expect(result.data).toEqual({ + getName: expectedName, + getDependencyName: expectedName, + getNameFromContext: expectedName, + }); + } +}); From a780ed010c0335bd82f069e26e3daf51759eaef8 Mon Sep 17 00:00:00 2001 From: Kamil Kisiela Date: Thu, 2 Nov 2023 14:46:22 +0100 Subject: [PATCH 2/2] Update packages/graphql-modules/tests/execution-context.spec.ts Co-authored-by: Denis Badurina --- packages/graphql-modules/tests/execution-context.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/graphql-modules/tests/execution-context.spec.ts b/packages/graphql-modules/tests/execution-context.spec.ts index 88b475331d..32fd6d38c3 100644 --- a/packages/graphql-modules/tests/execution-context.spec.ts +++ b/packages/graphql-modules/tests/execution-context.spec.ts @@ -604,7 +604,6 @@ test('accessing a singleton provider with execution context in another singleton useFactory(ctx) { return new Promise((resolve) => { setTimeout(() => { - console.log('resolved'); resolve(`request-${ctx.requestId}`); }, Math.random() * 200); });