import { tap, expect } from '@git.zone/tstest/tapbundle'; import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.js'; import { createSmtpClient } from '../../../ts/mail/delivery/smtpclient/index.js'; import { Email } from '../../../ts/mail/core/classes.email.js'; import * as net from 'net'; let testServer: ITestServer; tap.test('setup - start SMTP server for connection pool tests', async () => { testServer = await startTestServer({ port: 2583, tlsEnabled: false, authRequired: false }); expect(testServer.port).toEqual(2583); }); tap.test('CERR-09: Connection pool with concurrent sends', async () => { // Test basic connection pooling functionality const pooledClient = await createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, pool: true, maxConnections: 2, connectionTimeout: 5000 }); console.log('Testing connection pool with concurrent sends...'); // Send multiple messages concurrently const emails = [ new Email({ from: 'sender@example.com', to: 'recipient1@example.com', subject: 'Pool test 1', text: 'Testing connection pool' }), new Email({ from: 'sender@example.com', to: 'recipient2@example.com', subject: 'Pool test 2', text: 'Testing connection pool' }), new Email({ from: 'sender@example.com', to: 'recipient3@example.com', subject: 'Pool test 3', text: 'Testing connection pool' }) ]; const results = await Promise.all( emails.map(email => pooledClient.sendMail(email)) ); const successful = results.filter(r => r.success).length; console.log(`✅ Sent ${successful} messages using connection pool`); expect(successful).toBeGreaterThan(0); await pooledClient.close(); }); tap.test('CERR-09: Connection pool with server limit', async () => { // Create server that limits concurrent connections let activeConnections = 0; const maxServerConnections = 1; const limitedServer = net.createServer((socket) => { activeConnections++; if (activeConnections > maxServerConnections) { socket.write('421 4.7.0 Too many connections\r\n'); socket.end(); activeConnections--; return; } socket.write('220 Limited Server\r\n'); let buffer = ''; socket.on('data', (data) => { buffer += data.toString(); let lines = buffer.split('\r\n'); buffer = lines.pop() || ''; for (const line of lines) { const command = line.trim(); if (!command) continue; if (command.startsWith('EHLO')) { socket.write('250 OK\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } else { socket.write('250 OK\r\n'); } } }); socket.on('close', () => { activeConnections--; }); }); await new Promise((resolve) => { limitedServer.listen(2584, () => resolve()); }); const pooledClient = await createSmtpClient({ host: '127.0.0.1', port: 2584, secure: false, pool: true, maxConnections: 3, // Client wants 3 but server only allows 1 connectionTimeout: 5000 }); // Try concurrent connections const results = await Promise.all([ pooledClient.verify(), pooledClient.verify(), pooledClient.verify() ]); const successful = results.filter(r => r === true).length; console.log(`✅ ${successful} connections succeeded with server limit`); expect(successful).toBeGreaterThan(0); await pooledClient.close(); await new Promise((resolve) => { limitedServer.close(() => resolve()); }); }); tap.test('CERR-09: Connection pool recovery after error', async () => { // Create server that fails sometimes let requestCount = 0; const flakyServer = net.createServer((socket) => { requestCount++; // Fail every 3rd connection if (requestCount % 3 === 0) { socket.destroy(); return; } socket.write('220 Flaky Server\r\n'); let buffer = ''; let inData = false; socket.on('data', (data) => { buffer += data.toString(); let lines = buffer.split('\r\n'); buffer = lines.pop() || ''; for (const line of lines) { const command = line.trim(); if (!command) continue; if (inData) { if (command === '.') { inData = false; socket.write('250 OK\r\n'); } continue; } if (command.startsWith('EHLO')) { socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Send data\r\n'); inData = true; } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } } }); }); await new Promise((resolve) => { flakyServer.listen(2585, () => resolve()); }); const pooledClient = await createSmtpClient({ host: '127.0.0.1', port: 2585, secure: false, pool: true, maxConnections: 2, connectionTimeout: 5000 }); // Send multiple messages to test recovery const results = []; for (let i = 0; i < 5; i++) { const email = new Email({ from: 'sender@example.com', to: 'recipient@example.com', subject: `Recovery test ${i}`, text: 'Testing pool recovery' }); const result = await pooledClient.sendMail(email); results.push(result.success); console.log(`Message ${i}: ${result.success ? 'Success' : 'Failed'}`); } const successful = results.filter(r => r === true).length; console.log(`✅ Pool recovered from errors: ${successful}/5 succeeded`); expect(successful).toBeGreaterThan(2); await pooledClient.close(); await new Promise((resolve) => { flakyServer.close(() => resolve()); }); }); tap.test('CERR-09: Connection pool timeout handling', async () => { // Create very slow server const slowServer = net.createServer((socket) => { // Wait 2 seconds before sending greeting setTimeout(() => { socket.write('220 Very Slow Server\r\n'); }, 2000); socket.on('data', () => { // Don't respond to any commands }); }); await new Promise((resolve) => { slowServer.listen(2586, () => resolve()); }); const pooledClient = await createSmtpClient({ host: '127.0.0.1', port: 2586, secure: false, pool: true, connectionTimeout: 1000 // 1 second timeout }); const result = await pooledClient.verify(); expect(result).toBeFalse(); console.log('✅ Connection pool handled timeout correctly'); await pooledClient.close(); await new Promise((resolve) => { slowServer.close(() => resolve()); }); }); tap.test('CERR-09: Normal pooled operation', async () => { // Test successful pooled operation const pooledClient = await createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, pool: true, maxConnections: 2 }); const email = new Email({ from: 'sender@example.com', to: 'recipient@example.com', subject: 'Pool Test', text: 'Testing normal pooled operation' }); const result = await pooledClient.sendMail(email); expect(result.success).toBeTrue(); console.log('✅ Normal pooled email sent successfully'); await pooledClient.close(); }); tap.test('cleanup - stop SMTP server', async () => { await stopTestServer(testServer); }); export default tap.start();