diff --git a/packages/backend/server/src/__tests__/copilot.e2e.ts b/packages/backend/server/src/__tests__/copilot.e2e.ts index 0f402e8cfd..6c54929fba 100644 --- a/packages/backend/server/src/__tests__/copilot.e2e.ts +++ b/packages/backend/server/src/__tests__/copilot.e2e.ts @@ -5,6 +5,7 @@ import type { TestFn } from 'ava'; import ava from 'ava'; import Sinon from 'sinon'; +import { JobQueue } from '../base'; import { ConfigModule } from '../base/config'; import { AuthService } from '../core/auth'; import { WorkspaceModule } from '../core/workspaces'; @@ -88,6 +89,10 @@ test.before(async t => { WorkspaceModule, CopilotModule, ], + tapModule: m => { + // use real JobQueue for testing + m.overrideProvider(JobQueue).useClass(JobQueue); + }, }); const auth = app.get(AuthService); diff --git a/packages/backend/server/src/__tests__/mocks/index.ts b/packages/backend/server/src/__tests__/mocks/index.ts index 4e8dba884d..6e116cfa7e 100644 --- a/packages/backend/server/src/__tests__/mocks/index.ts +++ b/packages/backend/server/src/__tests__/mocks/index.ts @@ -4,6 +4,7 @@ export * from './user.mock'; export * from './workspace.mock'; import { MockMailer } from './mailer.mock'; +import { MockJobQueue } from './queue.mock'; import { MockTeamWorkspace } from './team-workspace.mock'; import { MockUser } from './user.mock'; import { MockWorkspace } from './workspace.mock'; @@ -14,4 +15,4 @@ export const Mockers = { TeamWorkspace: MockTeamWorkspace, }; -export { MockMailer }; +export { MockJobQueue, MockMailer }; diff --git a/packages/backend/server/src/__tests__/mocks/queue.mock.ts b/packages/backend/server/src/__tests__/mocks/queue.mock.ts new file mode 100644 index 0000000000..4d03b83899 --- /dev/null +++ b/packages/backend/server/src/__tests__/mocks/queue.mock.ts @@ -0,0 +1,26 @@ +import Sinon from 'sinon'; + +import { JobQueue } from '../../base'; + +export class MockJobQueue { + add = Sinon.createStubInstance(JobQueue).add.resolves(); + + last(name: Job): { name: Job; payload: Jobs[Job] } { + const addJobName = this.add.lastCall?.args[0]; + const payload = this.add.lastCall?.args[1]; + + if (!payload) { + throw new Error('No job ever added'); + } + + if (addJobName !== name) { + throw new Error(`Job name mismatch: ${addJobName} !== ${name}`); + } + + return { name, payload }; + } + + count(name: JobName) { + return this.add.getCalls().filter(call => call.args[0] === name).length; + } +} diff --git a/packages/backend/server/src/__tests__/utils/testing-app.ts b/packages/backend/server/src/__tests__/utils/testing-app.ts index eb40474560..d37637a852 100644 --- a/packages/backend/server/src/__tests__/utils/testing-app.ts +++ b/packages/backend/server/src/__tests__/utils/testing-app.ts @@ -8,11 +8,22 @@ import cookieParser from 'cookie-parser'; import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs'; import supertest from 'supertest'; -import { AFFiNELogger, ApplyType, GlobalExceptionFilter } from '../../base'; +import { + AFFiNELogger, + ApplyType, + GlobalExceptionFilter, + JobQueue, +} from '../../base'; import { AuthService } from '../../core/auth'; import { Mailer } from '../../core/mail'; import { UserModel } from '../../models'; -import { createFactory, MockedUser, MockUser, MockUserInput } from '../mocks'; +import { + createFactory, + MockedUser, + MockJobQueue, + MockUser, + MockUserInput, +} from '../mocks'; import { MockMailer } from '../mocks/mailer.mock'; import { createTestingModule } from './testing-module'; import { initTestingDB, TEST_LOG_LEVEL } from './utils'; @@ -85,6 +96,7 @@ export class TestingApp extends ApplyType() { readonly create!: ReturnType; readonly mails!: MockMailer; + readonly queue!: MockJobQueue; [Symbol.asyncDispose](): Promise { return this.close(); @@ -285,6 +297,8 @@ function makeTestingApp(app: INestApplication): TestingApp { testingApp.create = createFactory(app.get(PrismaClient, { strict: false })); // @ts-expect-error allow testingApp.mails = app.get(Mailer, { strict: false }) as MockMailer; + // @ts-expect-error allow + testingApp.queue = app.get(JobQueue, { strict: false }) as MockJobQueue; return new Proxy(testingApp, { get(target, prop) { diff --git a/packages/backend/server/src/__tests__/utils/testing-module.ts b/packages/backend/server/src/__tests__/utils/testing-module.ts index 4df3b15c94..68bfa858d1 100644 --- a/packages/backend/server/src/__tests__/utils/testing-module.ts +++ b/packages/backend/server/src/__tests__/utils/testing-module.ts @@ -9,7 +9,7 @@ import { import { PrismaClient } from '@prisma/client'; import { AppModule, FunctionalityModules } from '../../app.module'; -import { AFFiNELogger, Runtime } from '../../base'; +import { AFFiNELogger, JobQueue, Runtime } from '../../base'; import { GqlModule } from '../../base/graphql'; import { AuthGuard, AuthModule } from '../../core/auth'; import { Mailer, MailModule } from '../../core/mail'; @@ -17,11 +17,11 @@ import { ModelsModule } from '../../models'; // for jsdoc inference // oxlint-disable-next-line no-unused-vars import type { createModule } from '../create-module'; -import { createFactory } from '../mocks'; +import { createFactory, MockJobQueue } from '../mocks'; import { MockMailer } from '../mocks/mailer.mock'; import { initTestingDB, TEST_LOG_LEVEL } from './utils'; -interface TestingModuleMeatdata extends ModuleMetadata { +interface TestingModuleMetadata extends ModuleMetadata { tapModule?(m: TestingModuleBuilder): void; } @@ -29,6 +29,7 @@ export interface TestingModule extends BaseTestingModule { initTestingDB(): Promise; create: ReturnType; mails: MockMailer; + queue: MockJobQueue; [Symbol.asyncDispose](): Promise; } @@ -58,7 +59,7 @@ class MockResolver { * @deprecated use {@link createModule} instead */ export async function createTestingModule( - moduleDef: TestingModuleMeatdata = {}, + moduleDef: TestingModuleMetadata = {}, autoInitialize = true ): Promise { // setting up @@ -88,10 +89,11 @@ export async function createTestingModule( controllers: moduleDef.controllers, }); + builder.overrideProvider(Mailer).useClass(MockMailer); + builder.overrideProvider(JobQueue).useClass(MockJobQueue); if (moduleDef.tapModule) { moduleDef.tapModule(builder); } - builder.overrideProvider(Mailer).useClass(MockMailer); const module = await builder.compile(); @@ -114,6 +116,7 @@ export async function createTestingModule( }; testingModule.mails = module.get(Mailer, { strict: false }) as MockMailer; + testingModule.queue = module.get(JobQueue, { strict: false }) as MockJobQueue; const logger = new AFFiNELogger(); // we got a lot smoking tests try to break nestjs diff --git a/packages/backend/server/src/base/job/queue/__tests__/queue.spec.ts b/packages/backend/server/src/base/job/queue/__tests__/queue.spec.ts index 14f2e18fe6..d0e309cbb3 100644 --- a/packages/backend/server/src/base/job/queue/__tests__/queue.spec.ts +++ b/packages/backend/server/src/base/job/queue/__tests__/queue.spec.ts @@ -68,6 +68,10 @@ test.before(async () => { JobModule.forRoot(), ], providers: [JobHandlers], + tapModule: builder => { + // use real JobQueue for testing + builder.overrideProvider(JobQueue).useClass(JobQueue); + }, }); queue = module.get(JobQueue);