test(server): new test facilities (#10870)

close CLOUD-142
This commit is contained in:
forehalo
2025-03-17 10:02:12 +00:00
parent 92db9a693a
commit 9b5d12dc71
12 changed files with 311 additions and 46 deletions

View File

@@ -0,0 +1,49 @@
import { ModuleMetadata } from '@nestjs/common';
import {
Test,
TestingModule as NestjsTestingModule,
TestingModuleBuilder,
} from '@nestjs/testing';
import { FunctionalityModules } from '../app.module';
import { AFFiNELogger } from '../base';
import { TEST_LOG_LEVEL } from './utils';
interface TestingModuleMetadata extends ModuleMetadata {
tapModule?(m: TestingModuleBuilder): void;
}
export interface TestingModule extends NestjsTestingModule {
[Symbol.asyncDispose](): Promise<void>;
}
export async function createModule(
metadata: TestingModuleMetadata
): Promise<TestingModule> {
const { tapModule, ...meta } = metadata;
const builder = Test.createTestingModule({
...meta,
imports: [...FunctionalityModules, ...(meta.imports ?? [])],
});
// when custom override happens
if (tapModule) {
tapModule(builder);
}
const module = (await builder.compile()) as TestingModule;
const logger = new AFFiNELogger();
// we got a lot smoking tests try to break nestjs
// can't tolerate the noisy logs
logger.setLogLevels([TEST_LOG_LEVEL]);
module.useLogger(logger);
await module.init();
module[Symbol.asyncDispose] = async () => {
await module.close();
};
return module;
}

View File

@@ -0,0 +1,5 @@
import { app, e2e } from './test';
e2e('should create test app correctly', async t => {
t.truthy(app);
});

View File

@@ -0,0 +1,79 @@
import { INestApplication } from '@nestjs/common';
import { NestApplication } from '@nestjs/core';
import { Test, TestingModuleBuilder } from '@nestjs/testing';
import cookieParser from 'cookie-parser';
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
import {
AFFiNELogger,
CacheInterceptor,
CloudThrottlerGuard,
GlobalExceptionFilter,
OneMB,
} from '../../base';
import { SocketIoAdapter } from '../../base/websocket';
import { AuthGuard } from '../../core/auth';
import { TEST_LOG_LEVEL } from '../utils';
interface TestingAppMetadata {
tapModule?(m: TestingModuleBuilder): void;
tapApp?(app: INestApplication): void;
}
export class TestingApp extends NestApplication {
async [Symbol.asyncDispose]() {
await this.close();
}
}
export async function createApp(
metadata: TestingAppMetadata = {}
): Promise<TestingApp> {
const { buildAppModule } = await import('../../app.module');
const { tapModule, tapApp } = metadata;
const builder = Test.createTestingModule({
imports: [buildAppModule()],
});
// when custom override happens
if (tapModule) {
tapModule(builder);
}
const module = await builder.compile();
module.useCustomApplicationConstructor(TestingApp);
const app = module.createNestApplication<TestingApp>({
cors: true,
bodyParser: true,
rawBody: true,
});
const logger = new AFFiNELogger();
logger.setLogLevels([TEST_LOG_LEVEL]);
app.useLogger(logger);
app.use(cookieParser());
app.useBodyParser('raw', { limit: 1 * OneMB });
app.use(
graphqlUploadExpress({
maxFileSize: 10 * OneMB,
maxFiles: 5,
})
);
app.useGlobalGuards(app.get(AuthGuard), app.get(CloudThrottlerGuard));
app.useGlobalInterceptors(app.get(CacheInterceptor));
app.useGlobalFilters(new GlobalExceptionFilter(app.getHttpAdapter()));
const adapter = new SocketIoAdapter(app);
app.useWebSocketAdapter(adapter);
app.enableShutdownHooks();
if (tapApp) {
tapApp(app);
}
return app;
}

View File

@@ -0,0 +1,4 @@
import { createApp } from './create-app';
// @ts-expect-error testing
globalThis.app = await createApp();

View File

@@ -0,0 +1,9 @@
import test, { registerCompletionHandler } from 'ava';
export const e2e = test;
// @ts-expect-error created in prelude.ts
export const app = globalThis.app;
registerCompletionHandler(() => {
app.close();
});

View File

@@ -13,6 +13,9 @@ import { AFFiNELogger, Runtime } from '../../base';
import { GqlModule } from '../../base/graphql';
import { AuthGuard, AuthModule } from '../../core/auth';
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 { initTestingDB, TEST_LOG_LEVEL } from './utils';
@@ -48,6 +51,9 @@ class MockResolver {
}
}
/**
* @deprecated use {@link createModule} instead
*/
export async function createTestingModule(
moduleDef: TestingModuleMeatdata = {},
autoInitialize = true