// Integration Test Setup const mongoose = require('mongoose'); const { MongoMemoryServer } = require('mongodb-memory-server'); const Redis = require('ioredis-mock'); const amqp = require('amqplib'); const { Pool } = require('pg'); const { Client } = require('@elastic/elasticsearch'); const { v4: uuidv4 } = require('uuid'); class TestEnvironment { constructor() { this.mongoServer = null; this.redisClient = null; this.rabbitConnection = null; this.pgPool = null; this.esClient = null; this.servers = new Map(); } async setup() { console.log('Setting up test environment...'); // Setup MongoDB this.mongoServer = await MongoMemoryServer.create(); const mongoUri = this.mongoServer.getUri(); await mongoose.connect(mongoUri); // Setup Redis Mock this.redisClient = new Redis({ data: {} }); // Setup PostgreSQL (using test database) this.pgPool = new Pool({ host: process.env.TEST_POSTGRES_HOST || 'localhost', port: process.env.TEST_POSTGRES_PORT || 5432, user: process.env.TEST_POSTGRES_USER || 'test_user', password: process.env.TEST_POSTGRES_PASSWORD || 'test_pass', database: process.env.TEST_POSTGRES_DB || 'marketing_test' }); // Create test database schema await this.setupPostgresSchema(); // Setup RabbitMQ connection if (process.env.TEST_RABBITMQ_URL) { try { this.rabbitConnection = await amqp.connect(process.env.TEST_RABBITMQ_URL); this.rabbitChannel = await this.rabbitConnection.createChannel(); } catch (error) { console.warn('RabbitMQ not available for tests:', error.message); } } // Setup Elasticsearch Mock this.esClient = { index: jest.fn().mockResolvedValue({ body: { _id: uuidv4() } }), search: jest.fn().mockResolvedValue({ body: { hits: { hits: [] } } }), bulk: jest.fn().mockResolvedValue({ body: { errors: false } }), ping: jest.fn().mockResolvedValue(true) }; // Set environment variables for services process.env.MONGODB_URI = mongoUri; process.env.REDIS_HOST = 'localhost'; process.env.NODE_ENV = 'test'; process.env.JWT_SECRET = 'test-jwt-secret-key-for-testing-only'; process.env.ENCRYPTION_KEY = 'test-encryption-key-32-chars-long'; console.log('Test environment setup complete'); } async setupPostgresSchema() { const schemas = [ // Users table `CREATE TABLE IF NOT EXISTS users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), username VARCHAR(255) UNIQUE NOT NULL, email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP )`, // API Keys table `CREATE TABLE IF NOT EXISTS api_keys ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id), key_hash VARCHAR(255) UNIQUE NOT NULL, name VARCHAR(255), permissions JSONB DEFAULT '[]', last_used_at TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, expires_at TIMESTAMP )`, // Telegram Accounts table `CREATE TABLE IF NOT EXISTS telegram_accounts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), phone_number VARCHAR(20) UNIQUE NOT NULL, api_id VARCHAR(255), api_hash VARCHAR(255), session_data TEXT, is_active BOOLEAN DEFAULT true, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP )` ]; for (const schema of schemas) { await this.pgPool.query(schema); } } async startService(name, app, port) { return new Promise((resolve) => { const server = app.listen(port, () => { console.log(`Test ${name} service started on port ${port}`); this.servers.set(name, server); resolve(server); }); }); } async cleanup() { console.log('Cleaning up test environment...'); // Close all servers for (const [name, server] of this.servers) { await new Promise((resolve) => { server.close(resolve); }); console.log(`Closed ${name} server`); } // Cleanup databases if (mongoose.connection.readyState === 1) { await mongoose.disconnect(); } if (this.mongoServer) { await this.mongoServer.stop(); } if (this.pgPool) { await this.pgPool.end(); } if (this.rabbitConnection) { await this.rabbitChannel.close(); await this.rabbitConnection.close(); } if (this.redisClient) { this.redisClient.disconnect(); } console.log('Test environment cleanup complete'); } // Helper methods for tests async createTestUser(userData = {}) { const defaultData = { username: `testuser_${Date.now()}`, email: `test_${Date.now()}@example.com`, password: 'TestPassword123!' }; const user = { ...defaultData, ...userData }; // Hash password const bcrypt = require('bcryptjs'); const passwordHash = await bcrypt.hash(user.password, 10); // Insert into database const result = await this.pgPool.query( 'INSERT INTO users (username, email, password_hash) VALUES ($1, $2, $3) RETURNING *', [user.username, user.email, passwordHash] ); return { ...result.rows[0], password: user.password // Return plain password for testing }; } async createTestApiKey(userId) { const crypto = require('crypto'); const apiKey = `test_${crypto.randomBytes(16).toString('hex')}`; const keyHash = crypto.createHash('sha256').update(apiKey).digest('hex'); const result = await this.pgPool.query( 'INSERT INTO api_keys (user_id, key_hash, name, permissions) VALUES ($1, $2, $3, $4) RETURNING *', [userId, keyHash, 'Test API Key', JSON.stringify(['read', 'write'])] ); return { ...result.rows[0], apiKey // Return plain key for testing }; } async createTestTelegramAccount(data = {}) { const defaultData = { phone_number: `+1234567${Date.now().toString().slice(-4)}`, api_id: '12345', api_hash: 'test_api_hash', session_data: 'test_session_data' }; const account = { ...defaultData, ...data }; const result = await this.pgPool.query( 'INSERT INTO telegram_accounts (phone_number, api_id, api_hash, session_data) VALUES ($1, $2, $3, $4) RETURNING *', [account.phone_number, account.api_id, account.api_hash, account.session_data] ); return result.rows[0]; } getRedisClient() { return this.redisClient; } getMongoConnection() { return mongoose.connection; } getPostgresPool() { return this.pgPool; } getRabbitChannel() { return this.rabbitChannel; } getElasticsearchClient() { return this.esClient; } } module.exports = TestEnvironment;