import { exec, ChildProcess } from 'child_process'; import { setup as globalSetup } from './global-setup'; import { pingBackend } from '../../services/apiClient'; import { logger } from '../../services/logger'; import { getPool } from '../../services/db/connection'; // Import getPool let serverProcess: ChildProcess; export async function setup() { console.log('\n--- Running Integration Test Setup ---'); await globalSetup(); console.log('Starting backend server for integration tests...'); // Use the dedicated test server script, which correctly uses the .env.test file. serverProcess = exec('npm run start:test'); // --- NEW LOGGING START --- // Listen for premature exit to debug crashes immediately serverProcess.on('exit', (code, signal) => { if (code !== null && code !== 0) { console.error(`\n[SERVER EXIT]: Backend server process exited prematurely with code ${code} and signal ${signal}`); console.error('Check [SERVER STDERR] logs above for details.\n'); } }); // --- NEW LOGGING END --- serverProcess.stdout?.on('data', (data) => { console.log(`[SERVER STDOUT]: ${data}`); }); serverProcess.stderr?.on('data', (data) => { console.error(`[SERVER STDERR]: ${data}`); }); const maxRetries = 10; const retryDelay = 1000; for (let i = 0; i < maxRetries; i++) { try { // If the process has already died, stop waiting if (serverProcess.exitCode !== null) { throw new Error(`Server process exited with code ${serverProcess.exitCode}`); } if (await pingBackend()) { console.log('✅ Backend server is running and responsive.'); return; } } catch (e) { // Only log debug if we are still waiting, otherwise throw if (serverProcess.exitCode !== null) throw e; logger.debug('Ping failed while waiting for server, this is expected.', { error: e }); } console.log(`Waiting for backend server... (attempt ${i + 1}/${maxRetries})`); await new Promise(resolve => setTimeout(resolve, retryDelay)); } console.error('🔴 Backend server did not start in time. Integration tests will likely fail.'); serverProcess.kill(); throw new Error('Backend server failed to start.'); } export async function teardown() { console.log('--- Integration Test Teardown ---'); // Close the database connection pool after all integration tests have run. // This is the correct place to ensure all connections are closed gracefully. const pool = getPool(); if (serverProcess) { serverProcess.kill(); console.log('✅ Backend server process terminated.'); } if (pool) { // Check if the pool has any clients (total, idle, or waiting) before ending it. // This is a safer, type-approved way to see if the pool was used, avoiding `any`. if (pool.totalCount > 0 || pool.idleCount > 0 || pool.waitingCount > 0) { await pool.end(); } console.log('✅ Global database pool teardown complete.'); } }