/** * CMD-03: RCPT TO Command Tests * Tests SMTP RCPT TO command for recipient validation */ import { assert, 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 = 25253; let testServer: ITestServer; Deno.test({ name: 'CMD-03: 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-03: RCPT TO - accepts valid recipient addresses', 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'); const validRecipients = [ 'user@example.com', 'test.user+tag@example.com', 'user@[192.168.1.1]', // IP literal 'user@subdomain.example.com', 'multiple_recipients@example.com', ]; for (const recipient of validRecipients) { console.log(`✓ Testing valid recipient: ${recipient}`); const response = await sendSmtpCommand(conn, `RCPT TO:<${recipient}>`, '250'); assert(response.startsWith('250'), `Should accept valid recipient: ${recipient}`); } } finally { await closeSmtpConnection(conn); } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'CMD-03: RCPT TO - accepts multiple recipients', 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'); // Add multiple recipients await sendSmtpCommand(conn, 'RCPT TO:', '250'); await sendSmtpCommand(conn, 'RCPT TO:', '250'); await sendSmtpCommand(conn, 'RCPT TO:', '250'); console.log('✓ Successfully added 3 recipients'); } finally { await closeSmtpConnection(conn); } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'CMD-03: RCPT TO - rejects invalid recipient addresses', 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'); const invalidRecipients = [ 'notanemail', '@example.com', 'user@', 'user space@example.com', ]; for (const recipient of invalidRecipients) { console.log(`✗ Testing invalid recipient: ${recipient}`); try { const response = await sendSmtpCommand(conn, `RCPT TO:<${recipient}>`); assertMatch(response, /^5\d\d/, `Should reject invalid recipient: ${recipient}`); } catch (error) { console.log(` Recipient caused error (acceptable): ${error.message}`); } } } finally { try { await closeSmtpConnection(conn); } catch { // Ignore close errors } } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'CMD-03: RCPT TO - enforces correct sequence', async fn() { const conn = await connectToSmtp('localhost', TEST_PORT); try { await waitForGreeting(conn); await sendSmtpCommand(conn, 'EHLO test.example.com', '250'); // Try RCPT TO before MAIL FROM const response = await sendSmtpCommand(conn, 'RCPT TO:'); assertMatch(response, /^503/, 'Should reject RCPT TO before MAIL FROM'); } finally { try { await closeSmtpConnection(conn); } catch { // Ignore errors } } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'CMD-03: RCPT TO - RSET clears recipients', 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'); await sendSmtpCommand(conn, 'RCPT TO:', '250'); await sendSmtpCommand(conn, 'RCPT TO:', '250'); // Reset should clear recipients await sendSmtpCommand(conn, 'RSET', '250'); // Should be able to start new transaction await sendSmtpCommand(conn, 'MAIL FROM:', '250'); await sendSmtpCommand(conn, 'RCPT TO:', '250'); console.log('✓ RSET successfully cleared recipients'); } finally { await closeSmtpConnection(conn); } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'CMD-03: Cleanup - Stop SMTP server', async fn() { await stopTestServer(testServer); }, sanitizeResources: false, sanitizeOps: false, });