/* eslint-disable @typescript-eslint/no-explicit-any */ import { Test, TestingModuleBuilder } from '@nestjs/testing' import { TestTenantStrategy } from './tenant/TestTenant.strategy' import { CoreModule } from '@api/core' import { INestApplication, Type } from '@nestjs/common' import cookieParser from 'cookie-parser' import { useValidate } from '@api/validate' import { request } from './helper/request' import { TestModule } from './test.module' type TestingModule = { new (...args: any[]): unknown build: (builder: TestingModuleBuilder) => void | Promise } export type CreateTestCoreModuleOptions = { module: TestingModule build?: (builder: TestingModuleBuilder) => void | Promise } export class TestCoreModule { static create(options: CreateTestCoreModuleOptions) { const instance = new TestCoreModule(options) return instance.createTestingModule() } protected app!: INestApplication protected constructor(protected options: CreateTestCoreModuleOptions) {} protected async createTestingModule() { TestTenantStrategy.apply() const builder = Test.createTestingModule({ imports: [ // CoreModule.forRoot(), TestModule.forRoot(() => this.app), this.options.module, ], }) for (const module of this.modules) { if ('build' in module && typeof module.build === 'function') { module.build(builder) } } this.options.build?.(builder) const ref = await builder.compile() this.app = ref.createNestApplication({ logger: false, }) this.app.use(cookieParser('cookie-secret')) useValidate(this.app, this.options.module) await this.app.init() return { resolve: (token: string | symbol | Type): Promise => this.app.resolve(token), request: request(() => this.app.getHttpServer()), } } protected get modules() { const getModule = (module: any) => ('module' in module ? module.module : module) const toProcess = [this.options.module] as any[] const result = [] as any[] while (toProcess.length) { const current = toProcess.pop() const imports = Reflect.getMetadata('imports', getModule(current)) if (Array.isArray(imports)) { imports.map(getModule).forEach((imported) => { if (!result.includes(imported)) { toProcess.push(imported) } }) } if (!result.includes(current)) { result.push(current) } } return result } }