import { spawn } from 'child_process'; import path from 'path'; import fs from 'fs'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const ROOT = path.resolve(__dirname, '..', '..'); const SERVER_DIR = path.join(ROOT, 'server'); const WEB_DIR = path.join(ROOT, 'web'); const PID_FILE = path.join(__dirname, '.server-pids.json'); async function waitForServer(url: string, label: string, timeoutSec = 45) { const start = Date.now(); while (Date.now() - start < timeoutSec * 1000) { try { const res = await fetch(url); console.log(`[setup] ${label} probe: status ${res.status}`); return; } catch {} await new Promise(r => setTimeout(r, 1000)); } throw new Error(`[setup] ${label} at ${url} failed to start within ${timeoutSec}s`); } export default async function globalSetup() { const env = { ...process.env, PORT: '3002', AUTH_PASSWORD: 'test123', ADMIN_TEST_TOKEN: 'true', // 隔离 e2e 测试库,避免污染真实评审数据 DB_PATH: path.join(SERVER_DIR, 'data', 'e2e-test.db'), // 测试模式下 writeEnvVar 只更新进程内 env,避免改密用例污染真实 server/.env NODE_ENV: 'test', } as Record; const shell = process.platform === 'win32' ? { shell: 'cmd.exe' } : {}; console.log('[setup] Starting backend server...'); const server = spawn('npx.cmd', ['tsx', 'src/index.ts'], { cwd: SERVER_DIR, stdio: ['ignore', 'pipe', 'pipe'], env, ...shell, }); server.stdout.on('data', (d: Buffer) => process.stdout.write(`[server] ${d}`)); server.stderr.on('data', (d: Buffer) => process.stderr.write(`[server-err] ${d}`)); server.on('error', (e: Error) => console.error('[setup] server spawn error:', e.message)); console.log('[setup] Starting frontend...'); const frontend = spawn('npx.cmd', ['vite', '--port', '14001', '--strictPort'], { cwd: WEB_DIR, stdio: ['ignore', 'pipe', 'pipe'], env: { ...process.env } as Record, ...shell, }); frontend.stdout.on('data', (d: Buffer) => process.stdout.write(`[web] ${d}`)); frontend.stderr.on('data', (d: Buffer) => process.stderr.write(`[web-err] ${d}`)); frontend.on('error', (e: Error) => console.error('[setup] frontend spawn error:', e.message)); fs.writeFileSync(PID_FILE, JSON.stringify({ serverPid: server.pid, frontendPid: frontend.pid, })); try { await waitForServer('http://localhost:3002/api/health', 'backend'); await waitForServer('http://localhost:14001', 'frontend'); console.log('[setup] Both servers ready'); } catch (e) { console.error('[setup] Failed:', (e as Error).message); server.kill(); frontend.kill(); throw e; } }