import { tap, expect } from '@git.zone/tstest/tapbundle'; import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.js'; import * as plugins from '../../../ts/plugins.js'; const TEST_PORT = 2525; let testServer: ITestServer; tap.test('setup - start SMTP server with short timeout', async () => { testServer = await startTestServer({ port: TEST_PORT, timeout: 5000 // 5 second timeout for this test }); await new Promise(resolve => setTimeout(resolve, 1000)); }); tap.test('CM-03: Connection Timeout - idle connections are closed after timeout', async (tools) => { const startTime = Date.now(); // Create connection const socket = await new Promise((resolve, reject) => { const client = plugins.net.createConnection({ host: testServer.hostname, port: testServer.port }); client.on('connect', () => resolve(client)); client.on('error', reject); setTimeout(() => reject(new Error('Connection timeout')), 3000); }); // Wait for greeting await new Promise((resolve) => { socket.once('data', (data) => { const response = data.toString(); expect(response).toInclude('220'); resolve(); }); }); console.log('✅ Connected and received greeting'); // Now stay idle and wait for server to timeout the connection const disconnectPromise = new Promise((resolve) => { socket.on('close', () => { const duration = Date.now() - startTime; resolve(duration); }); socket.on('end', () => { console.log('📡 Server initiated connection close'); }); socket.on('error', (err) => { console.log('⚠️ Socket error:', err.message); }); }); // Wait for timeout (should be around 5 seconds) const duration = await disconnectPromise; console.log(`⏱️ Connection closed after ${duration}ms`); // Verify timeout happened within expected range (4-6 seconds) expect(duration).toBeGreaterThan(4000); expect(duration).toBeLessThan(7000); console.log('✅ Connection timeout test passed'); }); tap.test('CM-03: Active connection should not timeout', async () => { // Create new connection const socket = plugins.net.createConnection({ host: testServer.hostname, port: testServer.port }); await new Promise((resolve) => { socket.on('connect', resolve); }); // Wait for greeting await new Promise((resolve) => { socket.once('data', resolve); }); // Keep connection active with NOOP commands let isConnected = true; socket.on('close', () => { isConnected = false; }); // Send NOOP every 2 seconds for 8 seconds for (let i = 0; i < 4; i++) { if (!isConnected) break; socket.write('NOOP\r\n'); // Wait for response await new Promise((resolve) => { socket.once('data', (data) => { const response = data.toString(); expect(response).toInclude('250'); resolve(); }); }); console.log(`✅ NOOP ${i + 1}/4 successful`); // Wait 2 seconds before next NOOP await new Promise(resolve => setTimeout(resolve, 2000)); } // Connection should still be active expect(isConnected).toEqual(true); // Close connection gracefully socket.write('QUIT\r\n'); await new Promise((resolve) => { socket.once('data', () => { socket.end(); resolve(); }); }); console.log('✅ Active connection did not timeout'); }); tap.test('cleanup - stop SMTP server', async () => { await stopTestServer(testServer); }); tap.start();