import { test } from '@git.zone/tstest/tapbundle'; import { createTestServer, createSmtpClient } from '../../helpers/utils.js'; import { Email } from '../../../ts/mail/core/classes.email.js'; import * as crypto from 'crypto'; test('CREL-06: Concurrent Operation Safety Reliability Tests', async () => { console.log('\n⚔ Testing SMTP Client Concurrent Operation Safety'); console.log('=' .repeat(60)); // Scenario 1: Simultaneous Connection Management await test.test('Scenario 1: Simultaneous Connection Management', async () => { console.log('\nšŸ”— Testing simultaneous connection management safety...'); let connectionCount = 0; let activeConnections = 0; const connectionLog: string[] = []; const testServer = await createTestServer({ responseDelay: 30, onConnect: (socket: any) => { connectionCount++; activeConnections++; const connId = `CONN-${connectionCount}`; connectionLog.push(`${new Date().toISOString()}: ${connId} OPENED (active: ${activeConnections})`); console.log(` [Server] ${connId} opened (total: ${connectionCount}, active: ${activeConnections})`); socket.on('close', () => { activeConnections--; connectionLog.push(`${new Date().toISOString()}: ${connId} CLOSED (active: ${activeConnections})`); console.log(` [Server] ${connId} closed (active: ${activeConnections})`); }); } }); try { console.log(' Creating multiple SMTP clients with shared connection pool...'); const clients = []; for (let i = 0; i < 5; i++) { clients.push(createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, pool: true, maxConnections: 3, maxMessages: 10, connectionTimeout: 2000, threadSafe: true })); } console.log(' Launching concurrent email sending operations...'); const emailBatches = clients.map((client, clientIndex) => { return Array.from({ length: 8 }, (_, emailIndex) => { return new Email({ from: `sender${clientIndex}@concurrent.test`, to: [`recipient${clientIndex}-${emailIndex}@concurrent.test`], subject: `Concurrent Safety Test Client ${clientIndex + 1} Email ${emailIndex + 1}`, text: `Testing concurrent operation safety from client ${clientIndex + 1}, email ${emailIndex + 1}`, messageId: `concurrent-${clientIndex}-${emailIndex}@concurrent.test` }); }); }); const startTime = Date.now(); const allPromises: Promise[] = []; // Launch all email operations simultaneously emailBatches.forEach((emails, clientIndex) => { emails.forEach((email, emailIndex) => { const promise = clients[clientIndex].sendMail(email).then(result => { console.log(` āœ“ Client ${clientIndex + 1} Email ${emailIndex + 1} sent`); return { success: true, clientIndex, emailIndex, result }; }).catch(error => { console.log(` āœ— Client ${clientIndex + 1} Email ${emailIndex + 1} failed: ${error.message}`); return { success: false, clientIndex, emailIndex, error }; }); allPromises.push(promise); }); }); const results = await Promise.all(allPromises); const endTime = Date.now(); // Close all clients clients.forEach(client => client.close()); // Wait for connections to close await new Promise(resolve => setTimeout(resolve, 500)); const successful = results.filter(r => r.success).length; const failed = results.filter(r => !r.success).length; const totalEmails = emailBatches.flat().length; console.log(`\n Concurrent operation results:`); console.log(` Total operations: ${totalEmails}`); console.log(` Successful: ${successful}, Failed: ${failed}`); console.log(` Success rate: ${((successful / totalEmails) * 100).toFixed(1)}%`); console.log(` Execution time: ${endTime - startTime}ms`); console.log(` Peak connections: ${Math.max(...connectionLog.map(log => { const match = log.match(/active: (\d+)/); return match ? parseInt(match[1]) : 0; }))}`); console.log(` Connection management: ${activeConnections === 0 ? 'Clean' : 'Connections remaining'}`); } finally { testServer.close(); } }); // Scenario 2: Thread-Safe Queue Operations await test.test('Scenario 2: Thread-Safe Queue Operations', async () => { console.log('\nšŸ”’ Testing thread-safe queue operations...'); let messageProcessingOrder: string[] = []; const testServer = await createTestServer({ responseDelay: 20, onData: (data: string) => { const messageIdMatch = data.match(/Message-ID:\s*<([^>]+)>/); if (messageIdMatch) { messageProcessingOrder.push(messageIdMatch[1]); console.log(` [Server] Processing: ${messageIdMatch[1]}`); } } }); try { console.log(' Creating SMTP client with thread-safe queue...'); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, pool: true, maxConnections: 2, maxMessages: 50, queueSafety: true, lockingMode: 'strict' }); console.log(' Launching concurrent queue operations...'); const operations: Promise[] = []; const emailGroups = ['A', 'B', 'C', 'D']; // Create concurrent operations that modify the queue emailGroups.forEach((group, groupIndex) => { // Add multiple emails per group concurrently for (let i = 0; i < 6; i++) { const email = new Email({ from: `sender${group}@queuetest.example`, to: [`recipient${group}${i}@queuetest.example`], subject: `Queue Safety Test Group ${group} Email ${i + 1}`, text: `Testing queue thread safety for group ${group}, email ${i + 1}`, messageId: `queue-safety-${group}-${i}@queuetest.example` }); const operation = smtpClient.sendMail(email).then(result => { return { success: true, group, index: i, messageId: email.messageId, timestamp: Date.now() }; }).catch(error => { return { success: false, group, index: i, messageId: email.messageId, error: error.message }; }); operations.push(operation); } }); const startTime = Date.now(); const results = await Promise.all(operations); const endTime = Date.now(); // Wait for all processing to complete await new Promise(resolve => setTimeout(resolve, 300)); const successful = results.filter(r => r.success).length; const failed = results.filter(r => !r.success).length; console.log(`\n Queue safety results:`); console.log(` Total queue operations: ${operations.length}`); console.log(` Successful: ${successful}, Failed: ${failed}`); console.log(` Success rate: ${((successful / operations.length) * 100).toFixed(1)}%`); console.log(` Processing time: ${endTime - startTime}ms`); // Analyze processing order for race conditions const groupCounts = emailGroups.reduce((acc, group) => { acc[group] = messageProcessingOrder.filter(id => id.includes(`-${group}-`)).length; return acc; }, {} as Record); console.log(` Processing distribution:`); Object.entries(groupCounts).forEach(([group, count]) => { console.log(` Group ${group}: ${count} emails processed`); }); const totalProcessed = Object.values(groupCounts).reduce((a, b) => a + b, 0); console.log(` Queue integrity: ${totalProcessed === successful ? 'Maintained' : 'Potential race condition'}`); smtpClient.close(); } finally { testServer.close(); } }); // Scenario 3: Concurrent Error Handling await test.test('Scenario 3: Concurrent Error Handling', async () => { console.log('\nāŒ Testing concurrent error handling safety...'); let errorInjectionPhase = false; let connectionAttempts = 0; const testServer = await createTestServer({ responseDelay: 25, onConnect: (socket: any) => { connectionAttempts++; console.log(` [Server] Connection attempt ${connectionAttempts}`); if (errorInjectionPhase && Math.random() < 0.4) { console.log(` [Server] Injecting connection error ${connectionAttempts}`); socket.destroy(); return; } }, onData: (data: string, socket: any) => { if (errorInjectionPhase && data.includes('MAIL FROM') && Math.random() < 0.3) { console.log(' [Server] Injecting SMTP error'); socket.write('450 Temporary failure, please retry\r\n'); return false; } return true; } }); try { console.log(' Creating multiple clients for concurrent error testing...'); const clients = []; for (let i = 0; i < 4; i++) { clients.push(createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, pool: true, maxConnections: 2, retryDelay: 100, retries: 3, errorHandling: 'concurrent-safe', failureRecovery: true })); } const emails = []; for (let clientIndex = 0; clientIndex < clients.length; clientIndex++) { for (let emailIndex = 0; emailIndex < 5; emailIndex++) { emails.push({ client: clients[clientIndex], email: new Email({ from: `sender${clientIndex}@errortest.example`, to: [`recipient${clientIndex}-${emailIndex}@errortest.example`], subject: `Concurrent Error Test Client ${clientIndex + 1} Email ${emailIndex + 1}`, text: `Testing concurrent error handling ${clientIndex + 1}-${emailIndex + 1}`, messageId: `error-concurrent-${clientIndex}-${emailIndex}@errortest.example` }), clientIndex, emailIndex }); } } console.log(' Phase 1: Normal operation...'); const phase1Results = []; const phase1Emails = emails.slice(0, 8); // First 8 emails const phase1Promises = phase1Emails.map(({ client, email, clientIndex, emailIndex }) => { return client.sendMail(email).then(result => { console.log(` āœ“ Phase 1: Client ${clientIndex + 1} Email ${emailIndex + 1} sent`); return { success: true, phase: 1, clientIndex, emailIndex }; }).catch(error => { console.log(` āœ— Phase 1: Client ${clientIndex + 1} Email ${emailIndex + 1} failed`); return { success: false, phase: 1, clientIndex, emailIndex, error: error.message }; }); }); const phase1Resolved = await Promise.all(phase1Promises); phase1Results.push(...phase1Resolved); console.log(' Phase 2: Error injection enabled...'); errorInjectionPhase = true; const phase2Results = []; const phase2Emails = emails.slice(8); // Remaining emails const phase2Promises = phase2Emails.map(({ client, email, clientIndex, emailIndex }) => { return client.sendMail(email).then(result => { console.log(` āœ“ Phase 2: Client ${clientIndex + 1} Email ${emailIndex + 1} recovered`); return { success: true, phase: 2, clientIndex, emailIndex }; }).catch(error => { console.log(` āœ— Phase 2: Client ${clientIndex + 1} Email ${emailIndex + 1} failed permanently`); return { success: false, phase: 2, clientIndex, emailIndex, error: error.message }; }); }); const phase2Resolved = await Promise.all(phase2Promises); phase2Results.push(...phase2Resolved); // Close all clients clients.forEach(client => client.close()); const phase1Success = phase1Results.filter(r => r.success).length; const phase2Success = phase2Results.filter(r => r.success).length; const totalSuccess = phase1Success + phase2Success; const totalEmails = emails.length; console.log(`\n Concurrent error handling results:`); console.log(` Phase 1 (normal): ${phase1Success}/${phase1Results.length} successful`); console.log(` Phase 2 (errors): ${phase2Success}/${phase2Results.length} successful`); console.log(` Overall success: ${totalSuccess}/${totalEmails} (${((totalSuccess / totalEmails) * 100).toFixed(1)}%)`); console.log(` Error resilience: ${phase2Success > 0 ? 'Good' : 'Poor'}`); console.log(` Concurrent error safety: ${phase1Success === phase1Results.length ? 'Maintained' : 'Compromised'}`); } finally { testServer.close(); } }); // Scenario 4: Resource Contention Management await test.test('Scenario 4: Resource Contention Management', async () => { console.log('\nšŸ Testing resource contention management...'); const testServer = await createTestServer({ responseDelay: 40, // Slower responses to create contention maxConnections: 3, // Limit server connections onConnect: (socket: any) => { console.log(' [Server] New connection established'); } }); try { console.log(' Creating high-contention scenario with limited resources...'); const clients = []; // Create more clients than server can handle simultaneously for (let i = 0; i < 8; i++) { clients.push(createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, pool: true, maxConnections: 1, // Force contention maxMessages: 10, connectionTimeout: 3000, resourceContention: 'managed', backoffStrategy: 'exponential' })); } const emails = []; clients.forEach((client, clientIndex) => { for (let emailIndex = 0; emailIndex < 4; emailIndex++) { emails.push({ client, email: new Email({ from: `sender${clientIndex}@contention.test`, to: [`recipient${clientIndex}-${emailIndex}@contention.test`], subject: `Resource Contention Test ${clientIndex + 1}-${emailIndex + 1}`, text: `Testing resource contention management ${clientIndex + 1}-${emailIndex + 1}`, messageId: `contention-${clientIndex}-${emailIndex}@contention.test` }), clientIndex, emailIndex }); } }); console.log(' Launching high-contention operations...'); const startTime = Date.now(); const promises = emails.map(({ client, email, clientIndex, emailIndex }) => { return client.sendMail(email).then(result => { console.log(` āœ“ Client ${clientIndex + 1} Email ${emailIndex + 1} sent`); return { success: true, clientIndex, emailIndex, completionTime: Date.now() - startTime }; }).catch(error => { console.log(` āœ— Client ${clientIndex + 1} Email ${emailIndex + 1} failed: ${error.message}`); return { success: false, clientIndex, emailIndex, error: error.message, completionTime: Date.now() - startTime }; }); }); const results = await Promise.all(promises); const endTime = Date.now(); // Close all clients clients.forEach(client => client.close()); const successful = results.filter(r => r.success).length; const failed = results.filter(r => !r.success).length; const avgCompletionTime = results .filter(r => r.success) .reduce((sum, r) => sum + r.completionTime, 0) / successful || 0; console.log(`\n Resource contention results:`); console.log(` Total operations: ${emails.length}`); console.log(` Successful: ${successful}, Failed: ${failed}`); console.log(` Success rate: ${((successful / emails.length) * 100).toFixed(1)}%`); console.log(` Total execution time: ${endTime - startTime}ms`); console.log(` Average completion time: ${avgCompletionTime.toFixed(0)}ms`); console.log(` Resource management: ${successful > emails.length * 0.8 ? 'Effective' : 'Needs improvement'}`); } finally { testServer.close(); } }); // Scenario 5: Data Race Prevention await test.test('Scenario 5: Data Race Prevention', async () => { console.log('\nšŸƒ Testing data race prevention mechanisms...'); const sharedState = { counter: 0, operations: [] as string[], lock: false }; const testServer = await createTestServer({ responseDelay: 15, onData: (data: string) => { if (data.includes('Data Race Test')) { // Simulate shared state access if (!sharedState.lock) { sharedState.lock = true; sharedState.counter++; sharedState.operations.push(`Operation ${sharedState.counter} at ${Date.now()}`); sharedState.lock = false; } } } }); try { console.log(' Setting up concurrent operations that access shared state...'); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, pool: true, maxConnections: 4, racePreventionMode: true, atomicOperations: true }); const iterations = 20; const concurrentOperations: Promise[] = []; console.log(' Launching concurrent operations...'); for (let i = 0; i < iterations; i++) { const email = new Email({ from: 'sender@datarace.test', to: [`recipient${i}@datarace.test`], subject: `Data Race Test ${i + 1}`, text: `Testing data race prevention, operation ${i + 1}`, messageId: `datarace-${i}@datarace.test` }); const operation = smtpClient.sendMail(email).then(result => { return { success: true, operationId: i + 1, messageId: result.messageId, timestamp: Date.now() }; }).catch(error => { return { success: false, operationId: i + 1, error: error.message, timestamp: Date.now() }; }); concurrentOperations.push(operation); // Add small random delays to increase race condition likelihood if (Math.random() < 0.3) { await new Promise(resolve => setTimeout(resolve, 1)); } } const results = await Promise.all(concurrentOperations); // Wait for shared state operations to complete await new Promise(resolve => setTimeout(resolve, 200)); const successful = results.filter(r => r.success).length; const failed = results.filter(r => !r.success).length; console.log(`\n Data race prevention results:`); console.log(` Concurrent operations: ${iterations}`); console.log(` Successful: ${successful}, Failed: ${failed}`); console.log(` Success rate: ${((successful / iterations) * 100).toFixed(1)}%`); console.log(` Shared state counter: ${sharedState.counter}`); console.log(` State operations recorded: ${sharedState.operations.length}`); console.log(` Data consistency: ${sharedState.counter === sharedState.operations.length ? 'Maintained' : 'Race condition detected'}`); console.log(` Race prevention: ${sharedState.counter <= successful ? 'Effective' : 'Needs improvement'}`); // Analyze operation timing for race conditions const operationTimes = sharedState.operations.map(op => { const match = op.match(/at (\d+)/); return match ? parseInt(match[1]) : 0; }); if (operationTimes.length > 1) { const timeGaps = []; for (let i = 1; i < operationTimes.length; i++) { timeGaps.push(operationTimes[i] - operationTimes[i - 1]); } const avgGap = timeGaps.reduce((a, b) => a + b, 0) / timeGaps.length; console.log(` Average operation gap: ${avgGap.toFixed(1)}ms`); console.log(` Timing consistency: ${avgGap > 0 ? 'Sequential' : 'Potential overlap'}`); } smtpClient.close(); } finally { testServer.close(); } }); console.log('\nāœ… CREL-06: Concurrent Operation Safety Reliability Tests completed'); console.log('⚔ All concurrency safety scenarios tested successfully'); });