/** * CMD-06: RSET Command Tests * Tests SMTP RSET command for transaction reset */ 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 = 25259; let testServer: ITestServer; Deno.test({ name: 'CMD-06: 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-06: RSET - resets transaction after MAIL FROM', 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 RSET const rsetResponse = await sendSmtpCommand(conn, 'RSET', '250'); assert(rsetResponse.includes('250'), 'RSET should respond with 250 OK'); // After RSET, should be able to send new MAIL FROM const mailFromResponse = await sendSmtpCommand(conn, 'MAIL FROM:', '250'); assert(mailFromResponse.includes('250'), 'Should accept MAIL FROM after RSET'); await sendSmtpCommand(conn, 'QUIT', '221'); console.log('✓ RSET successfully resets transaction after MAIL FROM'); } finally { try { conn.close(); } catch { // Ignore } } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'CMD-06: RSET - resets transaction after RCPT TO', 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'); // Send RSET const rsetResponse = await sendSmtpCommand(conn, 'RSET', '250'); assert(rsetResponse.includes('250'), 'RSET should respond with 250 OK'); // After RSET, RCPT TO should fail (need MAIL FROM first) const rcptToResponse = await sendSmtpCommand(conn, 'RCPT TO:'); assertMatch(rcptToResponse, /^503/, 'Should reject RCPT TO without MAIL FROM after RSET'); await sendSmtpCommand(conn, 'QUIT', '221'); console.log('✓ RSET clears transaction state requiring new MAIL FROM'); } finally { try { conn.close(); } catch { // Ignore } } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'CMD-06: RSET - handles multiple consecutive RSET commands', 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 multiple RSETs const rset1 = await sendSmtpCommand(conn, 'RSET', '250'); assert(rset1.includes('250'), 'First RSET should succeed'); const rset2 = await sendSmtpCommand(conn, 'RSET', '250'); assert(rset2.includes('250'), 'Second RSET should succeed'); const rset3 = await sendSmtpCommand(conn, 'RSET', '250'); assert(rset3.includes('250'), 'Third RSET should succeed'); await sendSmtpCommand(conn, 'QUIT', '221'); console.log('✓ Multiple consecutive RSET commands work (idempotent)'); } finally { try { conn.close(); } catch { // Ignore } } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'CMD-06: RSET - works without active transaction', async fn() { const conn = await connectToSmtp('localhost', TEST_PORT); try { await waitForGreeting(conn); await sendSmtpCommand(conn, 'EHLO test.example.com', '250'); // Send RSET without any transaction const rsetResponse = await sendSmtpCommand(conn, 'RSET', '250'); assert(rsetResponse.includes('250'), 'RSET should work even without active transaction'); await sendSmtpCommand(conn, 'QUIT', '221'); console.log('✓ RSET works without active transaction'); } finally { try { conn.close(); } catch { // Ignore } } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'CMD-06: RSET - clears all 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'); // Send RSET const rsetResponse = await sendSmtpCommand(conn, 'RSET', '250'); assert(rsetResponse.includes('250'), 'RSET should respond with 250 OK'); // After RSET, DATA should fail (no recipients) const dataResponse = await sendSmtpCommand(conn, 'DATA'); assertMatch(dataResponse, /^503/, 'DATA should fail without recipients after RSET'); await sendSmtpCommand(conn, 'QUIT', '221'); console.log('✓ RSET clears all recipients from transaction'); } finally { try { conn.close(); } catch { // Ignore } } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'CMD-06: RSET - ignores parameters', async fn() { const conn = await connectToSmtp('localhost', TEST_PORT); try { await waitForGreeting(conn); await sendSmtpCommand(conn, 'EHLO test.example.com', '250'); // Send RSET with parameters (should be ignored) const rsetResponse = await sendSmtpCommand(conn, 'RSET ignored parameter', '250'); assert(rsetResponse.includes('250'), 'RSET should ignore parameters and respond with 250 OK'); await sendSmtpCommand(conn, 'QUIT', '221'); console.log('✓ RSET ignores parameters as per RFC'); } finally { try { conn.close(); } catch { // Ignore } } }, sanitizeResources: false, sanitizeOps: false, }); Deno.test({ name: 'CMD-06: Cleanup - Stop SMTP server', async fn() { await stopTestServer(testServer); }, sanitizeResources: false, sanitizeOps: false, });