/** * ERR-01: Syntax Error Handling Tests * Tests SMTP server handling of syntax errors and malformed commands */ import { assert, assertMatch } from '@std/assert'; import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.ts'; import { connectToSmtp, waitForGreeting, sendSmtpCommand, readSmtpResponse, closeSmtpConnection, } from '../../helpers/utils.ts'; const TEST_PORT = 25261; let testServer: ITestServer; Deno.test({ name: 'ERR-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: 'ERR-01: Syntax Errors - rejects invalid command', async fn() { const conn = await connectToSmtp('localhost', TEST_PORT); try { await waitForGreeting(conn); // Send invalid command const encoder = new TextEncoder(); await conn.write(encoder.encode('INVALID_COMMAND\r\n')); const response = await readSmtpResponse(conn); // RFC 5321: Should return 500 (syntax error) or 502 (command not implemented) assertMatch(response, /^(500|502)/, 'Should reject invalid command with 500 or 502'); await sendSmtpCommand(conn, 'QUIT', '221'); console.log('✓ Invalid command rejected with appropriate error code'); } finally { try { conn.close(); } catch { // Ignore } } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'ERR-01: Syntax Errors - rejects MAIL FROM without brackets', async fn() { const conn = await connectToSmtp('localhost', TEST_PORT); try { await waitForGreeting(conn); await sendSmtpCommand(conn, 'EHLO test.example.com', '250'); // Send MAIL FROM without angle brackets const encoder = new TextEncoder(); await conn.write(encoder.encode('MAIL FROM:test@example.com\r\n')); const response = await readSmtpResponse(conn); // Should return 501 (syntax error in parameters) assertMatch(response, /^501/, 'Should reject MAIL FROM without brackets with 501'); await sendSmtpCommand(conn, 'QUIT', '221'); console.log('✓ MAIL FROM without brackets rejected'); } finally { try { conn.close(); } catch { // Ignore } } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'ERR-01: Syntax Errors - rejects RCPT TO without brackets', async fn() { const conn = await connectToSmtp('localhost', TEST_PORT); try { await waitForGreeting(conn); await sendSmtpCommand(conn, 'EHLO test.example.com', '250'); await sendSmtpCommand(conn, 'MAIL FROM:', '250'); // Send RCPT TO without angle brackets const encoder = new TextEncoder(); await conn.write(encoder.encode('RCPT TO:recipient@example.com\r\n')); const response = await readSmtpResponse(conn); // Should return 501 (syntax error in parameters) assertMatch(response, /^501/, 'Should reject RCPT TO without brackets with 501'); await sendSmtpCommand(conn, 'QUIT', '221'); console.log('✓ RCPT TO without brackets rejected'); } finally { try { conn.close(); } catch { // Ignore } } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'ERR-01: Syntax Errors - rejects EHLO without hostname', async fn() { const conn = await connectToSmtp('localhost', TEST_PORT); try { await waitForGreeting(conn); // Send EHLO without hostname const encoder = new TextEncoder(); await conn.write(encoder.encode('EHLO\r\n')); const response = await readSmtpResponse(conn); // Should return 501 (syntax error in parameters - missing domain) assertMatch(response, /^501/, 'Should reject EHLO without hostname with 501'); await sendSmtpCommand(conn, 'QUIT', '221'); console.log('✓ EHLO without hostname rejected'); } finally { try { conn.close(); } catch { // Ignore } } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'ERR-01: Syntax Errors - handles commands with extra parameters', async fn() { const conn = await connectToSmtp('localhost', TEST_PORT); try { await waitForGreeting(conn); await sendSmtpCommand(conn, 'EHLO test.example.com', '250'); // Send QUIT with extra parameters (QUIT doesn't take parameters) const encoder = new TextEncoder(); await conn.write(encoder.encode('QUIT extra parameters\r\n')); const response = await readSmtpResponse(conn); // Some servers accept it (221), others reject it (501) assertMatch(response, /^(221|501)/, 'Should either accept or reject QUIT with extra params'); console.log(`✓ QUIT with extra parameters handled: ${response.substring(0, 3)}`); } finally { try { conn.close(); } catch { // Ignore } } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'ERR-01: Syntax Errors - rejects malformed email addresses', async fn() { const conn = await connectToSmtp('localhost', TEST_PORT); try { await waitForGreeting(conn); await sendSmtpCommand(conn, 'EHLO test.example.com', '250'); // Send malformed email address const encoder = new TextEncoder(); await conn.write(encoder.encode('MAIL FROM:\r\n')); const response = await readSmtpResponse(conn); // Should return 501 (syntax error) or 553 (bad address) assertMatch(response, /^(501|553)/, 'Should reject malformed email with 501 or 553'); await sendSmtpCommand(conn, 'QUIT', '221'); console.log('✓ Malformed email address rejected'); } finally { try { conn.close(); } catch { // Ignore } } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'ERR-01: Syntax Errors - rejects commands in wrong sequence', async fn() { const conn = await connectToSmtp('localhost', TEST_PORT); try { await waitForGreeting(conn); // Send DATA without MAIL FROM/RCPT TO const encoder = new TextEncoder(); await conn.write(encoder.encode('DATA\r\n')); const response = await readSmtpResponse(conn); // Should return 503 (bad sequence of commands) assertMatch(response, /^503/, 'Should reject DATA without setup with 503'); await sendSmtpCommand(conn, 'QUIT', '221'); console.log('✓ Commands in wrong sequence rejected'); } finally { try { conn.close(); } catch { // Ignore } } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'ERR-01: Syntax Errors - handles excessively long commands', async fn() { const conn = await connectToSmtp('localhost', TEST_PORT); try { await waitForGreeting(conn); // Send EHLO with excessively long hostname const longString = 'A'.repeat(1000); const encoder = new TextEncoder(); await conn.write(encoder.encode(`EHLO ${longString}\r\n`)); const response = await readSmtpResponse(conn); // Some servers accept long hostnames (250), others reject (500/501) assertMatch(response, /^(250|500|501)/, 'Should handle long commands (accept or reject)'); await sendSmtpCommand(conn, 'QUIT', '221'); console.log(`✓ Excessively long command handled: ${response.substring(0, 3)}`); } finally { try { conn.close(); } catch { // Ignore } } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'ERR-01: Cleanup - Stop SMTP server', async fn() { await stopTestServer(testServer); }, sanitizeResources: false, sanitizeOps: false, });