update
This commit is contained in:
@ -0,0 +1,670 @@
|
||||
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 ✓`);
|
||||
});
|
Reference in New Issue
Block a user