214 lines
5.3 KiB
TypeScript
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
|
|
}; |