686 lines
23 KiB
TypeScript
686 lines
23 KiB
TypeScript
|
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||
|
import * as plugins from './plugins.js';
|
||
|
import { createTestServer } from '../../helpers/server.loader.js';
|
||
|
import { createSmtpClient } from '../../helpers/smtp.client.js';
|
||
|
|
||
|
tap.test('CPERF-05: should optimize network efficiency', async (tools) => {
|
||
|
const testId = 'CPERF-05-network-efficiency';
|
||
|
console.log(`\n${testId}: Testing network efficiency optimization...`);
|
||
|
|
||
|
let scenarioCount = 0;
|
||
|
|
||
|
// Helper to track network activity
|
||
|
class NetworkTracker {
|
||
|
private startTime: number;
|
||
|
private bytesSent: number = 0;
|
||
|
private bytesReceived: number = 0;
|
||
|
private connections: number = 0;
|
||
|
private roundTrips: number = 0;
|
||
|
|
||
|
constructor() {
|
||
|
this.startTime = Date.now();
|
||
|
}
|
||
|
|
||
|
addConnection() {
|
||
|
this.connections++;
|
||
|
}
|
||
|
|
||
|
addBytesSent(bytes: number) {
|
||
|
this.bytesSent += bytes;
|
||
|
}
|
||
|
|
||
|
addBytesReceived(bytes: number) {
|
||
|
this.bytesReceived += bytes;
|
||
|
}
|
||
|
|
||
|
addRoundTrip() {
|
||
|
this.roundTrips++;
|
||
|
}
|
||
|
|
||
|
getStats() {
|
||
|
const elapsed = Date.now() - this.startTime;
|
||
|
return {
|
||
|
elapsed,
|
||
|
bytesSent: this.bytesSent,
|
||
|
bytesReceived: this.bytesReceived,
|
||
|
totalBytes: this.bytesSent + this.bytesReceived,
|
||
|
connections: this.connections,
|
||
|
roundTrips: this.roundTrips,
|
||
|
bytesPerSecond: ((this.bytesSent + this.bytesReceived) / elapsed) * 1000,
|
||
|
efficiency: this.bytesSent / (this.bytesSent + this.bytesReceived)
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Scenario 1: Connection reuse efficiency
|
||
|
await (async () => {
|
||
|
scenarioCount++;
|
||
|
console.log(`\nScenario ${scenarioCount}: Testing connection reuse efficiency`);
|
||
|
|
||
|
const tracker = new NetworkTracker();
|
||
|
let connectionCount = 0;
|
||
|
let totalCommandBytes = 0;
|
||
|
let totalResponseBytes = 0;
|
||
|
|
||
|
const testServer = await createTestServer({
|
||
|
onConnection: async (socket) => {
|
||
|
connectionCount++;
|
||
|
tracker.addConnection();
|
||
|
console.log(` [Server] Connection ${connectionCount} established`);
|
||
|
|
||
|
const greeting = '220 reuse.example.com ESMTP\r\n';
|
||
|
socket.write(greeting);
|
||
|
tracker.addBytesSent(greeting.length);
|
||
|
|
||
|
socket.on('close', () => {
|
||
|
console.log(` [Server] Connection ${connectionCount} closed`);
|
||
|
});
|
||
|
|
||
|
socket.on('data', (data) => {
|
||
|
totalCommandBytes += data.length;
|
||
|
tracker.addBytesReceived(data.length);
|
||
|
tracker.addRoundTrip();
|
||
|
|
||
|
const command = data.toString().trim();
|
||
|
let response = '';
|
||
|
|
||
|
if (command.startsWith('EHLO')) {
|
||
|
response = '250-reuse.example.com\r\n250 OK\r\n';
|
||
|
} else if (command.startsWith('MAIL FROM:')) {
|
||
|
response = '250 OK\r\n';
|
||
|
} else if (command.startsWith('RCPT TO:')) {
|
||
|
response = '250 OK\r\n';
|
||
|
} else if (command === 'DATA') {
|
||
|
response = '354 Start mail input\r\n';
|
||
|
} else if (command === '.') {
|
||
|
response = '250 OK\r\n';
|
||
|
} else if (command === 'RSET') {
|
||
|
response = '250 OK\r\n';
|
||
|
} else if (command === 'QUIT') {
|
||
|
response = '221 Bye\r\n';
|
||
|
}
|
||
|
|
||
|
if (response) {
|
||
|
socket.write(response);
|
||
|
totalResponseBytes += response.length;
|
||
|
tracker.addBytesSent(response.length);
|
||
|
}
|
||
|
|
||
|
if (command === 'QUIT') {
|
||
|
socket.end();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Test individual connections vs reused connection
|
||
|
const messageCount = 10;
|
||
|
|
||
|
// Individual connections approach
|
||
|
console.log(` Testing ${messageCount} individual connections...`);
|
||
|
const individualStart = Date.now();
|
||
|
connectionCount = 0;
|
||
|
totalCommandBytes = 0;
|
||
|
totalResponseBytes = 0;
|
||
|
|
||
|
for (let i = 0; i < messageCount; i++) {
|
||
|
const client = createSmtpClient({
|
||
|
host: testServer.hostname,
|
||
|
port: testServer.port,
|
||
|
secure: false
|
||
|
});
|
||
|
|
||
|
const email = new plugins.smartmail.Email({
|
||
|
from: 'sender@example.com',
|
||
|
to: [`individual${i + 1}@example.com`],
|
||
|
subject: `Individual connection test ${i + 1}`,
|
||
|
text: `Testing individual connections - message ${i + 1}`
|
||
|
});
|
||
|
|
||
|
await client.sendMail(email);
|
||
|
|
||
|
if (client.close) {
|
||
|
await client.close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const individualTime = Date.now() - individualStart;
|
||
|
const individualStats = {
|
||
|
connections: connectionCount,
|
||
|
commandBytes: totalCommandBytes,
|
||
|
responseBytes: totalResponseBytes,
|
||
|
totalBytes: totalCommandBytes + totalResponseBytes,
|
||
|
time: individualTime
|
||
|
};
|
||
|
|
||
|
console.log(` Individual connections: ${individualStats.connections} connections, ${individualStats.totalBytes} bytes`);
|
||
|
|
||
|
// Connection reuse approach
|
||
|
console.log(` Testing connection reuse...`);
|
||
|
const reuseStart = Date.now();
|
||
|
connectionCount = 0;
|
||
|
totalCommandBytes = 0;
|
||
|
totalResponseBytes = 0;
|
||
|
|
||
|
const reuseClient = createSmtpClient({
|
||
|
host: testServer.hostname,
|
||
|
port: testServer.port,
|
||
|
secure: false,
|
||
|
pool: true,
|
||
|
maxConnections: 1,
|
||
|
maxMessages: messageCount
|
||
|
});
|
||
|
|
||
|
const reuseEmails = Array(messageCount).fill(null).map((_, i) =>
|
||
|
new plugins.smartmail.Email({
|
||
|
from: 'sender@example.com',
|
||
|
to: [`reuse${i + 1}@example.com`],
|
||
|
subject: `Connection reuse test ${i + 1}`,
|
||
|
text: `Testing connection reuse - message ${i + 1}`
|
||
|
})
|
||
|
);
|
||
|
|
||
|
for (const email of reuseEmails) {
|
||
|
await reuseClient.sendMail(email);
|
||
|
}
|
||
|
|
||
|
await reuseClient.close();
|
||
|
|
||
|
const reuseTime = Date.now() - reuseStart;
|
||
|
const reuseStats = {
|
||
|
connections: connectionCount,
|
||
|
commandBytes: totalCommandBytes,
|
||
|
responseBytes: totalResponseBytes,
|
||
|
totalBytes: totalCommandBytes + totalResponseBytes,
|
||
|
time: reuseTime
|
||
|
};
|
||
|
|
||
|
console.log(` Connection reuse: ${reuseStats.connections} connections, ${reuseStats.totalBytes} bytes`);
|
||
|
|
||
|
// Calculate efficiency
|
||
|
const connectionEfficiency = individualStats.connections / reuseStats.connections;
|
||
|
const byteEfficiency = individualStats.totalBytes / reuseStats.totalBytes;
|
||
|
const timeEfficiency = individualTime / reuseTime;
|
||
|
|
||
|
console.log(` Connection efficiency: ${connectionEfficiency.toFixed(1)}x`);
|
||
|
console.log(` Byte efficiency: ${byteEfficiency.toFixed(1)}x`);
|
||
|
console.log(` Time efficiency: ${timeEfficiency.toFixed(1)}x`);
|
||
|
|
||
|
// Connection reuse should be more efficient
|
||
|
expect(reuseStats.connections).toBeLessThan(individualStats.connections);
|
||
|
expect(reuseStats.totalBytes).toBeLessThan(individualStats.totalBytes);
|
||
|
expect(connectionEfficiency).toBeGreaterThan(5); // At least 5x fewer connections
|
||
|
|
||
|
await testServer.server.close();
|
||
|
})();
|
||
|
|
||
|
// Scenario 2: Command pipelining efficiency
|
||
|
await (async () => {
|
||
|
scenarioCount++;
|
||
|
console.log(`\nScenario ${scenarioCount}: Testing command pipelining efficiency`);
|
||
|
|
||
|
let totalCommands = 0;
|
||
|
let pipelinedCommands = 0;
|
||
|
let maxPipelineDepth = 0;
|
||
|
|
||
|
const testServer = await createTestServer({
|
||
|
onConnection: async (socket) => {
|
||
|
console.log(' [Server] Client connected for pipelining test');
|
||
|
socket.write('220 pipeline.example.com ESMTP\r\n');
|
||
|
|
||
|
socket.on('data', (data) => {
|
||
|
const commands = data.toString().split('\r\n').filter(cmd => cmd.length > 0);
|
||
|
totalCommands += commands.length;
|
||
|
|
||
|
if (commands.length > 1) {
|
||
|
pipelinedCommands += commands.length;
|
||
|
maxPipelineDepth = Math.max(maxPipelineDepth, commands.length);
|
||
|
console.log(` [Server] Received ${commands.length} pipelined commands`);
|
||
|
}
|
||
|
|
||
|
commands.forEach(command => {
|
||
|
if (command.startsWith('EHLO')) {
|
||
|
socket.write('250-pipeline.example.com\r\n250-PIPELINING\r\n250 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 Start mail input\r\n');
|
||
|
} else if (command === '.') {
|
||
|
socket.write('250 OK\r\n');
|
||
|
} else if (command === 'QUIT') {
|
||
|
socket.write('221 Bye\r\n');
|
||
|
socket.end();
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
|
||
|
const pipelineClient = createSmtpClient({
|
||
|
host: testServer.hostname,
|
||
|
port: testServer.port,
|
||
|
secure: false,
|
||
|
pipelining: true
|
||
|
});
|
||
|
|
||
|
// Send emails with multiple recipients (triggers pipelining)
|
||
|
const emails = [
|
||
|
new plugins.smartmail.Email({
|
||
|
from: 'sender@example.com',
|
||
|
to: ['recipient1@example.com', 'recipient2@example.com', 'recipient3@example.com'],
|
||
|
subject: 'Pipelining test 1',
|
||
|
text: 'Testing command pipelining efficiency'
|
||
|
}),
|
||
|
new plugins.smartmail.Email({
|
||
|
from: 'sender@example.com',
|
||
|
to: ['recipient4@example.com', 'recipient5@example.com'],
|
||
|
subject: 'Pipelining test 2',
|
||
|
text: 'Testing command pipelining efficiency'
|
||
|
})
|
||
|
];
|
||
|
|
||
|
console.log(' Sending emails with pipelining support...');
|
||
|
for (const email of emails) {
|
||
|
await pipelineClient.sendMail(email);
|
||
|
}
|
||
|
|
||
|
console.log(` Total commands sent: ${totalCommands}`);
|
||
|
console.log(` Pipelined commands: ${pipelinedCommands}`);
|
||
|
console.log(` Max pipeline depth: ${maxPipelineDepth}`);
|
||
|
console.log(` Pipelining efficiency: ${(pipelinedCommands / totalCommands * 100).toFixed(1)}%`);
|
||
|
|
||
|
// Should use pipelining for efficiency
|
||
|
expect(pipelinedCommands).toBeGreaterThan(0);
|
||
|
expect(maxPipelineDepth).toBeGreaterThan(1);
|
||
|
|
||
|
await testServer.server.close();
|
||
|
})();
|
||
|
|
||
|
// Scenario 3: Message size optimization
|
||
|
await (async () => {
|
||
|
scenarioCount++;
|
||
|
console.log(`\nScenario ${scenarioCount}: Testing message size optimization`);
|
||
|
|
||
|
let totalMessageBytes = 0;
|
||
|
let messageCount = 0;
|
||
|
|
||
|
const testServer = await createTestServer({
|
||
|
onConnection: async (socket) => {
|
||
|
console.log(' [Server] Client connected for size optimization test');
|
||
|
socket.write('220 size.example.com ESMTP\r\n');
|
||
|
|
||
|
let inData = false;
|
||
|
let messageBytes = 0;
|
||
|
|
||
|
socket.on('data', (data) => {
|
||
|
if (inData) {
|
||
|
messageBytes += data.length;
|
||
|
if (data.toString().includes('\r\n.\r\n')) {
|
||
|
inData = false;
|
||
|
messageCount++;
|
||
|
totalMessageBytes += messageBytes;
|
||
|
console.log(` [Server] Message ${messageCount}: ${messageBytes} bytes`);
|
||
|
socket.write('250 OK\r\n');
|
||
|
messageBytes = 0;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const command = data.toString().trim();
|
||
|
|
||
|
if (command.startsWith('EHLO')) {
|
||
|
socket.write('250-size.example.com\r\n250-SIZE 52428800\r\n250 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 Start mail input\r\n');
|
||
|
inData = true;
|
||
|
messageBytes = 0;
|
||
|
} else if (command === 'QUIT') {
|
||
|
socket.write('221 Bye\r\n');
|
||
|
socket.end();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
|
||
|
const sizeClient = createSmtpClient({
|
||
|
host: testServer.hostname,
|
||
|
port: testServer.port,
|
||
|
secure: false
|
||
|
});
|
||
|
|
||
|
// Test different message sizes and encoding efficiency
|
||
|
const sizeTests = [
|
||
|
{
|
||
|
name: 'Plain text',
|
||
|
email: new plugins.smartmail.Email({
|
||
|
from: 'sender@example.com',
|
||
|
to: ['recipient@example.com'],
|
||
|
subject: 'Plain text efficiency test',
|
||
|
text: 'This is a plain text message for testing size efficiency.'
|
||
|
})
|
||
|
},
|
||
|
{
|
||
|
name: 'HTML message',
|
||
|
email: new plugins.smartmail.Email({
|
||
|
from: 'sender@example.com',
|
||
|
to: ['recipient@example.com'],
|
||
|
subject: 'HTML efficiency test',
|
||
|
html: '<html><body><h1>HTML Message</h1><p>This is an HTML message.</p></body></html>'
|
||
|
})
|
||
|
},
|
||
|
{
|
||
|
name: 'Multipart message',
|
||
|
email: new plugins.smartmail.Email({
|
||
|
from: 'sender@example.com',
|
||
|
to: ['recipient@example.com'],
|
||
|
subject: 'Multipart efficiency test',
|
||
|
text: 'Plain text version of the message.',
|
||
|
html: '<p>HTML version of the message.</p>'
|
||
|
})
|
||
|
},
|
||
|
{
|
||
|
name: 'Message with small attachment',
|
||
|
email: new plugins.smartmail.Email({
|
||
|
from: 'sender@example.com',
|
||
|
to: ['recipient@example.com'],
|
||
|
subject: 'Attachment efficiency test',
|
||
|
text: 'Message with attachment.',
|
||
|
attachments: [{
|
||
|
filename: 'test.txt',
|
||
|
content: 'Small test attachment content for efficiency testing.'
|
||
|
}]
|
||
|
})
|
||
|
}
|
||
|
];
|
||
|
|
||
|
for (const test of sizeTests) {
|
||
|
console.log(` Testing ${test.name}...`);
|
||
|
|
||
|
const beforeBytes = totalMessageBytes;
|
||
|
const beforeCount = messageCount;
|
||
|
|
||
|
await sizeClient.sendMail(test.email);
|
||
|
|
||
|
const messageSize = totalMessageBytes - beforeBytes;
|
||
|
const overhead = messageSize / (test.email.text?.length || test.email.html?.length || 100);
|
||
|
|
||
|
console.log(` ${test.name}: ${messageSize} bytes (overhead: ${overhead.toFixed(1)}x)`);
|
||
|
|
||
|
// Message overhead should be reasonable
|
||
|
expect(overhead).toBeLessThan(10); // Less than 10x overhead
|
||
|
}
|
||
|
|
||
|
const avgMessageSize = totalMessageBytes / messageCount;
|
||
|
console.log(` Average message size: ${avgMessageSize.toFixed(0)} bytes`);
|
||
|
|
||
|
await testServer.server.close();
|
||
|
})();
|
||
|
|
||
|
// Scenario 4: Bandwidth utilization
|
||
|
await (async () => {
|
||
|
scenarioCount++;
|
||
|
console.log(`\nScenario ${scenarioCount}: Testing bandwidth utilization`);
|
||
|
|
||
|
let totalBytes = 0;
|
||
|
let dataTransferTime = 0;
|
||
|
|
||
|
const testServer = await createTestServer({
|
||
|
onConnection: async (socket) => {
|
||
|
socket.write('220 bandwidth.example.com ESMTP\r\n');
|
||
|
|
||
|
let transferStart = 0;
|
||
|
let inData = false;
|
||
|
|
||
|
socket.on('data', (data) => {
|
||
|
totalBytes += data.length;
|
||
|
|
||
|
if (inData) {
|
||
|
if (data.toString().includes('\r\n.\r\n')) {
|
||
|
inData = false;
|
||
|
dataTransferTime += Date.now() - transferStart;
|
||
|
socket.write('250 OK\r\n');
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const command = data.toString().trim();
|
||
|
|
||
|
if (command.startsWith('EHLO')) {
|
||
|
socket.write('250-bandwidth.example.com\r\n250 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 Start mail input\r\n');
|
||
|
inData = true;
|
||
|
transferStart = Date.now();
|
||
|
} else if (command === 'QUIT') {
|
||
|
socket.write('221 Bye\r\n');
|
||
|
socket.end();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
|
||
|
const bandwidthClient = createSmtpClient({
|
||
|
host: testServer.hostname,
|
||
|
port: testServer.port,
|
||
|
secure: false
|
||
|
});
|
||
|
|
||
|
// Test bandwidth efficiency with varying message sizes
|
||
|
const messageSizes = [1024, 10240, 102400]; // 1KB, 10KB, 100KB
|
||
|
|
||
|
console.log(' Testing bandwidth utilization with different message sizes...');
|
||
|
const bandwidthStart = Date.now();
|
||
|
|
||
|
for (const size of messageSizes) {
|
||
|
const content = 'x'.repeat(size);
|
||
|
|
||
|
const email = new plugins.smartmail.Email({
|
||
|
from: 'sender@example.com',
|
||
|
to: ['recipient@example.com'],
|
||
|
subject: `Bandwidth test ${size} bytes`,
|
||
|
text: content
|
||
|
});
|
||
|
|
||
|
await bandwidthClient.sendMail(email);
|
||
|
}
|
||
|
|
||
|
const bandwidthElapsed = Date.now() - bandwidthStart;
|
||
|
const throughput = (totalBytes / bandwidthElapsed) * 1000; // bytes per second
|
||
|
const dataEfficiency = (messageSizes.reduce((a, b) => a + b, 0) / totalBytes) * 100;
|
||
|
|
||
|
console.log(` Total bytes transferred: ${totalBytes}`);
|
||
|
console.log(` Data transfer time: ${dataTransferTime}ms`);
|
||
|
console.log(` Overall throughput: ${(throughput / 1024).toFixed(1)} KB/s`);
|
||
|
console.log(` Data efficiency: ${dataEfficiency.toFixed(1)}% (payload vs total)`);
|
||
|
|
||
|
// Bandwidth utilization should be efficient
|
||
|
expect(throughput).toBeGreaterThan(1024); // At least 1KB/s
|
||
|
expect(dataEfficiency).toBeGreaterThan(20); // At least 20% payload efficiency
|
||
|
|
||
|
await testServer.server.close();
|
||
|
})();
|
||
|
|
||
|
// Scenario 5: Network round-trip optimization
|
||
|
await (async () => {
|
||
|
scenarioCount++;
|
||
|
console.log(`\nScenario ${scenarioCount}: Testing network round-trip optimization`);
|
||
|
|
||
|
let roundTrips = 0;
|
||
|
let commandCount = 0;
|
||
|
|
||
|
const testServer = await createTestServer({
|
||
|
onConnection: async (socket) => {
|
||
|
socket.write('220 roundtrip.example.com ESMTP\r\n');
|
||
|
|
||
|
socket.on('data', (data) => {
|
||
|
const commands = data.toString().split('\r\n').filter(cmd => cmd.length > 0);
|
||
|
roundTrips++;
|
||
|
commandCount += commands.length;
|
||
|
|
||
|
console.log(` [Server] Round-trip ${roundTrips}: ${commands.length} commands`);
|
||
|
|
||
|
commands.forEach(command => {
|
||
|
if (command.startsWith('EHLO')) {
|
||
|
socket.write('250-roundtrip.example.com\r\n250-PIPELINING\r\n250 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 Start mail input\r\n');
|
||
|
} else if (command === '.') {
|
||
|
socket.write('250 OK\r\n');
|
||
|
} else if (command === 'QUIT') {
|
||
|
socket.write('221 Bye\r\n');
|
||
|
socket.end();
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
|
||
|
const roundtripClient = createSmtpClient({
|
||
|
host: testServer.hostname,
|
||
|
port: testServer.port,
|
||
|
secure: false,
|
||
|
pipelining: true
|
||
|
});
|
||
|
|
||
|
// Send email with multiple recipients to test round-trip efficiency
|
||
|
const email = new plugins.smartmail.Email({
|
||
|
from: 'sender@example.com',
|
||
|
to: ['rcpt1@example.com', 'rcpt2@example.com', 'rcpt3@example.com', 'rcpt4@example.com'],
|
||
|
subject: 'Round-trip optimization test',
|
||
|
text: 'Testing network round-trip optimization with multiple recipients'
|
||
|
});
|
||
|
|
||
|
console.log(' Sending email with multiple recipients...');
|
||
|
await roundtripClient.sendMail(email);
|
||
|
|
||
|
const commandsPerRoundTrip = commandCount / roundTrips;
|
||
|
const efficiency = commandsPerRoundTrip;
|
||
|
|
||
|
console.log(` Total round-trips: ${roundTrips}`);
|
||
|
console.log(` Total commands: ${commandCount}`);
|
||
|
console.log(` Commands per round-trip: ${commandsPerRoundTrip.toFixed(1)}`);
|
||
|
console.log(` Round-trip efficiency: ${efficiency.toFixed(1)}`);
|
||
|
|
||
|
// Should minimize round-trips through pipelining
|
||
|
expect(roundTrips).toBeLessThan(commandCount); // Fewer round-trips than commands
|
||
|
expect(commandsPerRoundTrip).toBeGreaterThan(1.5); // At least 1.5 commands per round-trip
|
||
|
|
||
|
await testServer.server.close();
|
||
|
})();
|
||
|
|
||
|
// Scenario 6: Connection pooling network efficiency
|
||
|
await (async () => {
|
||
|
scenarioCount++;
|
||
|
console.log(`\nScenario ${scenarioCount}: Testing connection pooling network efficiency`);
|
||
|
|
||
|
let totalConnections = 0;
|
||
|
let totalBytes = 0;
|
||
|
let connectionSetupBytes = 0;
|
||
|
|
||
|
const testServer = await createTestServer({
|
||
|
onConnection: async (socket) => {
|
||
|
totalConnections++;
|
||
|
const greeting = '220 pool.example.com ESMTP\r\n';
|
||
|
socket.write(greeting);
|
||
|
connectionSetupBytes += greeting.length;
|
||
|
|
||
|
console.log(` [Server] Pool connection ${totalConnections} established`);
|
||
|
|
||
|
socket.on('data', (data) => {
|
||
|
totalBytes += data.length;
|
||
|
const command = data.toString().trim();
|
||
|
|
||
|
if (command.startsWith('EHLO')) {
|
||
|
const response = '250-pool.example.com\r\n250 OK\r\n';
|
||
|
socket.write(response);
|
||
|
connectionSetupBytes += response.length;
|
||
|
totalBytes += response.length;
|
||
|
} else if (command.startsWith('MAIL FROM:')) {
|
||
|
const response = '250 OK\r\n';
|
||
|
socket.write(response);
|
||
|
totalBytes += response.length;
|
||
|
} else if (command.startsWith('RCPT TO:')) {
|
||
|
const response = '250 OK\r\n';
|
||
|
socket.write(response);
|
||
|
totalBytes += response.length;
|
||
|
} else if (command === 'DATA') {
|
||
|
const response = '354 Start mail input\r\n';
|
||
|
socket.write(response);
|
||
|
totalBytes += response.length;
|
||
|
} else if (command === '.') {
|
||
|
const response = '250 OK\r\n';
|
||
|
socket.write(response);
|
||
|
totalBytes += response.length;
|
||
|
} else if (command === 'QUIT') {
|
||
|
const response = '221 Bye\r\n';
|
||
|
socket.write(response);
|
||
|
totalBytes += response.length;
|
||
|
socket.end();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
|
||
|
const poolClient = createSmtpClient({
|
||
|
host: testServer.hostname,
|
||
|
port: testServer.port,
|
||
|
secure: false,
|
||
|
pool: true,
|
||
|
maxConnections: 3,
|
||
|
maxMessages: 100
|
||
|
});
|
||
|
|
||
|
// Send multiple emails through pool
|
||
|
const emailCount = 15;
|
||
|
const emails = Array(emailCount).fill(null).map((_, i) =>
|
||
|
new plugins.smartmail.Email({
|
||
|
from: 'sender@example.com',
|
||
|
to: [`pooled${i + 1}@example.com`],
|
||
|
subject: `Pool efficiency test ${i + 1}`,
|
||
|
text: `Testing pooled connection network efficiency - message ${i + 1}`
|
||
|
})
|
||
|
);
|
||
|
|
||
|
console.log(` Sending ${emailCount} emails through connection pool...`);
|
||
|
const poolStart = Date.now();
|
||
|
|
||
|
await Promise.all(emails.map(email => poolClient.sendMail(email)));
|
||
|
await poolClient.close();
|
||
|
|
||
|
const poolTime = Date.now() - poolStart;
|
||
|
const setupOverhead = (connectionSetupBytes / totalBytes) * 100;
|
||
|
const messagesPerConnection = emailCount / totalConnections;
|
||
|
const bytesPerMessage = totalBytes / emailCount;
|
||
|
|
||
|
console.log(` Emails sent: ${emailCount}`);
|
||
|
console.log(` Connections used: ${totalConnections}`);
|
||
|
console.log(` Messages per connection: ${messagesPerConnection.toFixed(1)}`);
|
||
|
console.log(` Total bytes: ${totalBytes}`);
|
||
|
console.log(` Setup overhead: ${setupOverhead.toFixed(1)}%`);
|
||
|
console.log(` Bytes per message: ${bytesPerMessage.toFixed(0)}`);
|
||
|
console.log(` Network efficiency: ${(emailCount / totalConnections).toFixed(1)} msg/conn`);
|
||
|
|
||
|
// Connection pooling should be network efficient
|
||
|
expect(totalConnections).toBeLessThan(emailCount); // Fewer connections than messages
|
||
|
expect(messagesPerConnection).toBeGreaterThan(3); // At least 3 messages per connection
|
||
|
expect(setupOverhead).toBeLessThan(20); // Less than 20% setup overhead
|
||
|
|
||
|
await testServer.server.close();
|
||
|
})();
|
||
|
|
||
|
console.log(`\n${testId}: All ${scenarioCount} network efficiency scenarios tested ✓`);
|
||
|
});
|