import { API_PREFIX, ErrorCode } from '@lingxi/shared'; import { Controller, Get, NotFoundException } from '@nestjs/common'; import { FastifyAdapter, type NestFastifyApplication } from '@nestjs/platform-fastify'; import { Test } from '@nestjs/testing'; import { type Mock, vi } from 'vitest'; import { AppException } from '../common/errors/app-exception'; import { AllExceptionsFilter } from '../common/filters/all-exceptions.filter'; import { APP_CONFIG } from '../config/config.module'; import type { AppConfig } from '../config/env'; import { PRISMA, REDIS } from '../infra/tokens'; import { HealthController } from '../modules/platform/health/health.controller'; import { HealthService } from '../modules/platform/health/health.service'; export const testConfig: AppConfig = { nodeEnv: 'test', isProduction: false, port: 3000, host: '127.0.0.1', logLevel: 'error', databaseUrl: 'postgresql://lingxi:lingxi@127.0.0.1:15432/lingxi_test', redisUrl: 'redis://127.0.0.1:16379', webOrigins: ['http://127.0.0.1:5173'], maxUploadMb: 500, featureVideo: false, }; @Controller('__test') class ThrowingController { @Get('app-error') appError(): never { throw new AppException({ code: ErrorCode.STATE_NOT_ALLOWED, message: '当前状态不允许该操作' }); } @Get('http-404') notFound(): never { throw new NotFoundException('资源不存在'); } @Get('boom') boom(): never { throw new Error('unexpected failure'); } } export interface TestAppOptions { databaseUp?: boolean; redisUp?: boolean; includeErrorRoutes?: boolean; } export interface TestApp { app: NestFastifyApplication; prisma: { $queryRaw: Mock }; redis: { ping: Mock }; } export async function createTestApp(options: TestAppOptions = {}): Promise { const prisma = { $queryRaw: options.databaseUp === false ? vi.fn().mockRejectedValue(new Error('connect ECONNREFUSED 127.0.0.1:15432')) : vi.fn().mockResolvedValue([{ '?column?': 1 }]), }; const redis = { ping: options.redisUp === false ? vi.fn().mockRejectedValue(new Error('connect ECONNREFUSED 127.0.0.1:16379')) : vi.fn().mockResolvedValue('PONG'), }; const moduleRef = await Test.createTestingModule({ controllers: options.includeErrorRoutes === true ? [HealthController, ThrowingController] : [HealthController], providers: [ HealthService, { provide: PRISMA, useValue: prisma }, { provide: REDIS, useValue: redis }, { provide: APP_CONFIG, useValue: testConfig }, ], }).compile(); const app = moduleRef.createNestApplication(new FastifyAdapter()); app.setGlobalPrefix(API_PREFIX, { exclude: ['health'] }); app.useGlobalFilters(new AllExceptionsFilter()); await app.init(); return { app, prisma, redis }; }