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-11: Basic HELP 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 HELP without parameters const helpResponse = await smtpClient.sendCommand('HELP'); // HELP typically returns 214 or 211 expect(helpResponse).toMatch(/^21[14]/); console.log('HELP response:'); console.log(helpResponse); // Check if it's multi-line const lines = helpResponse.split('\r\n').filter(line => line.length > 0); if (lines.length > 1) { console.log(`Multi-line help with ${lines.length} lines`); } await smtpClient.close(); }); tap.test('CCMD-11: HELP with specific 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'); // Test HELP for specific commands const commands = [ 'HELO', 'EHLO', 'MAIL', 'RCPT', 'DATA', 'RSET', 'NOOP', 'QUIT', 'VRFY', 'EXPN', 'HELP', 'AUTH', 'STARTTLS' ]; for (const cmd of commands) { const response = await smtpClient.sendCommand(`HELP ${cmd}`); console.log(`\nHELP ${cmd}:`); if (response.startsWith('214') || response.startsWith('211')) { // Extract help text const helpText = response.replace(/^21[14][\s-]/, ''); console.log(` ${helpText.trim()}`); } else if (response.startsWith('502')) { console.log(` Command not implemented`); } else if (response.startsWith('504')) { console.log(` Command parameter not implemented`); } else { console.log(` ${response.trim()}`); } } await smtpClient.close(); }); tap.test('CCMD-11: HELP response format variations', 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'); // Test different HELP queries const queries = [ '', // No parameter 'MAIL FROM', // Command with space 'RCPT TO', // Another with space 'UNKNOWN', // Unknown command 'mail', // Lowercase 'MaIl' // Mixed case ]; for (const query of queries) { const cmd = query ? `HELP ${query}` : 'HELP'; const response = await smtpClient.sendCommand(cmd); console.log(`\n"${cmd}":`); // Parse response code const codeMatch = response.match(/^(\d{3})/); if (codeMatch) { const code = codeMatch[1]; console.log(` Response code: ${code}`); // Common codes: // 211 - System status // 214 - Help message // 502 - Command not implemented // 504 - Command parameter not implemented if (code === '214' || code === '211') { // Check if response mentions the queried command if (query && response.toLowerCase().includes(query.toLowerCase())) { console.log(` Help specifically mentions "${query}"`); } } } } await smtpClient.close(); }); tap.test('CCMD-11: HELP during transaction', 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 a transaction await smtpClient.sendCommand('MAIL FROM:'); await smtpClient.sendCommand('RCPT TO:'); // HELP should not affect transaction console.log('\nHELP during transaction:'); const helpResponse = await smtpClient.sendCommand('HELP DATA'); expect(helpResponse).toMatch(/^21[14]/); // Continue transaction const dataResponse = await smtpClient.sendCommand('DATA'); expect(dataResponse).toInclude('354'); await smtpClient.sendCommand('Subject: Test\r\n\r\nTest message\r\n.'); console.log('Transaction completed successfully after HELP'); await smtpClient.close(); }); tap.test('CCMD-11: HELP command availability check', async () => { const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, connectionTimeout: 5000, debug: true }); await smtpClient.connect(); // Check HELP before EHLO console.log('\nTesting HELP before EHLO:'); const earlyHelp = await smtpClient.sendCommand('HELP'); console.log(`Response: ${earlyHelp.substring(0, 50)}...`); // HELP should work even before EHLO expect(earlyHelp).toMatch(/^[25]\d\d/); // Now do EHLO and check features const ehloResponse = await smtpClient.sendCommand('EHLO testclient.example.com'); // Check if HELP is advertised (not common but possible) if (ehloResponse.includes('HELP')) { console.log('Server explicitly advertises HELP support'); } await smtpClient.close(); }); tap.test('CCMD-11: HELP with invalid parameters', 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'); // Test HELP with various invalid inputs const invalidTests = [ 'HELP ' + 'X'.repeat(100), // Very long parameter 'HELP <>', // Special characters 'HELP MAIL RCPT DATA', // Multiple commands 'HELP\t\tTABS', // Tabs 'HELP\r\nINJECTION' // Injection attempt ]; for (const cmd of invalidTests) { try { const response = await smtpClient.sendCommand(cmd); console.log(`\n"${cmd.substring(0, 30)}...": ${response.substring(0, 50)}...`); // Should still get a valid SMTP response expect(response).toMatch(/^\d{3}/); } catch (error) { console.log(`Command rejected: ${error.message}`); } } await smtpClient.close(); }); tap.test('CCMD-11: HELP response parsing', 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'); // Get general HELP const helpResponse = await smtpClient.sendCommand('HELP'); // Parse help content if (helpResponse.match(/^21[14]/)) { // Extract command list if present const commandMatches = helpResponse.match(/\b(HELO|EHLO|MAIL|RCPT|DATA|RSET|NOOP|QUIT|VRFY|EXPN|HELP|AUTH|STARTTLS)\b/g); if (commandMatches) { const uniqueCommands = [...new Set(commandMatches)]; console.log('\nCommands mentioned in HELP:'); uniqueCommands.forEach(cmd => console.log(` - ${cmd}`)); // Verify common commands are mentioned const essentialCommands = ['MAIL', 'RCPT', 'DATA', 'QUIT']; const mentionedEssentials = essentialCommands.filter(cmd => uniqueCommands.includes(cmd) ); console.log(`\nEssential commands mentioned: ${mentionedEssentials.length}/${essentialCommands.length}`); } // Check for URLs or references const urlMatch = helpResponse.match(/https?:\/\/[^\s]+/); if (urlMatch) { console.log(`\nHelp includes URL: ${urlMatch[0]}`); } // Check for RFC references const rfcMatch = helpResponse.match(/RFC\s*\d+/gi); if (rfcMatch) { console.log(`\nRFC references: ${rfcMatch.join(', ')}`); } } await smtpClient.close(); }); tap.test('CCMD-11: HELP command localization', 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'); // Some servers might support localized help // Test with Accept-Language style parameter (non-standard) const languages = ['en', 'es', 'fr', 'de']; for (const lang of languages) { const response = await smtpClient.sendCommand(`HELP ${lang}`); console.log(`\nHELP ${lang}: ${response.substring(0, 60)}...`); // Most servers will treat this as unknown command // But we're testing how they handle it } await smtpClient.close(); }); tap.test('CCMD-11: HELP performance', 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'); // Measure HELP response times const iterations = 10; const times: number[] = []; for (let i = 0; i < iterations; i++) { const startTime = Date.now(); await smtpClient.sendCommand('HELP'); const elapsed = Date.now() - startTime; times.push(elapsed); } const avgTime = times.reduce((a, b) => a + b, 0) / times.length; const minTime = Math.min(...times); const maxTime = Math.max(...times); console.log(`\nHELP command performance (${iterations} iterations):`); console.log(` Average: ${avgTime.toFixed(2)}ms`); console.log(` Min: ${minTime}ms`); console.log(` Max: ${maxTime}ms`); // HELP should be fast (static response) expect(avgTime).toBeLessThan(100); await smtpClient.close(); }); tap.test('cleanup test SMTP server', async () => { if (testServer) { await testServer.stop(); } }); export default tap.start();