183 lines
6.2 KiB
TypeScript
183 lines
6.2 KiB
TypeScript
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
|
import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.js';
|
|
// import { createTestSmtpClient, sendConcurrentEmails, measureClientThroughput } from '../../helpers/smtp.client.js';
|
|
import { connectToSmtp, sendSmtpCommand, waitForGreeting, createMimeMessage, closeSmtpConnection } from '../../helpers/utils.js';
|
|
|
|
let testServer: ITestServer;
|
|
|
|
tap.test('setup - start SMTP server for performance testing', async () => {
|
|
testServer = await startTestServer({
|
|
port: 2531,
|
|
hostname: 'localhost',
|
|
maxConnections: 1000,
|
|
size: 50 * 1024 * 1024 // 50MB for performance testing
|
|
});
|
|
expect(testServer).toBeInstanceOf(Object);
|
|
});
|
|
|
|
// TODO: Enable these tests when the helper functions are implemented
|
|
/*
|
|
tap.test('PERF-01: Throughput Testing - measure emails per second', async () => {
|
|
const client = createTestSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
maxConnections: 10
|
|
});
|
|
|
|
try {
|
|
// Warm up the connection pool
|
|
console.log('🔥 Warming up connection pool...');
|
|
await sendConcurrentEmails(client, 5);
|
|
|
|
// Measure throughput for 10 seconds
|
|
console.log('📊 Measuring throughput for 10 seconds...');
|
|
const startTime = Date.now();
|
|
const testDuration = 10000; // 10 seconds
|
|
|
|
const result = await measureClientThroughput(client, testDuration, {
|
|
from: 'perf-test@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Performance Test Email',
|
|
text: 'This is a performance test email to measure throughput.'
|
|
});
|
|
|
|
const actualDuration = (Date.now() - startTime) / 1000;
|
|
|
|
console.log('📈 Throughput Test Results:');
|
|
console.log(` Total emails sent: ${result.totalSent}`);
|
|
console.log(` Successful: ${result.successCount}`);
|
|
console.log(` Failed: ${result.errorCount}`);
|
|
console.log(` Duration: ${actualDuration.toFixed(2)}s`);
|
|
console.log(` Throughput: ${result.throughput.toFixed(2)} emails/second`);
|
|
|
|
// Performance expectations
|
|
expect(result.throughput).toBeGreaterThan(10); // At least 10 emails/second
|
|
expect(result.errorCount).toBeLessThan(result.totalSent * 0.05); // Less than 5% errors
|
|
|
|
console.log('✅ Throughput test passed');
|
|
|
|
} finally {
|
|
if (client.close) {
|
|
await client.close();
|
|
}
|
|
}
|
|
});
|
|
|
|
tap.test('PERF-01: Burst throughput - handle sudden load spikes', async () => {
|
|
const client = createTestSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
maxConnections: 20
|
|
});
|
|
|
|
try {
|
|
// Send burst of emails
|
|
const burstSize = 100;
|
|
console.log(`💥 Sending burst of ${burstSize} emails...`);
|
|
|
|
const startTime = Date.now();
|
|
const results = await sendConcurrentEmails(client, burstSize, {
|
|
from: 'burst-test@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Burst Test Email',
|
|
text: 'Testing burst performance.'
|
|
});
|
|
|
|
const duration = Date.now() - startTime;
|
|
const successCount = results.filter(r => r && !r.rejected).length;
|
|
const throughput = (successCount / duration) * 1000;
|
|
|
|
console.log(`✅ Burst completed in ${duration}ms`);
|
|
console.log(` Success rate: ${successCount}/${burstSize} (${(successCount/burstSize*100).toFixed(1)}%)`);
|
|
console.log(` Burst throughput: ${throughput.toFixed(2)} emails/second`);
|
|
|
|
expect(successCount).toBeGreaterThan(burstSize * 0.95); // 95% success rate
|
|
|
|
} finally {
|
|
if (client.close) {
|
|
await client.close();
|
|
}
|
|
}
|
|
});
|
|
*/
|
|
|
|
tap.test('PERF-01: Large message throughput - measure with varying sizes', async () => {
|
|
const messageSizes = [
|
|
{ size: 1024, label: '1KB' },
|
|
{ size: 100 * 1024, label: '100KB' },
|
|
{ size: 1024 * 1024, label: '1MB' },
|
|
{ size: 5 * 1024 * 1024, label: '5MB' }
|
|
];
|
|
|
|
for (const { size, label } of messageSizes) {
|
|
console.log(`\n📧 Testing throughput with ${label} messages...`);
|
|
|
|
const socket = await connectToSmtp(testServer.hostname, testServer.port);
|
|
|
|
try {
|
|
await waitForGreeting(socket);
|
|
await sendSmtpCommand(socket, 'EHLO test.example.com', '250');
|
|
|
|
// Send a few messages of this size
|
|
const messageCount = 5;
|
|
const timings: number[] = [];
|
|
|
|
for (let i = 0; i < messageCount; i++) {
|
|
const startTime = Date.now();
|
|
|
|
await sendSmtpCommand(socket, 'MAIL FROM:<size-test@example.com>', '250');
|
|
await sendSmtpCommand(socket, 'RCPT TO:<recipient@example.com>', '250');
|
|
await sendSmtpCommand(socket, 'DATA', '354');
|
|
|
|
// Create message with padding to reach target size
|
|
const padding = 'X'.repeat(Math.max(0, size - 200)); // Account for headers
|
|
const emailContent = createMimeMessage({
|
|
from: 'size-test@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: `${label} Performance Test`,
|
|
text: padding
|
|
});
|
|
|
|
socket.write(emailContent);
|
|
socket.write('\r\n.\r\n');
|
|
|
|
// Wait for acceptance
|
|
await new Promise<void>((resolve, reject) => {
|
|
const timeout = setTimeout(() => reject(new Error('Timeout')), 30000);
|
|
const onData = (data: Buffer) => {
|
|
if (data.toString().includes('250')) {
|
|
clearTimeout(timeout);
|
|
socket.removeListener('data', onData);
|
|
resolve();
|
|
}
|
|
};
|
|
socket.on('data', onData);
|
|
});
|
|
|
|
const duration = Date.now() - startTime;
|
|
timings.push(duration);
|
|
|
|
// Reset for next message
|
|
await sendSmtpCommand(socket, 'RSET', '250');
|
|
}
|
|
|
|
const avgTime = timings.reduce((a, b) => a + b, 0) / timings.length;
|
|
const throughputMBps = (size / 1024 / 1024) / (avgTime / 1000);
|
|
|
|
console.log(` Average time: ${avgTime.toFixed(0)}ms`);
|
|
console.log(` Throughput: ${throughputMBps.toFixed(2)} MB/s`);
|
|
|
|
} finally {
|
|
await closeSmtpConnection(socket);
|
|
}
|
|
}
|
|
|
|
console.log('\n✅ Large message throughput test completed');
|
|
});
|
|
|
|
tap.test('cleanup - stop SMTP server', async () => {
|
|
await stopTestServer(testServer);
|
|
console.log('✅ Test server stopped');
|
|
});
|
|
|
|
export default tap.start(); |