304 lines
9.7 KiB
TypeScript
304 lines
9.7 KiB
TypeScript
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(); |