dcrouter/test/suite/server.loader.ts
2025-05-23 19:49:25 +00:00

214 lines
5.3 KiB
TypeScript

/**
* Test server loader for SMTP test suite
* Provides simplified server lifecycle management for tests
*/
import { DcRouter } from '../../ts/classes.dcrouter.js';
import { UnifiedEmailServer } from '../../ts/mail/routing/classes.unified.email.server.js';
import { createSmtpServer } from '../../ts/mail/delivery/smtpserver/index.js';
import * as fs from 'fs';
import * as path from 'path';
import * as net from 'net';
let activeServer = null;
let activePort = null;
/**
* Start test server on default port 2525
*/
async function startTestServer(port = 2525) {
if (activeServer) {
console.log('Test server already running, stopping it first...');
await stopTestServer();
}
console.log(`Starting test SMTP server on port ${port}...`);
try {
// Create a minimal email server for testing
const mockEmailServer = {
processEmailByMode: async (emailData: any) => {
console.log('📧 Processed test email:', emailData.subject || 'No subject');
return emailData;
}
} as any; // Type assertion for test purposes
// Load test certificates if available
let key = '';
let cert = '';
try {
const __dirname = path.dirname(new URL(import.meta.url).pathname);
key = fs.readFileSync(path.join(__dirname, '../../../test/smtp-prod/certs/test.key'), 'utf8');
cert = fs.readFileSync(path.join(__dirname, '../../../test/smtp-prod/certs/test.cert'), 'utf8');
} catch (e) {
console.log('Test certificates not found, running without TLS');
}
// SMTP server options
const smtpOptions = {
port: port,
hostname: 'localhost',
key: key,
cert: cert,
maxConnections: 100,
size: 10 * 1024 * 1024, // 10MB
maxRecipients: 100,
socketTimeout: 30000,
connectionTimeout: 60000,
cleanupInterval: 300000,
auth: {
required: false,
methods: [] as ('PLAIN' | 'LOGIN' | 'OAUTH2')[]
}
};
// Create and start SMTP server
const smtpServer = createSmtpServer(mockEmailServer, smtpOptions);
await smtpServer.listen();
activeServer = smtpServer;
activePort = port;
// Wait for server to be ready
await waitForServerReady('localhost', port, 10000);
console.log(`✅ Test SMTP server started on port ${port}`);
return smtpServer;
} catch (error) {
console.error('Failed to start test server:', error);
throw error;
}
}
/**
* Stop test server
*/
async function stopTestServer() {
if (!activeServer) {
console.log('No active test server to stop');
return;
}
console.log(`Stopping test SMTP server on port ${activePort}...`);
try {
if (activeServer.close && typeof activeServer.close === 'function') {
await activeServer.close();
} else if (activeServer.destroy && typeof activeServer.destroy === 'function') {
await activeServer.destroy();
} else if (activeServer.stop && typeof activeServer.stop === 'function') {
await activeServer.stop();
}
// Force close any remaining connections
if (activeServer._connections) {
for (const conn of activeServer._connections) {
if (conn && !conn.destroyed) {
conn.destroy();
}
}
}
activeServer = null;
const port = activePort;
activePort = null;
// Wait for port to be free
await waitForPortFree(port, 3000);
console.log(`✅ Test SMTP server stopped`);
} catch (error) {
console.error('Error stopping test server:', error);
activeServer = null;
activePort = null;
}
}
/**
* Wait for server to be ready to accept connections
*/
async function waitForServerReady(hostname, port, timeout) {
const startTime = Date.now();
const maxRetries = 20;
let retries = 0;
while (retries < maxRetries) {
try {
await new Promise((resolve, reject) => {
const socket = net.createConnection({ port, host: hostname });
socket.on('connect', () => {
socket.end();
resolve(undefined);
});
socket.on('error', (error) => {
socket.destroy();
reject(error);
});
setTimeout(() => {
socket.destroy();
reject(new Error('Connection timeout'));
}, 1000);
});
return; // Server is ready
} catch (error) {
retries++;
if (Date.now() - startTime > timeout) {
throw new Error(`Server did not become ready within ${timeout}ms`);
}
// Wait before retrying
await new Promise(resolve => setTimeout(resolve, 500));
}
}
throw new Error(`Server did not become ready after ${maxRetries} retries`);
}
/**
* Wait for port to be free
*/
async function waitForPortFree(port, timeout) {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const isFree = await isPortFree(port);
if (isFree) {
return true;
}
await new Promise(resolve => setTimeout(resolve, 100));
}
return false;
}
/**
* Check if port is free
*/
async function isPortFree(port) {
return new Promise((resolve) => {
const server = net.createServer();
server.listen(port, () => {
server.close(() => {
resolve(true);
});
});
server.on('error', () => {
resolve(false);
});
});
}
// Export functions
export {
startTestServer,
stopTestServer
};