import { mkdtempSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { INestApplication } from '@nestjs/common'; import { Test, TestingModuleBuilder } from '@nestjs/testing'; import type { NestExpressApplication } from '@nestjs/platform-express'; import cookieParser from 'cookie-parser'; import { AppModule } from '../app.module'; /** * Boots the full application for e2e tests, mirroring main.ts middleware. * Requires TEST_DATABASE_URL; DATABASE_URL is pointed at it so the app * under test uses the test database. `customize` can override providers * (e.g. inject a fake converter for the conversion queue tests, issue #62). */ export async function createTestApp( customize?: (builder: TestingModuleBuilder) => TestingModuleBuilder, ): Promise { process.env.NODE_ENV = 'test'; if (process.env.TEST_DATABASE_URL) { process.env.DATABASE_URL = process.env.TEST_DATABASE_URL; } process.env.DATABASE_URL ??= 'postgresql://nobody:nothing@127.0.0.1:59999/absent'; // Fresh scratch directory per test file so upload tests never touch the // repository or collide with each other. process.env.UPLOADS_DIR ??= mkdtempSync(join(tmpdir(), 'dorfteich-uploads-')); process.env.PLUGINS_DIR ??= mkdtempSync(join(tmpdir(), 'dorfteich-plugins-')); const base = Test.createTestingModule({ imports: [AppModule] }); const moduleRef = await (customize ? customize(base) : base).compile(); const app = moduleRef.createNestApplication(); app.use(cookieParser()); // Mirrors main.ts: base64 Yjs page state needs more than Express's 100kb default. app.useBodyParser('json', { limit: '8mb' }); // Mirrors main.ts: the public API (issue #104) declares its full path. app.setGlobalPrefix('api/v1', { exclude: ['api/public/v1', 'api/public/v1/{*path}', 'api/mcp'], }); await app.init(); return app; } /** Extracts the dt_session cookie pair ("name=value") from a response. */ export function sessionCookieOf(res: { headers: Record }): string { const header = res.headers['set-cookie']; const cookies = Array.isArray(header) ? header : [header].filter(Boolean); const session = (cookies as string[]).find((c) => c.startsWith('dt_session=')); if (!session) throw new Error('response carries no dt_session cookie'); return session.split(';')[0]!; }