/** * CMD-01: EHLO Command Tests * Tests SMTP EHLO command and server capabilities advertisement */ import { assert, assertEquals, assertMatch } from '@std/assert'; import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.ts'; import { connectToSmtp, waitForGreeting, sendSmtpCommand, closeSmtpConnection, } from '../../helpers/utils.ts'; const TEST_PORT = 25251; let testServer: ITestServer; Deno.test({ name: 'CMD-01: Setup - Start SMTP server', async fn() { testServer = await startTestServer({ port: TEST_PORT }); assert(testServer, 'Test server should be created'); }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'CMD-01: EHLO Command - server responds with proper capabilities', async fn() { const conn = await connectToSmtp('localhost', TEST_PORT); try { // Wait for greeting const greeting = await waitForGreeting(conn); assert(greeting.includes('220'), 'Should receive 220 greeting'); // Send EHLO const ehloResponse = await sendSmtpCommand(conn, 'EHLO test.example.com', '250'); // Parse capabilities const lines = ehloResponse .split('\r\n') .filter((line) => line.startsWith('250')) .filter((line) => line.length > 0); const capabilities = lines.map((line) => line.substring(4).trim()); console.log('📋 Server capabilities:', capabilities); // Verify essential capabilities assert( capabilities.some((cap) => cap.includes('SIZE')), 'Should advertise SIZE capability' ); assert( capabilities.some((cap) => cap.includes('8BITMIME')), 'Should advertise 8BITMIME capability' ); // The last line should be "250 " (without hyphen) const lastLine = lines[lines.length - 1]; assert(lastLine.startsWith('250 '), 'Last line should start with "250 " (space, not hyphen)'); } finally { await closeSmtpConnection(conn); } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'CMD-01: EHLO with invalid hostname - server handles gracefully', async fn() { const conn = await connectToSmtp('localhost', TEST_PORT); try { await waitForGreeting(conn); const invalidHostnames = [ '', // Empty hostname ' ', // Whitespace only 'invalid..hostname', // Double dots '.invalid', // Leading dot 'invalid.', // Trailing dot 'very-long-hostname-that-exceeds-reasonable-limits-' + 'x'.repeat(200), ]; for (const hostname of invalidHostnames) { console.log(`Testing invalid hostname: "${hostname}"`); try { const response = await sendSmtpCommand(conn, `EHLO ${hostname}`); // Server should either accept with warning or reject with 5xx assertMatch(response, /^(250|5\d\d)/, 'Server should respond with 250 or 5xx'); // Reset session for next test if (response.startsWith('250')) { await sendSmtpCommand(conn, 'RSET', '250'); } } catch (error) { // Some invalid hostnames might cause connection issues, which is acceptable console.log(` Hostname "${hostname}" caused error (acceptable):`, error.message); } } // Send QUIT await sendSmtpCommand(conn, 'QUIT', '221'); } finally { try { conn.close(); } catch { // Ignore close errors } } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'CMD-01: EHLO command pipelining - multiple EHLO commands', async fn() { const conn = await connectToSmtp('localhost', TEST_PORT); try { await waitForGreeting(conn); // First EHLO const ehlo1Response = await sendSmtpCommand(conn, 'EHLO first.example.com', '250'); assert(ehlo1Response.startsWith('250'), 'First EHLO should succeed'); // Second EHLO (should reset session) const ehlo2Response = await sendSmtpCommand(conn, 'EHLO second.example.com', '250'); assert(ehlo2Response.startsWith('250'), 'Second EHLO should succeed'); // Verify session was reset by trying MAIL FROM const mailResponse = await sendSmtpCommand(conn, 'MAIL FROM:', '250'); assert(mailResponse.startsWith('250'), 'MAIL FROM should work after second EHLO'); } finally { await closeSmtpConnection(conn); } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'CMD-01: Cleanup - Stop SMTP server', async fn() { await stopTestServer(testServer); }, sanitizeResources: false, sanitizeOps: false, });