import { execFileSync, spawn, type ChildProcess } from "node:child_process" import process from "node:process" import { PostgreSqlContainer, type StartedPostgreSqlContainer, } from "@testcontainers/postgresql" const port = process.env.E2E_PORT ?? "3100" const host = "127.0.0.1" const baseUrl = `http://${host}:${port}` let container: StartedPostgreSqlContainer | undefined let nextProcess: ChildProcess | undefined let shuttingDown = false async function cleanup() { if (shuttingDown) return shuttingDown = true if (nextProcess && !nextProcess.killed) { nextProcess.kill("SIGTERM") } await container?.stop() container = undefined } async function main() { container = await new PostgreSqlContainer("postgres:18-alpine") .withDatabase("stock_manager_e2e") .withUsername("e2e") .withPassword("e2e") .start() const databaseUrl = container.getConnectionUri() const env = { ...process.env, DATABASE_URL: databaseUrl, AUTH_SECRET: process.env.AUTH_SECRET ?? "e2e-auth-secret", AUTH_URL: baseUrl, NEXTAUTH_URL: baseUrl, ADMIN_BOOTSTRAP_ENABLED: "true", ADMIN_USERNAME: "admin", ADMIN_EMAIL: "admin@example.test", ADMIN_NAME: "E2E Admin", ADMIN_PASSWORD: "admin-password", } process.env.DATABASE_URL = databaseUrl process.env.AUTH_SECRET = env.AUTH_SECRET process.env.AUTH_URL = baseUrl process.env.NEXTAUTH_URL = baseUrl process.env.ADMIN_BOOTSTRAP_ENABLED = "true" process.env.ADMIN_USERNAME = "admin" process.env.ADMIN_EMAIL = "admin@example.test" process.env.ADMIN_NAME = "E2E Admin" process.env.ADMIN_PASSWORD = "admin-password" try { execFileSync("bunx", ["prisma", "migrate", "deploy"], { env, stdio: "inherit", }) const prismaModule = await import("@/lib/prisma") const bootstrapModule = await import("../../../prisma/bootstrap-admin") await bootstrapModule.bootstrapAdmin(prismaModule.prisma) await prismaModule.prisma.$disconnect() } catch (error) { await cleanup() throw error } const startedNextProcess = spawn( "bunx", ["next", "dev", "--webpack", "--hostname", host, "--port", port], { env, stdio: "inherit", }, ) nextProcess = startedNextProcess startedNextProcess.on("exit", async (code, signal) => { const wasShuttingDown = shuttingDown await cleanup() if (!wasShuttingDown && code !== 0) { console.error(`Next dev server exited with code ${code} signal ${signal}`) process.exit(code ?? 1) } }) } process.on("SIGTERM", async () => { await cleanup() process.exit(0) }) process.on("SIGINT", async () => { await cleanup() process.exit(0) }) main().catch(async (error) => { console.error(error) await cleanup() process.exit(1) })