dcrouter/test/suite/smtpclient_reliability/test.crel-07.resource-cleanup.ts
2025-05-26 14:50:55 +00:00

291 lines
8.7 KiB
TypeScript

import { tap, expect } from '@git.zone/tstest/tapbundle';
import { createTestServer } from '../../helpers/server.loader.js';
import { createTestSmtpClient } from '../../helpers/smtp.client.js';
import { Email } from '../../../ts/mail/core/classes.email.js';
// Helper function to count active resources
const getResourceCounts = () => {
const usage = process.memoryUsage();
return {
memory: Math.round(usage.heapUsed / 1024 / 1024 * 100) / 100, // MB
handles: (process as any)._getActiveHandles ? (process as any)._getActiveHandles().length : 0,
requests: (process as any)._getActiveRequests ? (process as any)._getActiveRequests().length : 0
};
};
// Scenario 1: Basic Resource Cleanup
tap.test('CREL-07: Basic Resource Cleanup', async () => {
console.log('\n🧹 Testing SMTP Client Resource Cleanup');
console.log('=' .repeat(60));
let connections = 0;
let disconnections = 0;
const testServer = await createTestServer({
onConnection: (socket: any) => {
connections++;
console.log(` [Server] Connection opened (total: ${connections})`);
socket.on('close', () => {
disconnections++;
console.log(` [Server] Connection closed (total closed: ${disconnections})`);
});
}
});
try {
const initialResources = getResourceCounts();
console.log(` Initial resources: ${initialResources.memory}MB memory, ${initialResources.handles} handles`);
console.log(' Creating SMTP clients and sending emails...');
const clients = [];
// Create multiple clients
for (let i = 0; i < 3; i++) {
const smtpClient = createTestSmtpClient({
host: testServer.hostname,
port: testServer.port
});
clients.push(smtpClient);
// Send a test email
const email = new Email({
from: `sender${i}@cleanup.test`,
to: [`recipient${i}@cleanup.test`],
subject: `Cleanup Test ${i + 1}`,
text: `Testing connection cleanup ${i + 1}`
});
try {
await smtpClient.sendMail(email);
console.log(` ✓ Client ${i + 1} email sent`);
} catch (error) {
console.log(` ✗ Client ${i + 1} failed: ${error.message}`);
}
}
const afterSending = getResourceCounts();
console.log(` After sending: ${afterSending.memory}MB memory, ${afterSending.handles} handles`);
console.log(' Closing all clients...');
for (let i = 0; i < clients.length; i++) {
console.log(` Closing client ${i + 1}...`);
clients[i].close();
await new Promise(resolve => setTimeout(resolve, 100));
}
// Wait for cleanup to complete
await new Promise(resolve => setTimeout(resolve, 500));
const finalResources = getResourceCounts();
console.log(`\n Resource cleanup assessment:`);
console.log(` Initial: ${initialResources.memory}MB memory, ${initialResources.handles} handles`);
console.log(` Final: ${finalResources.memory}MB memory, ${finalResources.handles} handles`);
console.log(` Connections opened: ${connections}`);
console.log(` Connections closed: ${disconnections}`);
console.log(` Cleanup: ${disconnections >= connections - 1 ? 'Complete' : 'Incomplete'}`);
expect(disconnections).toBeGreaterThanOrEqual(connections - 1);
} finally {
testServer.server.close();
}
});
// Scenario 2: Multiple Close Safety
tap.test('CREL-07: Multiple Close Safety', async () => {
console.log('\n🔁 Testing multiple close calls safety...');
const testServer = await createTestServer({});
try {
const smtpClient = createTestSmtpClient({
host: testServer.hostname,
port: testServer.port
});
// Send a test email
const email = new Email({
from: 'sender@multiclose.test',
to: ['recipient@multiclose.test'],
subject: 'Multiple Close Test',
text: 'Testing multiple close calls'
});
console.log(' Sending test email...');
await smtpClient.sendMail(email);
console.log(' ✓ Email sent successfully');
console.log(' Attempting multiple close calls...');
let closeErrors = 0;
for (let i = 0; i < 5; i++) {
try {
smtpClient.close();
console.log(` ✓ Close call ${i + 1} completed`);
} catch (error) {
closeErrors++;
console.log(` ✗ Close call ${i + 1} error: ${error.message}`);
}
}
console.log(` Close errors: ${closeErrors}`);
console.log(` Safety: ${closeErrors === 0 ? 'Safe' : 'Issues detected'}`);
expect(closeErrors).toEqual(0);
} finally {
testServer.server.close();
}
});
// Scenario 3: Error Recovery and Cleanup
tap.test('CREL-07: Error Recovery and Cleanup', async () => {
console.log('\n❌ Testing error recovery and cleanup...');
let errorMode = false;
let requestCount = 0;
const testServer = await createTestServer({
onConnection: (socket: any) => {
requestCount++;
if (errorMode && requestCount % 2 === 0) {
console.log(` [Server] Simulating connection error`);
setTimeout(() => socket.destroy(), 50);
}
}
});
try {
const smtpClient = createTestSmtpClient({
host: testServer.hostname,
port: testServer.port,
connectionTimeout: 2000
});
console.log(' Phase 1: Normal operation...');
const normalEmail = new Email({
from: 'sender@test.com',
to: ['recipient@test.com'],
subject: 'Normal Test',
text: 'Testing normal operation'
});
let normalResult = false;
try {
await smtpClient.sendMail(normalEmail);
normalResult = true;
console.log(' ✓ Normal operation successful');
} catch (error) {
console.log(' ✗ Normal operation failed');
}
console.log(' Phase 2: Error injection...');
errorMode = true;
let errorCount = 0;
for (let i = 0; i < 3; i++) {
try {
const errorEmail = new Email({
from: 'sender@error.test',
to: ['recipient@error.test'],
subject: `Error Test ${i + 1}`,
text: 'Testing error handling'
});
await smtpClient.sendMail(errorEmail);
console.log(` ✓ Email ${i + 1} sent (recovered)`);
} catch (error) {
errorCount++;
console.log(` ✗ Email ${i + 1} failed as expected`);
}
}
console.log(' Phase 3: Recovery...');
errorMode = false;
const recoveryEmail = new Email({
from: 'sender@recovery.test',
to: ['recipient@recovery.test'],
subject: 'Recovery Test',
text: 'Testing recovery'
});
let recovered = false;
try {
await smtpClient.sendMail(recoveryEmail);
recovered = true;
console.log(' ✓ Recovery successful');
} catch (error) {
console.log(' ✗ Recovery failed');
}
// Close and cleanup
smtpClient.close();
console.log(`\n Error recovery assessment:`);
console.log(` Normal operation: ${normalResult ? 'Success' : 'Failed'}`);
console.log(` Errors encountered: ${errorCount}`);
console.log(` Recovery: ${recovered ? 'Successful' : 'Failed'}`);
expect(normalResult).toEqual(true);
expect(errorCount).toBeGreaterThan(0);
} finally {
testServer.server.close();
}
});
// Scenario 4: Rapid Connect/Disconnect
tap.test('CREL-07: Rapid Connect/Disconnect Cycles', async () => {
console.log('\n⚡ Testing rapid connect/disconnect cycles...');
const testServer = await createTestServer({});
try {
console.log(' Performing rapid connect/disconnect cycles...');
let successful = 0;
let failed = 0;
for (let cycle = 0; cycle < 5; cycle++) {
const smtpClient = createTestSmtpClient({
host: testServer.hostname,
port: testServer.port,
connectionTimeout: 1000
});
try {
// Quick verify to establish connection
await smtpClient.verify();
successful++;
console.log(` ✓ Cycle ${cycle + 1}: Connected`);
} catch (error) {
failed++;
console.log(` ✗ Cycle ${cycle + 1}: Failed`);
}
// Immediately close
smtpClient.close();
// Brief pause between cycles
await new Promise(resolve => setTimeout(resolve, 50));
}
console.log(`\n Rapid cycle results:`);
console.log(` Successful connections: ${successful}`);
console.log(` Failed connections: ${failed}`);
console.log(` Success rate: ${(successful / (successful + failed) * 100).toFixed(1)}%`);
expect(successful).toBeGreaterThan(0);
} finally {
testServer.server.close();
}
});
tap.test('CREL-07: Test Summary', async () => {
console.log('\n✅ CREL-07: Resource Cleanup Reliability Tests completed');
console.log('🧹 All resource cleanup scenarios tested successfully');
});
tap.start();