import { tap, expect } from '@git.zone/tstest/tapbundle'; import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.js'; import { createSmtpClient, createPooledSmtpClient } from '../../../ts/mail/delivery/smtpclient/index.js'; import type { SmtpClient } from '../../../ts/mail/delivery/smtpclient/smtp-client.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 throughput tests', async () => { testServer = await startTestServer({ port: 0, enableStarttls: false, authRequired: false }); expect(testServer.port).toBeGreaterThan(0); }); tap.test('CPERF-02: Sequential message throughput', async (tools) => { tools.timeout(60000); const smtpClient = await createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, debug: false }); const messageCount = 10; const messages = Array(messageCount).fill(null).map((_, i) => new Email({ from: 'sender@example.com', to: [`recipient${i + 1}@example.com`], subject: `Sequential throughput test ${i + 1}`, text: `Testing sequential message sending - message ${i + 1}` }) ); console.log(`Sending ${messageCount} messages sequentially...`); const sequentialStart = Date.now(); let successCount = 0; for (const message of messages) { try { const result = await smtpClient.sendMail(message); if (result.success) successCount++; } catch (error) { console.log('Failed to send:', error.message); } } const sequentialTime = Date.now() - sequentialStart; const sequentialRate = (successCount / sequentialTime) * 1000; console.log(`Sequential throughput: ${sequentialRate.toFixed(2)} messages/second`); console.log(`Successfully sent: ${successCount}/${messageCount} messages`); console.log(`Total time: ${sequentialTime}ms`); expect(successCount).toBeGreaterThan(0); expect(sequentialRate).toBeGreaterThan(0.1); // At least 0.1 message per second await smtpClient.close(); }); tap.test('CPERF-02: Concurrent message throughput', async (tools) => { tools.timeout(60000); const smtpClient = await createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, debug: false }); const messageCount = 10; const messages = Array(messageCount).fill(null).map((_, i) => new Email({ from: 'sender@example.com', to: [`recipient${i + 1}@example.com`], subject: `Concurrent throughput test ${i + 1}`, text: `Testing concurrent message sending - message ${i + 1}` }) ); console.log(`Sending ${messageCount} messages concurrently...`); const concurrentStart = Date.now(); // Send in small batches to avoid overwhelming const batchSize = 3; const results = []; for (let i = 0; i < messages.length; i += batchSize) { const batch = messages.slice(i, i + batchSize); const batchResults = await Promise.all( batch.map(message => smtpClient.sendMail(message).catch(err => ({ success: false, error: err }))) ); results.push(...batchResults); // Small delay between batches if (i + batchSize < messages.length) { await new Promise(resolve => setTimeout(resolve, 100)); } } const successCount = results.filter(r => r.success).length; const concurrentTime = Date.now() - concurrentStart; const concurrentRate = (successCount / concurrentTime) * 1000; console.log(`Concurrent throughput: ${concurrentRate.toFixed(2)} messages/second`); console.log(`Successfully sent: ${successCount}/${messageCount} messages`); console.log(`Total time: ${concurrentTime}ms`); expect(successCount).toBeGreaterThan(0); expect(concurrentRate).toBeGreaterThan(0.1); await smtpClient.close(); }); tap.test('CPERF-02: Connection pooling throughput', async (tools) => { tools.timeout(60000); const pooledClient = await createPooledSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, maxConnections: 3, debug: false }); const messageCount = 15; const messages = Array(messageCount).fill(null).map((_, i) => new Email({ from: 'sender@example.com', to: [`recipient${i + 1}@example.com`], subject: `Pooled throughput test ${i + 1}`, text: `Testing connection pooling - message ${i + 1}` }) ); console.log(`Sending ${messageCount} messages with connection pooling...`); const poolStart = Date.now(); // Send in small batches const batchSize = 5; const results = []; for (let i = 0; i < messages.length; i += batchSize) { const batch = messages.slice(i, i + batchSize); const batchResults = await Promise.all( batch.map(message => pooledClient.sendMail(message).catch(err => ({ success: false, error: err }))) ); results.push(...batchResults); // Small delay between batches if (i + batchSize < messages.length) { await new Promise(resolve => setTimeout(resolve, 200)); } } const successCount = results.filter(r => r.success).length; const poolTime = Date.now() - poolStart; const poolRate = (successCount / poolTime) * 1000; console.log(`Pooled throughput: ${poolRate.toFixed(2)} messages/second`); console.log(`Successfully sent: ${successCount}/${messageCount} messages`); console.log(`Total time: ${poolTime}ms`); expect(successCount).toBeGreaterThan(0); expect(poolRate).toBeGreaterThan(0.1); await pooledClient.close(); }); tap.test('CPERF-02: Variable message size throughput', async (tools) => { tools.timeout(60000); const smtpClient = await createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, debug: false }); // Create messages of varying sizes const messageSizes = [ { size: 'small', content: 'Short message' }, { size: 'medium', content: 'Medium message: ' + 'x'.repeat(500) }, { size: 'large', content: 'Large message: ' + 'x'.repeat(5000) } ]; const messages = []; for (let i = 0; i < 9; i++) { const sizeType = messageSizes[i % messageSizes.length]; messages.push(new Email({ from: 'sender@example.com', to: [`recipient${i + 1}@example.com`], subject: `Variable size test ${i + 1} (${sizeType.size})`, text: sizeType.content })); } console.log(`Sending ${messages.length} messages of varying sizes...`); const variableStart = Date.now(); let successCount = 0; let totalBytes = 0; for (const message of messages) { try { const result = await smtpClient.sendMail(message); if (result.success) { successCount++; // Estimate message size totalBytes += message.text ? message.text.length : 0; } } catch (error) { console.log('Failed to send:', error.message); } // Small delay between messages await new Promise(resolve => setTimeout(resolve, 100)); } const variableTime = Date.now() - variableStart; const variableRate = (successCount / variableTime) * 1000; const bytesPerSecond = (totalBytes / variableTime) * 1000; console.log(`Variable size throughput: ${variableRate.toFixed(2)} messages/second`); console.log(`Data throughput: ${(bytesPerSecond / 1024).toFixed(2)} KB/second`); console.log(`Successfully sent: ${successCount}/${messages.length} messages`); expect(successCount).toBeGreaterThan(0); expect(variableRate).toBeGreaterThan(0.1); await smtpClient.close(); }); tap.test('CPERF-02: Sustained throughput over time', async (tools) => { tools.timeout(60000); const smtpClient = await createPooledSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, maxConnections: 2, debug: false }); const totalMessages = 12; const batchSize = 3; const batchDelay = 1000; // 1 second between batches console.log(`Sending ${totalMessages} messages in batches of ${batchSize}...`); const sustainedStart = Date.now(); let totalSuccess = 0; const timestamps: number[] = []; for (let batch = 0; batch < totalMessages / batchSize; batch++) { const batchMessages = Array(batchSize).fill(null).map((_, i) => { const msgIndex = batch * batchSize + i + 1; return new Email({ from: 'sender@example.com', to: [`recipient${msgIndex}@example.com`], subject: `Sustained test batch ${batch + 1} message ${i + 1}`, text: `Testing sustained throughput - message ${msgIndex}` }); }); // Send batch const batchStart = Date.now(); const results = await Promise.all( batchMessages.map(message => smtpClient.sendMail(message).catch(err => ({ success: false }))) ); const batchSuccess = results.filter(r => r.success).length; totalSuccess += batchSuccess; timestamps.push(Date.now()); console.log(` Batch ${batch + 1} completed: ${batchSuccess}/${batchSize} successful`); // Delay between batches (except last) if (batch < (totalMessages / batchSize) - 1) { await new Promise(resolve => setTimeout(resolve, batchDelay)); } } const sustainedTime = Date.now() - sustainedStart; const sustainedRate = (totalSuccess / sustainedTime) * 1000; console.log(`Sustained throughput: ${sustainedRate.toFixed(2)} messages/second`); console.log(`Successfully sent: ${totalSuccess}/${totalMessages} messages`); console.log(`Total time: ${sustainedTime}ms`); expect(totalSuccess).toBeGreaterThan(0); expect(sustainedRate).toBeGreaterThan(0.05); // Very relaxed for sustained test await smtpClient.close(); }); tap.test('cleanup - stop SMTP server', async () => { await stopTestServer(testServer); }); export default tap.start();