670 lines
23 KiB
TypeScript
670 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-04: should optimize CPU utilization', async (tools) => {
|
|
const testId = 'CPERF-04-cpu-utilization';
|
|
console.log(`\n${testId}: Testing CPU utilization optimization...`);
|
|
|
|
let scenarioCount = 0;
|
|
|
|
// Helper function to measure CPU usage (simplified)
|
|
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;
|
|
|
|
return {
|
|
user: end.user / 1000, // Convert to milliseconds
|
|
system: end.system / 1000,
|
|
total: (end.user + end.system) / 1000,
|
|
elapsed,
|
|
userPercent: (end.user / 1000) / elapsed * 100,
|
|
systemPercent: (end.system / 1000) / elapsed * 100,
|
|
totalPercent: ((end.user + end.system) / 1000) / elapsed * 100
|
|
};
|
|
};
|
|
|
|
// Scenario 1: CPU usage during connection establishment
|
|
await (async () => {
|
|
scenarioCount++;
|
|
console.log(`\nScenario ${scenarioCount}: Testing CPU usage during connection establishment`);
|
|
|
|
const testServer = await createTestServer({
|
|
onConnection: async (socket) => {
|
|
console.log(' [Server] Client connected');
|
|
socket.write('220 cpu.example.com ESMTP\r\n');
|
|
|
|
socket.on('data', (data) => {
|
|
const command = data.toString().trim();
|
|
|
|
if (command.startsWith('EHLO')) {
|
|
socket.write('250-cpu.example.com\r\n');
|
|
socket.write('250 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();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Measure CPU during multiple connection establishments
|
|
const connectionCount = 10;
|
|
console.log(` Establishing ${connectionCount} connections...`);
|
|
|
|
const startCpu = process.cpuUsage();
|
|
const startTime = Date.now();
|
|
|
|
const clients: any[] = [];
|
|
for (let i = 0; i < connectionCount; i++) {
|
|
const client = createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false
|
|
});
|
|
clients.push(client);
|
|
|
|
// Verify connection
|
|
try {
|
|
await client.verify();
|
|
} catch (error) {
|
|
// Connection verification might not be available
|
|
}
|
|
}
|
|
|
|
const endCpu = process.cpuUsage(startCpu);
|
|
const elapsed = Date.now() - startTime;
|
|
|
|
const cpuUsage = {
|
|
user: endCpu.user / 1000,
|
|
system: endCpu.system / 1000,
|
|
total: (endCpu.user + endCpu.system) / 1000,
|
|
userPercent: (endCpu.user / 1000) / elapsed * 100,
|
|
systemPercent: (endCpu.system / 1000) / elapsed * 100,
|
|
totalPercent: ((endCpu.user + endCpu.system) / 1000) / elapsed * 100
|
|
};
|
|
|
|
console.log(` Connection establishment CPU usage:`);
|
|
console.log(` Total time: ${elapsed}ms`);
|
|
console.log(` User CPU: ${cpuUsage.user.toFixed(1)}ms (${cpuUsage.userPercent.toFixed(1)}%)`);
|
|
console.log(` System CPU: ${cpuUsage.system.toFixed(1)}ms (${cpuUsage.systemPercent.toFixed(1)}%)`);
|
|
console.log(` Total CPU: ${cpuUsage.total.toFixed(1)}ms (${cpuUsage.totalPercent.toFixed(1)}%)`);
|
|
console.log(` CPU per connection: ${(cpuUsage.total / connectionCount).toFixed(1)}ms`);
|
|
|
|
// Close all connections
|
|
await Promise.all(clients.map(client => {
|
|
if (client.close) {
|
|
return client.close();
|
|
}
|
|
return Promise.resolve();
|
|
}));
|
|
|
|
// CPU usage should be reasonable
|
|
expect(cpuUsage.totalPercent).toBeLessThan(50); // Less than 50% CPU usage
|
|
expect(cpuUsage.total / connectionCount).toBeLessThan(50); // Less than 50ms CPU per connection
|
|
|
|
await testServer.server.close();
|
|
})();
|
|
|
|
// Scenario 2: CPU usage during message composition
|
|
await (async () => {
|
|
scenarioCount++;
|
|
console.log(`\nScenario ${scenarioCount}: Testing CPU usage during message composition`);
|
|
|
|
const testServer = await createTestServer({
|
|
onConnection: async (socket) => {
|
|
socket.write('220 composition.example.com ESMTP\r\n');
|
|
|
|
socket.on('data', (data) => {
|
|
const command = data.toString().trim();
|
|
|
|
if (command.startsWith('EHLO')) {
|
|
socket.write('250-composition.example.com\r\n');
|
|
socket.write('250 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 smtpClient = createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false
|
|
});
|
|
|
|
// Test different message compositions
|
|
const compositionTests = [
|
|
{
|
|
name: 'Simple text',
|
|
email: new plugins.smartmail.Email({
|
|
from: 'sender@example.com',
|
|
to: ['recipient@example.com'],
|
|
subject: 'Simple text message',
|
|
text: 'This is a simple text message.'
|
|
})
|
|
},
|
|
{
|
|
name: 'HTML with formatting',
|
|
email: new plugins.smartmail.Email({
|
|
from: 'sender@example.com',
|
|
to: ['recipient@example.com'],
|
|
subject: 'HTML message',
|
|
html: '<h1>HTML Message</h1><p>This is an <strong>HTML</strong> message with <em>formatting</em>.</p>'
|
|
})
|
|
},
|
|
{
|
|
name: 'Multipart with text and HTML',
|
|
email: new plugins.smartmail.Email({
|
|
from: 'sender@example.com',
|
|
to: ['recipient@example.com'],
|
|
subject: 'Multipart message',
|
|
text: 'Plain text version',
|
|
html: '<p>HTML version</p>'
|
|
})
|
|
},
|
|
{
|
|
name: 'Message with small attachment',
|
|
email: new plugins.smartmail.Email({
|
|
from: 'sender@example.com',
|
|
to: ['recipient@example.com'],
|
|
subject: 'Message with attachment',
|
|
text: 'Message with small attachment',
|
|
attachments: [{
|
|
filename: 'test.txt',
|
|
content: 'This is a test attachment with some content.'
|
|
}]
|
|
})
|
|
}
|
|
];
|
|
|
|
for (const test of compositionTests) {
|
|
console.log(` Testing ${test.name}...`);
|
|
|
|
const startCpu = process.cpuUsage();
|
|
const startTime = Date.now();
|
|
|
|
await smtpClient.sendMail(test.email);
|
|
|
|
const endCpu = process.cpuUsage(startCpu);
|
|
const elapsed = Date.now() - startTime;
|
|
|
|
const cpuUsage = {
|
|
user: endCpu.user / 1000,
|
|
system: endCpu.system / 1000,
|
|
total: (endCpu.user + endCpu.system) / 1000,
|
|
totalPercent: ((endCpu.user + endCpu.system) / 1000) / elapsed * 100
|
|
};
|
|
|
|
console.log(` ${test.name}: ${cpuUsage.total.toFixed(1)}ms CPU (${cpuUsage.totalPercent.toFixed(1)}%)`);
|
|
|
|
// CPU usage should be efficient for message composition
|
|
expect(cpuUsage.totalPercent).toBeLessThan(25); // Less than 25% CPU
|
|
expect(cpuUsage.total).toBeLessThan(100); // Less than 100ms CPU time
|
|
}
|
|
|
|
await testServer.server.close();
|
|
})();
|
|
|
|
// Scenario 3: CPU usage with concurrent operations
|
|
await (async () => {
|
|
scenarioCount++;
|
|
console.log(`\nScenario ${scenarioCount}: Testing CPU usage with concurrent operations`);
|
|
|
|
const testServer = await createTestServer({
|
|
onConnection: async (socket) => {
|
|
socket.write('220 concurrent.example.com ESMTP\r\n');
|
|
|
|
socket.on('data', (data) => {
|
|
const command = data.toString().trim();
|
|
|
|
if (command.startsWith('EHLO')) {
|
|
socket.write('250-concurrent.example.com\r\n');
|
|
socket.write('250 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 smtpClient = createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false
|
|
});
|
|
|
|
// Test sequential vs concurrent CPU usage
|
|
const messageCount = 20;
|
|
const emails = Array(messageCount).fill(null).map((_, i) =>
|
|
new plugins.smartmail.Email({
|
|
from: 'sender@example.com',
|
|
to: [`recipient${i + 1}@example.com`],
|
|
subject: `CPU test message ${i + 1}`,
|
|
text: `Testing CPU utilization - message ${i + 1}`
|
|
})
|
|
);
|
|
|
|
// Sequential sending
|
|
console.log(` Sequential sending of ${messageCount} messages...`);
|
|
const sequentialStartCpu = process.cpuUsage();
|
|
const sequentialStartTime = Date.now();
|
|
|
|
for (const email of emails) {
|
|
await smtpClient.sendMail(email);
|
|
}
|
|
|
|
const sequentialEndCpu = process.cpuUsage(sequentialStartCpu);
|
|
const sequentialElapsed = Date.now() - sequentialStartTime;
|
|
|
|
const sequentialCpu = {
|
|
total: (sequentialEndCpu.user + sequentialEndCpu.system) / 1000,
|
|
totalPercent: ((sequentialEndCpu.user + sequentialEndCpu.system) / 1000) / sequentialElapsed * 100
|
|
};
|
|
|
|
console.log(` Sequential: ${sequentialCpu.total.toFixed(1)}ms CPU (${sequentialCpu.totalPercent.toFixed(1)}%)`);
|
|
console.log(` Per message: ${(sequentialCpu.total / messageCount).toFixed(1)}ms CPU`);
|
|
|
|
// Concurrent sending (new emails)
|
|
const concurrentEmails = Array(messageCount).fill(null).map((_, i) =>
|
|
new plugins.smartmail.Email({
|
|
from: 'sender@example.com',
|
|
to: [`concurrent${i + 1}@example.com`],
|
|
subject: `Concurrent CPU test ${i + 1}`,
|
|
text: `Testing concurrent CPU utilization - message ${i + 1}`
|
|
})
|
|
);
|
|
|
|
console.log(` Concurrent sending of ${messageCount} messages...`);
|
|
const concurrentStartCpu = process.cpuUsage();
|
|
const concurrentStartTime = Date.now();
|
|
|
|
await Promise.all(concurrentEmails.map(email => smtpClient.sendMail(email)));
|
|
|
|
const concurrentEndCpu = process.cpuUsage(concurrentStartCpu);
|
|
const concurrentElapsed = Date.now() - concurrentStartTime;
|
|
|
|
const concurrentCpu = {
|
|
total: (concurrentEndCpu.user + concurrentEndCpu.system) / 1000,
|
|
totalPercent: ((concurrentEndCpu.user + concurrentEndCpu.system) / 1000) / concurrentElapsed * 100
|
|
};
|
|
|
|
console.log(` Concurrent: ${concurrentCpu.total.toFixed(1)}ms CPU (${concurrentCpu.totalPercent.toFixed(1)}%)`);
|
|
console.log(` Per message: ${(concurrentCpu.total / messageCount).toFixed(1)}ms CPU`);
|
|
|
|
// Compare efficiency
|
|
const efficiency = sequentialCpu.total / concurrentCpu.total;
|
|
console.log(` CPU efficiency ratio: ${efficiency.toFixed(2)}x`);
|
|
|
|
// Concurrent should be more CPU efficient (higher throughput)
|
|
expect(concurrentElapsed).toBeLessThan(sequentialElapsed);
|
|
expect(concurrentCpu.totalPercent).toBeLessThan(80); // Less than 80% CPU
|
|
|
|
await testServer.server.close();
|
|
})();
|
|
|
|
// Scenario 4: CPU usage with large attachments
|
|
await (async () => {
|
|
scenarioCount++;
|
|
console.log(`\nScenario ${scenarioCount}: Testing CPU usage with large attachments`);
|
|
|
|
const testServer = await createTestServer({
|
|
onConnection: async (socket) => {
|
|
socket.write('220 attachments.example.com ESMTP\r\n');
|
|
|
|
let inData = false;
|
|
let dataSize = 0;
|
|
|
|
socket.on('data', (data) => {
|
|
if (inData) {
|
|
dataSize += data.length;
|
|
if (data.toString().includes('\r\n.\r\n')) {
|
|
inData = false;
|
|
console.log(` [Server] Received ${(dataSize / 1024).toFixed(1)}KB`);
|
|
socket.write('250 OK\r\n');
|
|
dataSize = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
const command = data.toString().trim();
|
|
|
|
if (command.startsWith('EHLO')) {
|
|
socket.write('250-attachments.example.com\r\n');
|
|
socket.write('250 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;
|
|
} else if (command === 'QUIT') {
|
|
socket.write('221 Bye\r\n');
|
|
socket.end();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
const smtpClient = createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false
|
|
});
|
|
|
|
// Test different attachment sizes
|
|
const attachmentSizes = [
|
|
{ name: '10KB', size: 10 * 1024 },
|
|
{ name: '100KB', size: 100 * 1024 },
|
|
{ name: '1MB', size: 1024 * 1024 }
|
|
];
|
|
|
|
for (const attachSize of attachmentSizes) {
|
|
console.log(` Testing ${attachSize.name} attachment...`);
|
|
|
|
// Create binary attachment
|
|
const attachmentData = Buffer.alloc(attachSize.size);
|
|
for (let i = 0; i < attachmentData.length; i++) {
|
|
attachmentData[i] = i % 256;
|
|
}
|
|
|
|
const email = new plugins.smartmail.Email({
|
|
from: 'sender@example.com',
|
|
to: ['recipient@example.com'],
|
|
subject: `CPU test with ${attachSize.name} attachment`,
|
|
text: `Testing CPU usage with ${attachSize.name} attachment`,
|
|
attachments: [{
|
|
filename: `${attachSize.name}-file.bin`,
|
|
content: attachmentData
|
|
}]
|
|
});
|
|
|
|
const startCpu = process.cpuUsage();
|
|
const startTime = Date.now();
|
|
|
|
await smtpClient.sendMail(email);
|
|
|
|
const endCpu = process.cpuUsage(startCpu);
|
|
const elapsed = Date.now() - startTime;
|
|
|
|
const cpuUsage = {
|
|
total: (endCpu.user + endCpu.system) / 1000,
|
|
totalPercent: ((endCpu.user + endCpu.system) / 1000) / elapsed * 100
|
|
};
|
|
|
|
const cpuPerKB = cpuUsage.total / (attachSize.size / 1024);
|
|
|
|
console.log(` ${attachSize.name}: ${cpuUsage.total.toFixed(1)}ms CPU (${cpuUsage.totalPercent.toFixed(1)}%)`);
|
|
console.log(` CPU per KB: ${cpuPerKB.toFixed(3)}ms/KB`);
|
|
|
|
// CPU usage should scale reasonably with attachment size
|
|
expect(cpuUsage.totalPercent).toBeLessThan(50);
|
|
expect(cpuPerKB).toBeLessThan(1); // Less than 1ms CPU per KB
|
|
}
|
|
|
|
await testServer.server.close();
|
|
})();
|
|
|
|
// Scenario 5: CPU usage with connection pooling
|
|
await (async () => {
|
|
scenarioCount++;
|
|
console.log(`\nScenario ${scenarioCount}: Testing CPU usage with connection pooling`);
|
|
|
|
let connectionCount = 0;
|
|
|
|
const testServer = await createTestServer({
|
|
onConnection: async (socket) => {
|
|
connectionCount++;
|
|
socket.write('220 pool.example.com ESMTP\r\n');
|
|
|
|
socket.on('data', (data) => {
|
|
const command = data.toString().trim();
|
|
|
|
if (command.startsWith('EHLO')) {
|
|
socket.write('250-pool.example.com\r\n');
|
|
socket.write('250 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();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Compare individual connections vs pooled
|
|
const messageCount = 15;
|
|
|
|
// Individual connections
|
|
console.log(` Testing ${messageCount} individual connections...`);
|
|
connectionCount = 0;
|
|
const individualStartCpu = process.cpuUsage();
|
|
const individualStartTime = Date.now();
|
|
|
|
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 connection - message ${i + 1}`
|
|
});
|
|
|
|
await client.sendMail(email);
|
|
|
|
if (client.close) {
|
|
await client.close();
|
|
}
|
|
}
|
|
|
|
const individualEndCpu = process.cpuUsage(individualStartCpu);
|
|
const individualElapsed = Date.now() - individualStartTime;
|
|
const individualConnections = connectionCount;
|
|
|
|
const individualCpu = {
|
|
total: (individualEndCpu.user + individualEndCpu.system) / 1000,
|
|
totalPercent: ((individualEndCpu.user + individualEndCpu.system) / 1000) / individualElapsed * 100
|
|
};
|
|
|
|
console.log(` Individual: ${individualCpu.total.toFixed(1)}ms CPU, ${individualConnections} connections`);
|
|
|
|
// Pooled connections
|
|
console.log(` Testing pooled connections...`);
|
|
connectionCount = 0;
|
|
const pooledStartCpu = process.cpuUsage();
|
|
const pooledStartTime = Date.now();
|
|
|
|
const pooledClient = createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
pool: true,
|
|
maxConnections: 3,
|
|
maxMessages: 100
|
|
});
|
|
|
|
const pooledEmails = Array(messageCount).fill(null).map((_, i) =>
|
|
new plugins.smartmail.Email({
|
|
from: 'sender@example.com',
|
|
to: [`pooled${i + 1}@example.com`],
|
|
subject: `Pooled connection test ${i + 1}`,
|
|
text: `Testing pooled connection - message ${i + 1}`
|
|
})
|
|
);
|
|
|
|
await Promise.all(pooledEmails.map(email => pooledClient.sendMail(email)));
|
|
await pooledClient.close();
|
|
|
|
const pooledEndCpu = process.cpuUsage(pooledStartCpu);
|
|
const pooledElapsed = Date.now() - pooledStartTime;
|
|
const pooledConnections = connectionCount;
|
|
|
|
const pooledCpu = {
|
|
total: (pooledEndCpu.user + pooledEndCpu.system) / 1000,
|
|
totalPercent: ((pooledEndCpu.user + pooledEndCpu.system) / 1000) / pooledElapsed * 100
|
|
};
|
|
|
|
console.log(` Pooled: ${pooledCpu.total.toFixed(1)}ms CPU, ${pooledConnections} connections`);
|
|
|
|
const cpuEfficiency = individualCpu.total / pooledCpu.total;
|
|
const connectionEfficiency = individualConnections / pooledConnections;
|
|
|
|
console.log(` CPU efficiency: ${cpuEfficiency.toFixed(2)}x`);
|
|
console.log(` Connection efficiency: ${connectionEfficiency.toFixed(2)}x`);
|
|
|
|
// Pooling should be more CPU efficient
|
|
expect(pooledCpu.total).toBeLessThan(individualCpu.total);
|
|
expect(pooledConnections).toBeLessThan(individualConnections);
|
|
expect(cpuEfficiency).toBeGreaterThan(1.2); // At least 20% more efficient
|
|
|
|
await testServer.server.close();
|
|
})();
|
|
|
|
// Scenario 6: CPU usage under stress
|
|
await (async () => {
|
|
scenarioCount++;
|
|
console.log(`\nScenario ${scenarioCount}: Testing CPU usage under stress`);
|
|
|
|
let messageCount = 0;
|
|
|
|
const testServer = await createTestServer({
|
|
onConnection: async (socket) => {
|
|
socket.write('220 stress.example.com ESMTP\r\n');
|
|
|
|
socket.on('data', (data) => {
|
|
const command = data.toString().trim();
|
|
|
|
if (command.startsWith('EHLO')) {
|
|
socket.write('250-stress.example.com\r\n');
|
|
socket.write('250 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 === '.') {
|
|
messageCount++;
|
|
socket.write(`250 OK: Message ${messageCount}\r\n`);
|
|
} else if (command === 'QUIT') {
|
|
socket.write('221 Bye\r\n');
|
|
socket.end();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
const pooledClient = createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
pool: true,
|
|
maxConnections: 5,
|
|
maxMessages: 100
|
|
});
|
|
|
|
// Stress test with many messages
|
|
const stressMessageCount = 50;
|
|
const stressEmails = Array(stressMessageCount).fill(null).map((_, i) =>
|
|
new plugins.smartmail.Email({
|
|
from: 'sender@example.com',
|
|
to: [`stress${i + 1}@example.com`],
|
|
subject: `Stress test message ${i + 1}`,
|
|
text: `Testing CPU under stress - message ${i + 1}`
|
|
})
|
|
);
|
|
|
|
console.log(` Stress testing with ${stressMessageCount} concurrent messages...`);
|
|
|
|
const stressStartCpu = process.cpuUsage();
|
|
const stressStartTime = Date.now();
|
|
|
|
// Monitor CPU usage during stress test
|
|
const cpuSamples: number[] = [];
|
|
const sampleInterval = setInterval(() => {
|
|
const currentCpu = process.cpuUsage(stressStartCpu);
|
|
const currentElapsed = Date.now() - stressStartTime;
|
|
const currentPercent = ((currentCpu.user + currentCpu.system) / 1000) / currentElapsed * 100;
|
|
cpuSamples.push(currentPercent);
|
|
}, 100);
|
|
|
|
await Promise.all(stressEmails.map(email => pooledClient.sendMail(email)));
|
|
|
|
clearInterval(sampleInterval);
|
|
|
|
const stressEndCpu = process.cpuUsage(stressStartCpu);
|
|
const stressElapsed = Date.now() - stressStartTime;
|
|
|
|
const stressCpu = {
|
|
total: (stressEndCpu.user + stressEndCpu.system) / 1000,
|
|
totalPercent: ((stressEndCpu.user + stressEndCpu.system) / 1000) / stressElapsed * 100
|
|
};
|
|
|
|
const maxCpuSample = Math.max(...cpuSamples);
|
|
const avgCpuSample = cpuSamples.reduce((a, b) => a + b, 0) / cpuSamples.length;
|
|
|
|
console.log(` Stress test results:`);
|
|
console.log(` Total CPU: ${stressCpu.total.toFixed(1)}ms (${stressCpu.totalPercent.toFixed(1)}%)`);
|
|
console.log(` Peak CPU: ${maxCpuSample.toFixed(1)}%`);
|
|
console.log(` Average CPU: ${avgCpuSample.toFixed(1)}%`);
|
|
console.log(` Messages per CPU ms: ${(stressMessageCount / stressCpu.total).toFixed(2)}`);
|
|
console.log(` Throughput: ${(stressMessageCount / stressElapsed * 1000).toFixed(1)} msg/sec`);
|
|
|
|
await pooledClient.close();
|
|
|
|
// CPU usage should remain reasonable under stress
|
|
expect(stressCpu.totalPercent).toBeLessThan(90); // Less than 90% CPU
|
|
expect(maxCpuSample).toBeLessThan(100); // No sustained 100% CPU
|
|
expect(stressMessageCount / stressCpu.total).toBeGreaterThan(0.1); // At least 0.1 msg/ms efficiency
|
|
|
|
await testServer.server.close();
|
|
})();
|
|
|
|
console.log(`\n${testId}: All ${scenarioCount} CPU utilization scenarios tested ✓`);
|
|
}); |