import { Pool } from 'pg'; import { OperatorService } from '../../src/gateway/auth/operator-service'; import { OperatorRole } from '../../src/gateway/auth/types'; import { JWTService } from '../../src/gateway/auth/jwt'; import type { PaymentRequest } from '../../src/gateway/validation/payment-validation'; import { PaymentType, Currency } from '../../src/models/payment'; /** * Test utilities and helpers */ export class TestHelpers { private static testDbPool: Pool | null = null; /** * Get test database connection */ static getTestDb(): Pool { if (!this.testDbPool) { this.testDbPool = new Pool({ connectionString: process.env.TEST_DATABASE_URL || 'postgresql://postgres:postgres@localhost:5432/dbis_core_test', }); } return this.testDbPool; } /** * Clean test database * Fast cleanup with timeout protection */ static async cleanDatabase(): Promise { const pool = this.getTestDb(); let client; try { client = await pool.connect(); // Set statement timeout to prevent hanging (5 seconds) await client.query('SET statement_timeout = 5000'); // Fast cleanup - just delete test operators, skip truncate to save time // Tests will handle their own data cleanup await client.query('DELETE FROM operators WHERE operator_id LIKE $1 OR operator_id LIKE $2', ['E2E_%', 'TEST_%']); } catch (error: any) { // Ignore cleanup errors - tests can continue // Don't log warnings in test environment to reduce noise } finally { if (client) { try { client.release(); } catch (releaseError: any) { // Ignore release errors } } } } /** * Create test operator * Handles duplicate key errors by returning existing operator * Has timeout protection to prevent hanging */ static async createTestOperator( operatorId: string, role: OperatorRole, password: string = 'Test123!@#' ) { const pool = this.getTestDb(); // First, try to get existing operator (faster) try { const result = await pool.query( 'SELECT * FROM operators WHERE operator_id = $1', [operatorId] ); if (result.rows.length > 0) { return result.rows[0]; } } catch (error: any) { // Ignore query errors, continue to create } // If not found, create new operator try { return await OperatorService.createOperator( operatorId, `Test ${role}`, password, role, `${operatorId.toLowerCase()}@test.com`, true // Skip password policy for tests ); } catch (error: any) { // If operator already exists (race condition), try to get it again if (error.message?.includes('duplicate key') || error.message?.includes('already exists')) { const result = await pool.query( 'SELECT * FROM operators WHERE operator_id = $1', [operatorId] ); if (result.rows.length > 0) { return result.rows[0]; } } throw error; } } /** * Generate test JWT token */ static generateTestToken(operatorId: string, id: string, role: OperatorRole): string { return JWTService.generateToken({ operatorId, id, role, }); } /** * Create test payment request */ static createTestPaymentRequest(): PaymentRequest { return { type: PaymentType.CUSTOMER_CREDIT_TRANSFER, amount: 1000.00, currency: Currency.USD, senderAccount: 'ACC001', senderBIC: 'TESTBIC1', receiverAccount: 'ACC002', receiverBIC: 'TESTBIC2', beneficiaryName: 'Test Beneficiary', purpose: 'Test payment', }; } /** * Sleep utility for tests */ static sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } }