import { expect, tap } from '@git.zone/tstest/tapbundle'; import * as plugins from './plugins.js'; import { createTestServer } from '../../helpers/server.loader.js'; import { createSmtpClient } from '../../helpers/smtp.client.js'; tap.test('CRFC-04: should handle SMTP response codes correctly (RFC 5321)', async (tools) => { const testId = 'CRFC-04-response-codes'; console.log(`\n${testId}: Testing SMTP response code compliance...`); let scenarioCount = 0; // Scenario 1: 2xx success response codes await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing 2xx success response codes`); const testServer = await createTestServer({ onConnection: async (socket) => { console.log(' [Server] Client connected'); socket.write('220 responses.example.com Service ready\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); console.log(` [Server] Received: ${command}`); if (command.startsWith('EHLO')) { // 250 - Requested mail action okay, completed socket.write('250-responses.example.com\r\n'); socket.write('250-SIZE 10485760\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { // 250 - Requested mail action okay, completed socket.write('250 2.1.0 Sender OK\r\n'); } else if (command.startsWith('RCPT TO:')) { // 250 - Requested mail action okay, completed socket.write('250 2.1.5 Recipient OK\r\n'); } else if (command === 'DATA') { // 354 - Start mail input; end with . socket.write('354 Start mail input; end with .\r\n'); } else if (command === '.') { // 250 - Requested mail action okay, completed socket.write('250 2.0.0 Message accepted for delivery\r\n'); } else if (command === 'QUIT') { // 221 - Service closing transmission channel socket.write('221 2.0.0 Service closing transmission channel\r\n'); socket.end(); } else if (command === 'NOOP') { // 250 - Requested mail action okay, completed socket.write('250 2.0.0 OK\r\n'); } else if (command === 'RSET') { // 250 - Requested mail action okay, completed socket.write('250 2.0.0 Reset OK\r\n'); } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: '2xx response test', text: 'Testing 2xx success response codes' }); const result = await smtpClient.sendMail(email); console.log(' All 2xx success codes handled correctly'); expect(result).toBeDefined(); expect(result.messageId).toBeDefined(); await testServer.server.close(); })(); // Scenario 2: 4xx temporary failure response codes await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing 4xx temporary failure response codes`); let attemptCount = 0; const testServer = await createTestServer({ onConnection: async (socket) => { attemptCount++; console.log(` [Server] Client connected (attempt ${attemptCount})`); socket.write('220 responses.example.com Service ready\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); console.log(` [Server] Received: ${command}`); if (command.startsWith('EHLO')) { socket.write('250-responses.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { if (attemptCount === 1) { // 451 - Requested action aborted: local error in processing socket.write('451 4.3.0 Temporary system failure, try again later\r\n'); } else { socket.write('250 OK\r\n'); } } else if (command.startsWith('RCPT TO:')) { const address = command.match(/<(.+)>/)?.[1] || ''; if (address.includes('full')) { // 452 - Requested action not taken: insufficient system storage socket.write('452 4.2.2 Mailbox full, try again later\r\n'); } else if (address.includes('busy')) { // 450 - Requested mail action not taken: mailbox unavailable socket.write('450 4.2.1 Mailbox busy, try again later\r\n'); } else { socket.write('250 OK\r\n'); } } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { if (attemptCount === 1) { // 421 - Service not available, closing transmission channel socket.write('421 4.3.2 System shutting down, try again later\r\n'); socket.end(); } else { socket.write('250 OK\r\n'); } } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); // Test temporary failures with retry const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); // First attempt with temporary failure const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: '4xx response test', text: 'Testing 4xx temporary failure codes' }); try { await smtpClient.sendMail(email); console.log(' Unexpected: First attempt succeeded'); } catch (error) { console.log(' Expected: Temporary failure on first attempt'); expect(error.responseCode).toBeGreaterThanOrEqual(400); expect(error.responseCode).toBeLessThan(500); } // Second attempt should succeed const retryResult = await smtpClient.sendMail(email); console.log(' Retry after temporary failure succeeded'); expect(retryResult).toBeDefined(); // Test specific 4xx codes const tempFailureEmail = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['full@example.com', 'busy@example.com'], subject: 'Specific 4xx test', text: 'Testing specific temporary failure codes' }); try { const result = await smtpClient.sendMail(tempFailureEmail); console.log(` Partial delivery: ${result.rejected?.length || 0} rejected`); expect(result.rejected?.length).toBeGreaterThan(0); } catch (error) { console.log(' Multiple 4xx failures handled'); } await testServer.server.close(); })(); // Scenario 3: 5xx permanent failure response codes await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing 5xx permanent failure response codes`); const testServer = await createTestServer({ onConnection: async (socket) => { console.log(' [Server] Client connected'); socket.write('220 responses.example.com Service ready\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); console.log(` [Server] Received: ${command}`); if (command.startsWith('EHLO')) { socket.write('250-responses.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { const address = command.match(/<(.+)>/)?.[1] || ''; if (address.includes('blocked')) { // 550 - Requested action not taken: mailbox unavailable socket.write('550 5.1.1 Sender blocked\r\n'); } else if (address.includes('invalid')) { // 553 - Requested action not taken: mailbox name not allowed socket.write('553 5.1.8 Invalid sender address format\r\n'); } else { socket.write('250 OK\r\n'); } } else if (command.startsWith('RCPT TO:')) { const address = command.match(/<(.+)>/)?.[1] || ''; if (address.includes('unknown')) { // 550 - Requested action not taken: mailbox unavailable socket.write('550 5.1.1 User unknown\r\n'); } else if (address.includes('disabled')) { // 551 - User not local; please try socket.write('551 5.1.6 User account disabled\r\n'); } else if (address.includes('relay')) { // 554 - Transaction failed socket.write('554 5.7.1 Relay access denied\r\n'); } else { socket.write('250 OK\r\n'); } } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write('250 OK\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } else if (command.startsWith('INVALID')) { // 500 - Syntax error, command unrecognized socket.write('500 5.5.1 Command not recognized\r\n'); } else if (command === 'MAIL') { // 501 - Syntax error in parameters or arguments socket.write('501 5.5.4 Syntax error in MAIL command\r\n'); } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); // Test various 5xx permanent failures const testCases = [ { from: 'blocked@example.com', to: 'recipient@example.com', desc: 'blocked sender' }, { from: 'sender@example.com', to: 'unknown@example.com', desc: 'unknown recipient' }, { from: 'sender@example.com', to: 'disabled@example.com', desc: 'disabled user' }, { from: 'sender@example.com', to: 'relay@external.com', desc: 'relay denied' } ]; for (const testCase of testCases) { console.log(` Testing ${testCase.desc}...`); const email = new plugins.smartmail.Email({ from: testCase.from, to: [testCase.to], subject: `5xx test: ${testCase.desc}`, text: `Testing 5xx permanent failure: ${testCase.desc}` }); try { await smtpClient.sendMail(email); console.log(` Unexpected: ${testCase.desc} succeeded`); } catch (error) { console.log(` Expected: ${testCase.desc} failed with 5xx`); expect(error.responseCode).toBeGreaterThanOrEqual(500); expect(error.responseCode).toBeLessThan(600); } } await testServer.server.close(); })(); // Scenario 4: Multi-line response handling await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing multi-line response handling`); const testServer = await createTestServer({ onConnection: async (socket) => { console.log(' [Server] Client connected'); socket.write('220-responses.example.com ESMTP Service Ready\r\n'); socket.write('220-This server supports multiple extensions\r\n'); socket.write('220 Please proceed with EHLO\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); console.log(` [Server] Received: ${command}`); if (command.startsWith('EHLO')) { // Multi-line EHLO response socket.write('250-responses.example.com Hello client\r\n'); socket.write('250-SIZE 10485760\r\n'); socket.write('250-8BITMIME\r\n'); socket.write('250-STARTTLS\r\n'); socket.write('250-ENHANCEDSTATUSCODES\r\n'); socket.write('250-PIPELINING\r\n'); socket.write('250-DSN\r\n'); socket.write('250 HELP\r\n'); // Last line ends with space } else if (command.startsWith('MAIL FROM:')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input; end with .\r\n'); } else if (command === '.') { // Multi-line success response socket.write('250-Message accepted for delivery\r\n'); socket.write('250-Queue ID: ABC123\r\n'); socket.write('250 Thank you\r\n'); } else if (command === 'HELP') { // Multi-line help response socket.write('214-This server supports the following commands:\r\n'); socket.write('214-EHLO HELO MAIL RCPT DATA\r\n'); socket.write('214-RSET NOOP QUIT HELP\r\n'); socket.write('214 For more info visit http://example.com/help\r\n'); } else if (command === 'QUIT') { socket.write('221-Thank you for using our service\r\n'); socket.write('221 Goodbye\r\n'); socket.end(); } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'Multi-line response test', text: 'Testing multi-line SMTP response handling' }); const result = await smtpClient.sendMail(email); console.log(' Multi-line responses handled correctly'); expect(result).toBeDefined(); expect(result.response).toContain('Queue ID'); await testServer.server.close(); })(); // Scenario 5: Response code format validation await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing response code format validation`); const testServer = await createTestServer({ onConnection: async (socket) => { console.log(' [Server] Client connected'); socket.write('220 responses.example.com ESMTP\r\n'); let commandCount = 0; socket.on('data', (data) => { commandCount++; const command = data.toString().trim(); console.log(` [Server] Received: ${command}`); if (command.startsWith('EHLO')) { socket.write('250-responses.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { // Test various response code formats if (commandCount === 2) { // Valid 3-digit code socket.write('250 OK\r\n'); } else { socket.write('250 OK\r\n'); } } else if (command.startsWith('RCPT TO:')) { const address = command.match(/<(.+)>/)?.[1] || ''; if (address.includes('enhanced')) { // Enhanced status code format (RFC 3463) socket.write('250 2.1.5 Recipient OK\r\n'); } else if (address.includes('detailed')) { // Detailed response with explanation socket.write('250 OK: Recipient accepted for delivery to local mailbox\r\n'); } else { socket.write('250 OK\r\n'); } } else if (command === 'DATA') { socket.write('354 Start mail input; end with .\r\n'); } else if (command === '.') { // Response with timestamp const timestamp = new Date().toISOString(); socket.write(`250 OK: Message accepted at ${timestamp}\r\n`); } else if (command === 'QUIT') { socket.write('221 Service closing transmission channel\r\n'); socket.end(); } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); // Test with recipients that trigger different response formats const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['enhanced@example.com', 'detailed@example.com', 'normal@example.com'], subject: 'Response format test', text: 'Testing SMTP response code format compliance' }); const result = await smtpClient.sendMail(email); console.log(' Various response code formats handled'); expect(result).toBeDefined(); expect(result.response).toContain('Message accepted'); await testServer.server.close(); })(); // Scenario 6: Error recovery and continuation await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing error recovery and continuation`); const testServer = await createTestServer({ onConnection: async (socket) => { console.log(' [Server] Client connected'); socket.write('220 responses.example.com ESMTP\r\n'); let errorCount = 0; socket.on('data', (data) => { const command = data.toString().trim(); console.log(` [Server] Received: ${command}`); if (command.startsWith('EHLO')) { socket.write('250-responses.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { const address = command.match(/<(.+)>/)?.[1] || ''; if (address.includes('error1')) { errorCount++; socket.write('550 5.1.1 First error - user unknown\r\n'); } else if (address.includes('error2')) { errorCount++; socket.write('551 5.1.6 Second error - user not local\r\n'); } else { socket.write('250 OK\r\n'); } } else if (command === 'DATA') { if (errorCount > 0) { console.log(` [Server] ${errorCount} errors occurred, but continuing`); } socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write(`250 OK: Message accepted despite ${errorCount} recipient errors\r\n`); } else if (command === 'RSET') { console.log(' [Server] Transaction reset'); errorCount = 0; socket.write('250 OK\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } else { // Unknown command socket.write('500 5.5.1 Command not recognized\r\n'); } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); // Test with mix of valid and invalid recipients const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['error1@example.com', 'valid@example.com', 'error2@example.com', 'another-valid@example.com'], subject: 'Error recovery test', text: 'Testing error handling and recovery' }); const result = await smtpClient.sendMail(email); console.log(` Partial delivery: ${result.accepted?.length || 0} accepted, ${result.rejected?.length || 0} rejected`); expect(result).toBeDefined(); expect(result.accepted?.length).toBeGreaterThan(0); expect(result.rejected?.length).toBeGreaterThan(0); await testServer.server.close(); })(); console.log(`\n${testId}: All ${scenarioCount} response code scenarios tested ✓`); });