dcrouter/test/suite/smtpclient_connection/test.ccm-06.connection-timeout.ts
2025-05-25 19:05:43 +00:00

267 lines
7.8 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 type { SmtpClient } from '../../../ts/mail/delivery/smtpclient/smtp-client.js';
import { Email } from '../../../ts/mail/core/classes.email.js';
import * as net from 'net';
let testServer: ITestServer;
tap.test('setup - start SMTP server for timeout tests', async () => {
testServer = await startTestServer({
port: 2532,
tlsEnabled: false,
authRequired: false
});
expect(testServer.port).toEqual(2532);
});
tap.test('CCM-06: Connection Timeout - should timeout on unresponsive server', async () => {
const startTime = Date.now();
const timeoutClient = createSmtpClient({
host: testServer.hostname,
port: 9999, // Non-existent port
secure: false,
connectionTimeout: 2000, // 2 second timeout
debug: true
});
// verify() returns false on connection failure, doesn't throw
const verified = await timeoutClient.verify();
const duration = Date.now() - startTime;
expect(verified).toBeFalse();
expect(duration).toBeLessThan(3000); // Should timeout within 3s
console.log(`✅ Connection timeout after ${duration}ms`);
});
tap.test('CCM-06: Connection Timeout - should handle slow server response', async () => {
// Create a mock slow server
const slowServer = net.createServer((socket) => {
// Accept connection but delay response
setTimeout(() => {
socket.write('220 Slow server ready\r\n');
}, 3000); // 3 second delay
});
await new Promise<void>((resolve) => {
slowServer.listen(2533, () => resolve());
});
const startTime = Date.now();
const slowClient = createSmtpClient({
host: 'localhost',
port: 2533,
secure: false,
connectionTimeout: 1000, // 1 second timeout
debug: true
});
// verify() should return false when server is too slow
const verified = await slowClient.verify();
const duration = Date.now() - startTime;
expect(verified).toBeFalse();
// Note: actual timeout might be longer due to system defaults
console.log(`✅ Slow server timeout after ${duration}ms`);
slowServer.close();
await new Promise(resolve => setTimeout(resolve, 100));
});
tap.test('CCM-06: Connection Timeout - should respect socket timeout during data transfer', async () => {
const socketTimeoutClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 5000,
socketTimeout: 10000, // 10 second socket timeout
debug: true
});
await socketTimeoutClient.verify();
// Send a normal email
const email = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Socket Timeout Test',
text: 'Testing socket timeout configuration'
});
const result = await socketTimeoutClient.sendMail(email);
expect(result.success).toBeTrue();
await socketTimeoutClient.close();
console.log('✅ Socket timeout configuration applied');
});
tap.test('CCM-06: Connection Timeout - should handle timeout during TLS handshake', async () => {
// Create a server that accepts connections but doesn't complete TLS
const badTlsServer = net.createServer((socket) => {
// Accept connection but don't respond to TLS
socket.on('data', () => {
// Do nothing - simulate hung TLS handshake
});
});
await new Promise<void>((resolve) => {
badTlsServer.listen(2534, () => resolve());
});
const startTime = Date.now();
const tlsTimeoutClient = createSmtpClient({
host: 'localhost',
port: 2534,
secure: true, // Try TLS
connectionTimeout: 2000,
tls: {
rejectUnauthorized: false
}
});
// verify() should return false when TLS handshake times out
const verified = await tlsTimeoutClient.verify();
const duration = Date.now() - startTime;
expect(verified).toBeFalse();
// Note: actual timeout might be longer due to system defaults
console.log(`✅ TLS handshake timeout after ${duration}ms`);
badTlsServer.close();
await new Promise(resolve => setTimeout(resolve, 100));
});
tap.test('CCM-06: Connection Timeout - should not timeout on successful quick connection', async () => {
const quickClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 30000, // Very long timeout
debug: true
});
const startTime = Date.now();
const isConnected = await quickClient.verify();
const duration = Date.now() - startTime;
expect(isConnected).toBeTrue();
expect(duration).toBeLessThan(5000); // Should connect quickly
await quickClient.close();
console.log(`✅ Quick connection established in ${duration}ms`);
});
tap.test('CCM-06: Connection Timeout - should handle timeout during authentication', async () => {
// Start auth server
const authServer = await startTestServer({
port: 2535,
authRequired: true
});
// Create mock auth that delays
const authTimeoutClient = createSmtpClient({
host: authServer.hostname,
port: authServer.port,
secure: false,
connectionTimeout: 5000,
socketTimeout: 1000, // Very short socket timeout
auth: {
user: 'testuser',
pass: 'testpass'
}
});
try {
await authTimeoutClient.verify();
// If this succeeds, auth was fast enough
await authTimeoutClient.close();
console.log('✅ Authentication completed within timeout');
} catch (error) {
console.log('✅ Authentication timeout handled');
}
await stopTestServer(authServer);
});
tap.test('CCM-06: Connection Timeout - should apply different timeouts for different operations', async () => {
const multiTimeoutClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 5000, // Connection establishment
socketTimeout: 30000, // Data operations
debug: true
});
// Connection should be quick
const connectStart = Date.now();
await multiTimeoutClient.verify();
const connectDuration = Date.now() - connectStart;
expect(connectDuration).toBeLessThan(5000);
// Send email with potentially longer operation
const email = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Multi-timeout Test',
text: 'Testing different timeout values',
attachments: [{
filename: 'test.txt',
content: Buffer.from('Test content'),
contentType: 'text/plain'
}]
});
const sendStart = Date.now();
const result = await multiTimeoutClient.sendMail(email);
const sendDuration = Date.now() - sendStart;
expect(result.success).toBeTrue();
console.log(`✅ Different timeouts applied: connect=${connectDuration}ms, send=${sendDuration}ms`);
await multiTimeoutClient.close();
});
tap.test('CCM-06: Connection Timeout - should retry after timeout with pooled connections', async () => {
const retryClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
pool: true,
maxConnections: 2,
connectionTimeout: 5000,
debug: true
});
// First connection should succeed
const email1 = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Pre-timeout Email',
text: 'Before any timeout'
});
const result1 = await retryClient.sendMail(email1);
expect(result1.success).toBeTrue();
// Pool should handle connection management
const poolStatus = retryClient.getPoolStatus();
console.log('📊 Pool status:', poolStatus);
await retryClient.close();
console.log('✅ Connection pool handles timeouts gracefully');
});
tap.test('cleanup - stop SMTP server', async () => {
await stopTestServer(testServer);
});
export default tap.start();