134 lines
3.6 KiB
TypeScript
134 lines
3.6 KiB
TypeScript
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<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
|
|
expect(isConnected).toEqual(true);
|
|
|
|
// 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 () => {
|
|
await stopTestServer(testServer);
|
|
});
|
|
|
|
export default tap.start(); |