/** * Integration Test Setup * * Sets up test containers and real database instances for integration testing. * This file is executed before integration tests run. */ import { GenericContainer, StartedTestContainer } from 'testcontainers'; import { MongoMemoryServer } from 'mongodb-memory-server'; let questdbContainer: StartedTestContainer; let postgresContainer: StartedTestContainer; let mongoContainer: StartedTestContainer; let mongoMemoryServer: MongoMemoryServer; /** * Global setup for integration tests * Starts real database containers for testing */ beforeAll(async () => { console.log('๐Ÿš€ Starting integration test containers...'); try { // Start QuestDB container console.log('๐Ÿ“Š Starting QuestDB container...'); questdbContainer = await new GenericContainer('questdb/questdb:7.3.10') .withExposedPorts(9000, 8812, 9009) .withEnvironment({ 'QDB_TELEMETRY_ENABLED': 'false', 'QDB_LOG_LEVEL': 'ERROR' }) .withStartupTimeout(60000) .start(); // Start PostgreSQL container console.log('๐Ÿ˜ Starting PostgreSQL container...'); postgresContainer = await new GenericContainer('postgres:15-alpine') .withExposedPorts(5432) .withEnvironment({ 'POSTGRES_DB': 'trading_bot_test', 'POSTGRES_USER': 'trading_admin', 'POSTGRES_PASSWORD': 'trading_pass_test' }) .withStartupTimeout(60000) .start(); // Start MongoDB container console.log('๐Ÿƒ Starting MongoDB container...'); mongoContainer = await new GenericContainer('mongo:7-jammy') .withExposedPorts(27017) .withEnvironment({ 'MONGO_INITDB_ROOT_USERNAME': 'trading_admin', 'MONGO_INITDB_ROOT_PASSWORD': 'trading_mongo_test', 'MONGO_INITDB_DATABASE': 'trading_bot_test' }) .withStartupTimeout(60000) .start(); // Update environment variables for tests process.env.QUESTDB_HOST = questdbContainer.getHost(); process.env.QUESTDB_HTTP_PORT = questdbContainer.getMappedPort(9000).toString(); process.env.QUESTDB_PG_PORT = questdbContainer.getMappedPort(8812).toString(); process.env.QUESTDB_INFLUX_PORT = questdbContainer.getMappedPort(9009).toString(); process.env.POSTGRES_HOST = postgresContainer.getHost(); process.env.POSTGRES_PORT = postgresContainer.getMappedPort(5432).toString(); process.env.MONGODB_HOST = mongoContainer.getHost(); process.env.MONGODB_PORT = mongoContainer.getMappedPort(27017).toString(); console.log('โœ… All containers started successfully!'); console.log(`๐Ÿ“Š QuestDB: http://${process.env.QUESTDB_HOST}:${process.env.QUESTDB_HTTP_PORT}`); console.log(`๐Ÿ˜ PostgreSQL: ${process.env.POSTGRES_HOST}:${process.env.POSTGRES_PORT}`); console.log(`๐Ÿƒ MongoDB: ${process.env.MONGODB_HOST}:${process.env.MONGODB_PORT}`); } catch (error) { console.error('โŒ Failed to start test containers:', error); // Try to use MongoDB Memory Server as fallback console.log('๐Ÿ”„ Falling back to MongoDB Memory Server...'); try { mongoMemoryServer = await MongoMemoryServer.create({ instance: { dbName: 'trading_bot_test' } }); const mongoUri = mongoMemoryServer.getUri(); const mongoUrl = new URL(mongoUri); process.env.MONGODB_HOST = mongoUrl.hostname; process.env.MONGODB_PORT = mongoUrl.port; process.env.MONGODB_URI = mongoUri; console.log('โœ… MongoDB Memory Server started as fallback'); } catch (fallbackError) { console.error('โŒ Failed to start MongoDB Memory Server:', fallbackError); throw fallbackError; } // For other databases, use localhost defaults if containers fail if (!questdbContainer) { console.log('โš ๏ธ Using localhost QuestDB (ensure it\'s running)'); process.env.QUESTDB_HOST = 'localhost'; process.env.QUESTDB_HTTP_PORT = '9000'; process.env.QUESTDB_PG_PORT = '8812'; process.env.QUESTDB_INFLUX_PORT = '9009'; } if (!postgresContainer) { console.log('โš ๏ธ Using localhost PostgreSQL (ensure it\'s running)'); process.env.POSTGRES_HOST = 'localhost'; process.env.POSTGRES_PORT = '5432'; } } }, 120000); // 2 minutes timeout for container startup /** * Global cleanup for integration tests * Stops all test containers */ afterAll(async () => { console.log('๐Ÿงน Cleaning up integration test containers...'); const cleanup = async (container: StartedTestContainer | undefined, name: string) => { if (container) { try { await container.stop(); console.log(`โœ… ${name} container stopped`); } catch (error) { console.warn(`โš ๏ธ Failed to stop ${name} container:`, error); } } }; await Promise.all([ cleanup(questdbContainer, 'QuestDB'), cleanup(postgresContainer, 'PostgreSQL'), cleanup(mongoContainer, 'MongoDB') ]); if (mongoMemoryServer) { try { await mongoMemoryServer.stop(); console.log('โœ… MongoDB Memory Server stopped'); } catch (error) { console.warn('โš ๏ธ Failed to stop MongoDB Memory Server:', error); } } console.log('๐ŸŽ‰ Integration test cleanup complete!'); }, 30000); /** * Wait for database services to be ready */ export const waitForServices = async (timeout: number = 30000): Promise => { const start = Date.now(); while (Date.now() - start < timeout) { try { // Check if QuestDB HTTP interface is ready const questdbUrl = `http://${process.env.QUESTDB_HOST}:${process.env.QUESTDB_HTTP_PORT}/status`; const response = await fetch(questdbUrl); if (response.ok) { console.log('โœ… QuestDB is ready'); return; } } catch (error) { // Service not ready yet, continue waiting } await new Promise(resolve => setTimeout(resolve, 1000)); } throw new Error('Services did not become ready within timeout'); }; /** * Test utilities for integration tests */ export const integrationTestHelpers = { /** * Get QuestDB HTTP URL */ getQuestDBUrl: () => `http://${process.env.QUESTDB_HOST}:${process.env.QUESTDB_HTTP_PORT}`, /** * Get PostgreSQL connection string */ getPostgresUrl: () => `postgresql://${process.env.POSTGRES_USER}:${process.env.POSTGRES_PASSWORD}@${process.env.POSTGRES_HOST}:${process.env.POSTGRES_PORT}/${process.env.POSTGRES_DB}`, /** * Get MongoDB connection string */ getMongoUrl: () => { if (process.env.MONGODB_URI) { return process.env.MONGODB_URI; } return `mongodb://${process.env.MONGODB_USERNAME}:${process.env.MONGODB_PASSWORD}@${process.env.MONGODB_HOST}:${process.env.MONGODB_PORT}/${process.env.MONGODB_DATABASE}`; }, /** * Wait for services to be ready */ waitForServices };