dcrouter/test/suite/smtpclient_edge-cases/test.cedge-07.concurrent-operations.ts
2025-05-26 04:09:29 +00:00

204 lines
6.2 KiB
TypeScript

import { tap, expect } from '@git.zone/tstest/tapbundle';
import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.js';
import { createSmtpClient } from '../../../ts/mail/delivery/smtpclient/index.js';
import { Email } from '../../../ts/mail/core/classes.email.js';
let testServer: ITestServer;
tap.test('setup test SMTP server', async () => {
testServer = await startTestServer({
port: 2576,
tlsEnabled: false,
authRequired: false,
maxConnections: 20 // Allow more connections for concurrent testing
});
expect(testServer).toBeTruthy();
expect(testServer.port).toEqual(2576);
});
tap.test('CEDGE-07: Multiple simultaneous connections', async () => {
console.log('Testing multiple simultaneous connections');
const connectionCount = 5;
const clients = [];
// Create multiple clients
for (let i = 0; i < connectionCount; i++) {
const client = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 5000,
debug: false, // Reduce noise
maxConnections: 2
});
clients.push(client);
}
// Test concurrent verification
console.log(` Testing ${connectionCount} concurrent verifications...`);
const verifyPromises = clients.map(async (client, index) => {
try {
const result = await client.verify();
console.log(` Client ${index + 1}: ${result ? 'Success' : 'Failed'}`);
return result;
} catch (error) {
console.log(` Client ${index + 1}: Error - ${error.message}`);
return false;
}
});
const verifyResults = await Promise.all(verifyPromises);
const successCount = verifyResults.filter(r => r).length;
console.log(` Verify results: ${successCount}/${connectionCount} successful`);
// We expect at least some connections to succeed
expect(successCount).toBeGreaterThan(0);
// Clean up clients
await Promise.all(clients.map(client => client.close().catch(() => {})));
});
tap.test('CEDGE-07: Concurrent email sending', async () => {
console.log('Testing concurrent email sending');
const smtpClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 5000,
debug: false,
maxConnections: 5
});
const emailCount = 10;
console.log(` Sending ${emailCount} emails concurrently...`);
const sendPromises = [];
for (let i = 0; i < emailCount; i++) {
const email = new Email({
from: 'sender@example.com',
to: [`recipient${i}@example.com`],
subject: `Concurrent test email ${i + 1}`,
text: `This is concurrent test email number ${i + 1}`
});
sendPromises.push(
smtpClient.sendMail(email).then(
result => {
console.log(` Email ${i + 1}: Success`);
return { success: true, result };
},
error => {
console.log(` Email ${i + 1}: Failed - ${error.message}`);
return { success: false, error };
}
)
);
}
const results = await Promise.all(sendPromises);
const successCount = results.filter(r => r.success).length;
console.log(` Send results: ${successCount}/${emailCount} successful`);
// We expect a high success rate
expect(successCount).toBeGreaterThan(emailCount * 0.7); // At least 70% success
await smtpClient.close();
});
tap.test('CEDGE-07: Rapid connection cycling', async () => {
console.log('Testing rapid connection cycling');
const cycleCount = 8;
console.log(` Performing ${cycleCount} rapid connect/disconnect cycles...`);
const cyclePromises = [];
for (let i = 0; i < cycleCount; i++) {
cyclePromises.push(
(async () => {
const client = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 3000,
debug: false
});
try {
const verified = await client.verify();
console.log(` Cycle ${i + 1}: ${verified ? 'Success' : 'Failed'}`);
await client.close();
return verified;
} catch (error) {
console.log(` Cycle ${i + 1}: Error - ${error.message}`);
await client.close().catch(() => {});
return false;
}
})()
);
}
const cycleResults = await Promise.all(cyclePromises);
const successCount = cycleResults.filter(r => r).length;
console.log(` Cycle results: ${successCount}/${cycleCount} successful`);
// We expect most cycles to succeed
expect(successCount).toBeGreaterThan(cycleCount * 0.6); // At least 60% success
});
tap.test('CEDGE-07: Connection pool stress test', async () => {
console.log('Testing connection pool under stress');
const smtpClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 5000,
debug: false,
maxConnections: 3,
maxMessages: 50
});
const stressCount = 15;
console.log(` Sending ${stressCount} emails to stress connection pool...`);
const startTime = Date.now();
const stressPromises = [];
for (let i = 0; i < stressCount; i++) {
const email = new Email({
from: 'stress@example.com',
to: [`stress${i}@example.com`],
subject: `Stress test ${i + 1}`,
text: `Connection pool stress test email ${i + 1}`
});
stressPromises.push(
smtpClient.sendMail(email).then(
result => ({ success: true, index: i }),
error => ({ success: false, index: i, error: error.message })
)
);
}
const stressResults = await Promise.all(stressPromises);
const duration = Date.now() - startTime;
const successCount = stressResults.filter(r => r.success).length;
console.log(` Stress results: ${successCount}/${stressCount} successful in ${duration}ms`);
console.log(` Average: ${Math.round(duration / stressCount)}ms per email`);
// Under stress, we still expect reasonable success rate
expect(successCount).toBeGreaterThan(stressCount * 0.5); // At least 50% success under stress
await smtpClient.close();
});
tap.test('cleanup test SMTP server', async () => {
if (testServer) {
await stopTestServer(testServer);
}
});
export default tap.start();