import { tap, expect } from '@git.zone/tstest/tapbundle'; import { startTestSmtpServer } from '../../helpers/server.loader.js'; import { createSmtpClient } from '../../helpers/smtp.client.js'; let testServer: any; tap.test('setup test SMTP server', async () => { testServer = await startTestSmtpServer(); expect(testServer).toBeTruthy(); expect(testServer.port).toBeGreaterThan(0); }); tap.test('CCMD-08: Basic RSET command', async () => { const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, connectionTimeout: 5000, debug: true }); await smtpClient.connect(); await smtpClient.sendCommand('EHLO testclient.example.com'); // Send RSET command const rsetResponse = await smtpClient.sendCommand('RSET'); // Verify response expect(rsetResponse).toInclude('250'); expect(rsetResponse).toMatch(/reset|ok/i); console.log(`RSET response: ${rsetResponse.trim()}`); await smtpClient.close(); }); tap.test('CCMD-08: RSET after MAIL FROM', async () => { const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, connectionTimeout: 5000, debug: true }); await smtpClient.connect(); await smtpClient.sendCommand('EHLO testclient.example.com'); // Start transaction const mailResponse = await smtpClient.sendCommand('MAIL FROM:'); expect(mailResponse).toInclude('250'); // Reset transaction const rsetResponse = await smtpClient.sendCommand('RSET'); expect(rsetResponse).toInclude('250'); // Verify transaction was reset by trying RCPT TO without MAIL FROM const rcptResponse = await smtpClient.sendCommand('RCPT TO:'); expect(rcptResponse).toMatch(/[45]\d\d/); // Should fail await smtpClient.close(); }); tap.test('CCMD-08: RSET after multiple recipients', async () => { const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, connectionTimeout: 5000, debug: true }); await smtpClient.connect(); await smtpClient.sendCommand('EHLO testclient.example.com'); // Build up a transaction with multiple recipients await smtpClient.sendCommand('MAIL FROM:'); await smtpClient.sendCommand('RCPT TO:'); await smtpClient.sendCommand('RCPT TO:'); await smtpClient.sendCommand('RCPT TO:'); console.log('Transaction built with 3 recipients'); // Reset the transaction const rsetResponse = await smtpClient.sendCommand('RSET'); expect(rsetResponse).toInclude('250'); // Start a new transaction to verify reset const newMailResponse = await smtpClient.sendCommand('MAIL FROM:'); expect(newMailResponse).toInclude('250'); const newRcptResponse = await smtpClient.sendCommand('RCPT TO:'); expect(newRcptResponse).toInclude('250'); console.log('Successfully started new transaction after RSET'); await smtpClient.sendCommand('RSET'); await smtpClient.close(); }); tap.test('CCMD-08: RSET during DATA phase', async () => { const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, connectionTimeout: 5000, debug: true }); await smtpClient.connect(); await smtpClient.sendCommand('EHLO testclient.example.com'); // Start transaction await smtpClient.sendCommand('MAIL FROM:'); await smtpClient.sendCommand('RCPT TO:'); // Enter DATA phase const dataResponse = await smtpClient.sendCommand('DATA'); expect(dataResponse).toInclude('354'); // Try RSET during DATA (should fail or be queued) // Most servers will interpret this as message content await smtpClient.sendCommand('RSET'); // Complete the DATA phase const endDataResponse = await smtpClient.sendCommand('.'); expect(endDataResponse).toInclude('250'); // Now RSET should work const rsetResponse = await smtpClient.sendCommand('RSET'); expect(rsetResponse).toInclude('250'); await smtpClient.close(); }); tap.test('CCMD-08: Multiple RSET commands', async () => { const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, connectionTimeout: 5000, debug: true }); await smtpClient.connect(); await smtpClient.sendCommand('EHLO testclient.example.com'); // Send multiple RSET commands for (let i = 0; i < 5; i++) { const rsetResponse = await smtpClient.sendCommand('RSET'); expect(rsetResponse).toInclude('250'); console.log(`RSET ${i + 1}: ${rsetResponse.trim()}`); } // Should still be able to start a transaction const mailResponse = await smtpClient.sendCommand('MAIL FROM:'); expect(mailResponse).toInclude('250'); await smtpClient.sendCommand('RSET'); await smtpClient.close(); }); tap.test('CCMD-08: RSET with pipelining', async () => { const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, enablePipelining: true, connectionTimeout: 5000, debug: true }); await smtpClient.connect(); await smtpClient.sendCommand('EHLO testclient.example.com'); // Pipeline commands including RSET const pipelinedCommands = [ smtpClient.sendCommand('MAIL FROM:'), smtpClient.sendCommand('RCPT TO:'), smtpClient.sendCommand('RSET'), smtpClient.sendCommand('MAIL FROM:'), smtpClient.sendCommand('RCPT TO:') ]; const responses = await Promise.all(pipelinedCommands); // Check responses expect(responses[0]).toInclude('250'); // MAIL FROM expect(responses[1]).toInclude('250'); // RCPT TO expect(responses[2]).toInclude('250'); // RSET expect(responses[3]).toInclude('250'); // New MAIL FROM expect(responses[4]).toInclude('250'); // New RCPT TO console.log('Successfully pipelined commands with RSET'); await smtpClient.sendCommand('RSET'); await smtpClient.close(); }); tap.test('CCMD-08: RSET state verification', async () => { const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, connectionTimeout: 5000, debug: true }); await smtpClient.connect(); await smtpClient.sendCommand('EHLO testclient.example.com'); // Build complex state await smtpClient.sendCommand('MAIL FROM: SIZE=1000'); await smtpClient.sendCommand('RCPT TO:'); await smtpClient.sendCommand('RCPT TO:'); console.log('Built transaction state with SIZE parameter and 2 recipients'); // Reset const rsetResponse = await smtpClient.sendCommand('RSET'); expect(rsetResponse).toInclude('250'); // Verify all state is cleared // 1. Can't add recipients without MAIL FROM const rcptResponse = await smtpClient.sendCommand('RCPT TO:'); expect(rcptResponse).toMatch(/[45]\d\d/); // 2. Can start fresh transaction const newMailResponse = await smtpClient.sendCommand('MAIL FROM:'); expect(newMailResponse).toInclude('250'); // 3. Previous recipients are not remembered const dataResponse = await smtpClient.sendCommand('DATA'); expect(dataResponse).toMatch(/[45]\d\d/); // Should fail - no recipients await smtpClient.sendCommand('RSET'); await smtpClient.close(); }); tap.test('CCMD-08: RSET performance impact', async () => { const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, connectionTimeout: 5000, debug: false // Quiet for performance test }); await smtpClient.connect(); await smtpClient.sendCommand('EHLO testclient.example.com'); const iterations = 20; const times: number[] = []; for (let i = 0; i < iterations; i++) { // Build transaction await smtpClient.sendCommand('MAIL FROM:'); await smtpClient.sendCommand('RCPT TO:'); // Measure RSET time const startTime = Date.now(); await smtpClient.sendCommand('RSET'); const elapsed = Date.now() - startTime; times.push(elapsed); } // Analyze RSET performance const avgTime = times.reduce((a, b) => a + b, 0) / times.length; const minTime = Math.min(...times); const maxTime = Math.max(...times); console.log(`RSET performance over ${iterations} iterations:`); console.log(` Average: ${avgTime.toFixed(2)}ms`); console.log(` Min: ${minTime}ms`); console.log(` Max: ${maxTime}ms`); // RSET should be fast expect(avgTime).toBeLessThan(100); await smtpClient.close(); }); tap.test('cleanup test SMTP server', async () => { if (testServer) { await testServer.stop(); } }); export default tap.start();