373 lines
12 KiB
TypeScript
373 lines
12 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';
|
|
|
|
let testServer: ITestServer;
|
|
|
|
// Helper function to measure CPU usage
|
|
const measureCpuUsage = async (duration: number) => {
|
|
const start = process.cpuUsage();
|
|
const startTime = Date.now();
|
|
|
|
await new Promise(resolve => setTimeout(resolve, duration));
|
|
|
|
const end = process.cpuUsage(start);
|
|
const elapsed = Date.now() - startTime;
|
|
|
|
// Ensure minimum elapsed time to avoid division issues
|
|
const actualElapsed = Math.max(elapsed, 1);
|
|
|
|
return {
|
|
user: end.user / 1000, // Convert to milliseconds
|
|
system: end.system / 1000,
|
|
total: (end.user + end.system) / 1000,
|
|
elapsed: actualElapsed,
|
|
userPercent: (end.user / 1000) / actualElapsed * 100,
|
|
systemPercent: (end.system / 1000) / actualElapsed * 100,
|
|
totalPercent: Math.min(((end.user + end.system) / 1000) / actualElapsed * 100, 100)
|
|
};
|
|
};
|
|
|
|
tap.test('setup - start SMTP server for CPU tests', async () => {
|
|
testServer = await startTestServer({
|
|
port: 0,
|
|
enableStarttls: false,
|
|
authRequired: false
|
|
});
|
|
|
|
expect(testServer.port).toBeGreaterThan(0);
|
|
});
|
|
|
|
tap.test('CPERF-04: CPU usage during connection establishment', async (tools) => {
|
|
tools.timeout(30000);
|
|
|
|
console.log('Testing CPU usage during connection establishment...');
|
|
|
|
// Measure baseline CPU
|
|
const baseline = await measureCpuUsage(1000);
|
|
console.log(`Baseline CPU: ${baseline.totalPercent.toFixed(2)}%`);
|
|
|
|
// Ensure we have a meaningful duration for measurement
|
|
const connectionCount = 5;
|
|
const startTime = Date.now();
|
|
const cpuStart = process.cpuUsage();
|
|
|
|
for (let i = 0; i < connectionCount; i++) {
|
|
const client = await createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
debug: false
|
|
});
|
|
|
|
await client.close();
|
|
|
|
// Small delay to ensure measurable duration
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
}
|
|
|
|
const elapsed = Date.now() - startTime;
|
|
const cpuEnd = process.cpuUsage(cpuStart);
|
|
|
|
// Ensure minimum elapsed time
|
|
const actualElapsed = Math.max(elapsed, 100);
|
|
const cpuPercent = Math.min(((cpuEnd.user + cpuEnd.system) / 1000) / actualElapsed * 100, 100);
|
|
|
|
console.log(`CPU usage for ${connectionCount} connections:`);
|
|
console.log(` Total time: ${actualElapsed}ms`);
|
|
console.log(` CPU time: ${(cpuEnd.user + cpuEnd.system) / 1000}ms`);
|
|
console.log(` CPU usage: ${cpuPercent.toFixed(2)}%`);
|
|
console.log(` Average per connection: ${(cpuPercent / connectionCount).toFixed(2)}%`);
|
|
|
|
// CPU usage should be reasonable (relaxed for test environment)
|
|
expect(cpuPercent).toBeLessThan(100); // Must be less than 100%
|
|
});
|
|
|
|
tap.test('CPERF-04: CPU usage during message sending', async (tools) => {
|
|
tools.timeout(30000);
|
|
|
|
console.log('\nTesting CPU usage during message sending...');
|
|
|
|
const client = await createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
debug: false
|
|
});
|
|
|
|
const messageCount = 10; // Reduced for more stable measurement
|
|
|
|
// Measure CPU during message sending
|
|
const cpuStart = process.cpuUsage();
|
|
const startTime = Date.now();
|
|
|
|
for (let i = 0; i < messageCount; i++) {
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: [`recipient${i}@example.com`],
|
|
subject: `CPU test message ${i + 1}`,
|
|
text: `Testing CPU usage during message ${i + 1}`
|
|
});
|
|
|
|
await client.sendMail(email);
|
|
|
|
// Small delay between messages
|
|
await new Promise(resolve => setTimeout(resolve, 50));
|
|
}
|
|
|
|
const elapsed = Date.now() - startTime;
|
|
const cpuEnd = process.cpuUsage(cpuStart);
|
|
const actualElapsed = Math.max(elapsed, 100);
|
|
const cpuPercent = Math.min(((cpuEnd.user + cpuEnd.system) / 1000) / actualElapsed * 100, 100);
|
|
|
|
await client.close();
|
|
|
|
console.log(`CPU usage for ${messageCount} messages:`);
|
|
console.log(` Total time: ${actualElapsed}ms`);
|
|
console.log(` CPU time: ${(cpuEnd.user + cpuEnd.system) / 1000}ms`);
|
|
console.log(` CPU usage: ${cpuPercent.toFixed(2)}%`);
|
|
console.log(` Messages per second: ${(messageCount / (actualElapsed / 1000)).toFixed(2)}`);
|
|
console.log(` CPU per message: ${(cpuPercent / messageCount).toFixed(2)}%`);
|
|
|
|
// CPU usage should be efficient (relaxed for test environment)
|
|
expect(cpuPercent).toBeLessThan(100);
|
|
});
|
|
|
|
tap.test('CPERF-04: CPU usage with parallel operations', async (tools) => {
|
|
tools.timeout(30000);
|
|
|
|
console.log('\nTesting CPU usage with parallel operations...');
|
|
|
|
// Create multiple clients for parallel operations
|
|
const clientCount = 2; // Reduced
|
|
const messagesPerClient = 3; // Reduced
|
|
|
|
const clients = [];
|
|
for (let i = 0; i < clientCount; i++) {
|
|
clients.push(await createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
debug: false
|
|
}));
|
|
}
|
|
|
|
// Measure CPU during parallel operations
|
|
const cpuStart = process.cpuUsage();
|
|
const startTime = Date.now();
|
|
|
|
const promises = [];
|
|
for (let clientIndex = 0; clientIndex < clientCount; clientIndex++) {
|
|
for (let msgIndex = 0; msgIndex < messagesPerClient; msgIndex++) {
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: [`recipient${clientIndex}-${msgIndex}@example.com`],
|
|
subject: `Parallel CPU test ${clientIndex}-${msgIndex}`,
|
|
text: 'Testing CPU with parallel operations'
|
|
});
|
|
|
|
promises.push(clients[clientIndex].sendMail(email));
|
|
}
|
|
}
|
|
|
|
await Promise.all(promises);
|
|
|
|
const elapsed = Date.now() - startTime;
|
|
const cpuEnd = process.cpuUsage(cpuStart);
|
|
const actualElapsed = Math.max(elapsed, 100);
|
|
const cpuPercent = Math.min(((cpuEnd.user + cpuEnd.system) / 1000) / actualElapsed * 100, 100);
|
|
|
|
// Close all clients
|
|
await Promise.all(clients.map(client => client.close()));
|
|
|
|
const totalMessages = clientCount * messagesPerClient;
|
|
console.log(`CPU usage for ${totalMessages} messages across ${clientCount} clients:`);
|
|
console.log(` Total time: ${actualElapsed}ms`);
|
|
console.log(` CPU time: ${(cpuEnd.user + cpuEnd.system) / 1000}ms`);
|
|
console.log(` CPU usage: ${cpuPercent.toFixed(2)}%`);
|
|
|
|
// Parallel operations should complete successfully
|
|
expect(cpuPercent).toBeLessThan(100);
|
|
});
|
|
|
|
tap.test('CPERF-04: CPU usage with large messages', async (tools) => {
|
|
tools.timeout(30000);
|
|
|
|
console.log('\nTesting CPU usage with large messages...');
|
|
|
|
const client = await createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
debug: false
|
|
});
|
|
|
|
const messageSizes = [
|
|
{ name: 'small', size: 1024 }, // 1KB
|
|
{ name: 'medium', size: 10240 }, // 10KB
|
|
{ name: 'large', size: 51200 } // 50KB (reduced from 100KB)
|
|
];
|
|
|
|
for (const { name, size } of messageSizes) {
|
|
const cpuStart = process.cpuUsage();
|
|
const startTime = Date.now();
|
|
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: ['recipient@example.com'],
|
|
subject: `Large message test (${name})`,
|
|
text: 'x'.repeat(size)
|
|
});
|
|
|
|
await client.sendMail(email);
|
|
|
|
const elapsed = Date.now() - startTime;
|
|
const cpuEnd = process.cpuUsage(cpuStart);
|
|
const actualElapsed = Math.max(elapsed, 1);
|
|
const cpuPercent = Math.min(((cpuEnd.user + cpuEnd.system) / 1000) / actualElapsed * 100, 100);
|
|
|
|
console.log(`CPU usage for ${name} message (${size} bytes):`);
|
|
console.log(` Time: ${actualElapsed}ms`);
|
|
console.log(` CPU: ${cpuPercent.toFixed(2)}%`);
|
|
console.log(` Throughput: ${(size / 1024 / (actualElapsed / 1000)).toFixed(2)} KB/s`);
|
|
|
|
// Small delay between messages
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
}
|
|
|
|
await client.close();
|
|
});
|
|
|
|
tap.test('CPERF-04: CPU usage with connection pooling', async (tools) => {
|
|
tools.timeout(30000);
|
|
|
|
console.log('\nTesting CPU usage with connection pooling...');
|
|
|
|
const pooledClient = await createPooledSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
maxConnections: 2, // Reduced
|
|
debug: false
|
|
});
|
|
|
|
const messageCount = 8; // Reduced
|
|
|
|
// Measure CPU with pooling
|
|
const cpuStart = process.cpuUsage();
|
|
const startTime = Date.now();
|
|
|
|
const promises = [];
|
|
for (let i = 0; i < messageCount; i++) {
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: [`recipient${i}@example.com`],
|
|
subject: `Pooled CPU test ${i + 1}`,
|
|
text: 'Testing CPU usage with connection pooling'
|
|
});
|
|
|
|
promises.push(pooledClient.sendMail(email));
|
|
}
|
|
|
|
await Promise.all(promises);
|
|
|
|
const elapsed = Date.now() - startTime;
|
|
const cpuEnd = process.cpuUsage(cpuStart);
|
|
const actualElapsed = Math.max(elapsed, 100);
|
|
const cpuPercent = Math.min(((cpuEnd.user + cpuEnd.system) / 1000) / actualElapsed * 100, 100);
|
|
|
|
await pooledClient.close();
|
|
|
|
console.log(`CPU usage for ${messageCount} messages with pooling:`);
|
|
console.log(` Total time: ${actualElapsed}ms`);
|
|
console.log(` CPU time: ${(cpuEnd.user + cpuEnd.system) / 1000}ms`);
|
|
console.log(` CPU usage: ${cpuPercent.toFixed(2)}%`);
|
|
|
|
// Pooling should complete successfully
|
|
expect(cpuPercent).toBeLessThan(100);
|
|
});
|
|
|
|
tap.test('CPERF-04: CPU profile over time', async (tools) => {
|
|
tools.timeout(30000);
|
|
|
|
console.log('\nTesting CPU profile over time...');
|
|
|
|
const client = await createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
debug: false
|
|
});
|
|
|
|
const duration = 8000; // 8 seconds (reduced)
|
|
const interval = 2000; // Sample every 2 seconds
|
|
const samples = [];
|
|
|
|
const endTime = Date.now() + duration;
|
|
let emailsSent = 0;
|
|
|
|
while (Date.now() < endTime) {
|
|
const sampleStart = Date.now();
|
|
const cpuStart = process.cpuUsage();
|
|
|
|
// Send some emails
|
|
for (let i = 0; i < 2; i++) { // Reduced from 3
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: ['recipient@example.com'],
|
|
subject: `CPU profile test ${++emailsSent}`,
|
|
text: `Testing CPU profile at ${new Date().toISOString()}`
|
|
});
|
|
|
|
await client.sendMail(email);
|
|
|
|
// Small delay between emails
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
}
|
|
|
|
const sampleElapsed = Date.now() - sampleStart;
|
|
const cpuEnd = process.cpuUsage(cpuStart);
|
|
const actualElapsed = Math.max(sampleElapsed, 100);
|
|
const cpuPercent = Math.min(((cpuEnd.user + cpuEnd.system) / 1000) / actualElapsed * 100, 100);
|
|
|
|
samples.push({
|
|
time: Date.now() - (endTime - duration),
|
|
cpu: cpuPercent,
|
|
emails: 2
|
|
});
|
|
|
|
console.log(`[${samples[samples.length - 1].time}ms] CPU: ${cpuPercent.toFixed(2)}%, Emails sent: ${emailsSent}`);
|
|
|
|
// Wait for next interval
|
|
const waitTime = interval - sampleElapsed;
|
|
if (waitTime > 0 && Date.now() + waitTime < endTime) {
|
|
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
}
|
|
}
|
|
|
|
await client.close();
|
|
|
|
// Calculate average CPU
|
|
const avgCpu = samples.reduce((sum, s) => sum + s.cpu, 0) / samples.length;
|
|
const maxCpu = Math.max(...samples.map(s => s.cpu));
|
|
const minCpu = Math.min(...samples.map(s => s.cpu));
|
|
|
|
console.log(`\nCPU profile summary:`);
|
|
console.log(` Samples: ${samples.length}`);
|
|
console.log(` Average CPU: ${avgCpu.toFixed(2)}%`);
|
|
console.log(` Min CPU: ${minCpu.toFixed(2)}%`);
|
|
console.log(` Max CPU: ${maxCpu.toFixed(2)}%`);
|
|
console.log(` Total emails: ${emailsSent}`);
|
|
|
|
// CPU should be bounded
|
|
expect(avgCpu).toBeLessThan(100); // Average CPU less than 100%
|
|
expect(maxCpu).toBeLessThan(100); // Max CPU less than 100%
|
|
});
|
|
|
|
tap.test('cleanup - stop SMTP server', async () => {
|
|
await stopTestServer(testServer);
|
|
});
|
|
|
|
export default tap.start(); |