import { execSync } from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; /** * Database fixture for E2E tests * Handles SQLite database setup, seeding, and cleanup */ export interface DatabaseConfig { dbPath: string; workflowId: string; } /** * Initialize and seed SQLite database for a test workflow */ export async function setupDatabase(config: DatabaseConfig): Promise { const { dbPath, workflowId } = config; // Ensure directory exists const dbDir = path.dirname(dbPath); if (!fs.existsSync(dbDir)) { fs.mkdirSync(dbDir, { recursive: true }); } // Create database file (SQLite creates it automatically on connection) fs.writeFileSync(dbPath, ''); // Run migrations (using backend migration scripts) // This assumes the backend has migration support via alembic or equivalent try { execSync(`cd backend && python3 -m alembic upgrade head`, { env: { ...process.env, DATABASE_URL: `sqlite:///${dbPath}`, }, }); } catch (error) { console.warn(`Database migration warning: ${error}`); // Continue even if migrations fail - tests can still work with empty schema } } /** * Seed database with test data */ export async function seedDatabase(config: DatabaseConfig, seedData: any): Promise { const { dbPath } = config; // For now, seed data is inserted via backend API calls during tests // This function is available for direct database operations if needed // Example: Direct SQL insertion for performance-critical scenarios // Verify database exists and is accessible if (!fs.existsSync(dbPath)) { throw new Error(`Database file not found: ${dbPath}`); } } /** * Clean up database after test completion */ export async function cleanupDatabase(config: DatabaseConfig): Promise { const { dbPath } = config; try { if (fs.existsSync(dbPath)) { fs.unlinkSync(dbPath); } } catch (error) { console.warn(`Database cleanup warning: ${error}`); } } /** * Reset database to clean state (truncate tables) */ export async function resetDatabase(config: DatabaseConfig): Promise { const { dbPath } = config; // Clear all tables without deleting the database // This is useful for test isolation within a single workflow try { if (fs.existsSync(dbPath)) { // Rerun migrations to reset schema execSync(`cd backend && python3 -m alembic downgrade base && python3 -m alembic upgrade head`, { env: { ...process.env, DATABASE_URL: `sqlite:///${dbPath}`, }, }); } } catch (error) { console.warn(`Database reset warning: ${error}`); } } /** * Get database path for a workflow instance */ export function getDatabasePath(workflowId: string, tempDir: string = '/tmp'): string { return path.join(tempDir, `test-${workflowId}.db`); } /** * Verify database connectivity */ export async function verifyDatabase(dbPath: string): Promise { try { return fs.existsSync(dbPath); } catch { return false; } } /** * Get database statistics (for debugging) */ export async function getDatabaseStats(dbPath: string): Promise<{ size: number; exists: boolean }> { try { const stats = fs.statSync(dbPath); return { size: stats.size, exists: true, }; } catch { return { size: 0, exists: false, }; } }