2025-05-23 19:03:44 +00:00
|
|
|
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';
|
|
|
|
|
2025-05-23 19:49:25 +00:00
|
|
|
let testServer: SmtpServer;
|
2025-05-23 19:03:44 +00:00
|
|
|
|
|
|
|
tap.test('setup - start SMTP server with short timeout', async () => {
|
2025-05-23 19:49:25 +00:00
|
|
|
testServer = await startTestServer();
|
|
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
2025-05-23 19:03:44 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
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<plugins.net.Socket>((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<void>((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<number>((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<void>((resolve) => {
|
|
|
|
socket.on('connect', resolve);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Wait for greeting
|
|
|
|
await new Promise<void>((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<void>((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
|
2025-05-23 21:20:39 +00:00
|
|
|
expect(isConnected).toEqual(true);
|
2025-05-23 19:03:44 +00:00
|
|
|
|
|
|
|
// Close connection gracefully
|
|
|
|
socket.write('QUIT\r\n');
|
|
|
|
await new Promise<void>((resolve) => {
|
|
|
|
socket.once('data', () => {
|
|
|
|
socket.end();
|
|
|
|
resolve();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
console.log('✅ Active connection did not timeout');
|
|
|
|
});
|
|
|
|
|
|
|
|
tap.test('cleanup - stop SMTP server', async () => {
|
2025-05-23 19:49:25 +00:00
|
|
|
await stopTestServer();
|
2025-05-23 19:03:44 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
tap.start();
|