import { tap, expect } from '@git.zone/tstest/tapbundle'; import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.js'; import { createSmtpClient } from '../../../ts/mail/delivery/smtpclient/index.js'; import { Email } from '../../../ts/mail/core/classes.email.js'; import * as net from 'net'; let testServer: ITestServer; tap.test('setup - start SMTP server for invalid recipient tests', async () => { testServer = await startTestServer({ port: 2568, tlsEnabled: false, authRequired: false }); expect(testServer.port).toEqual(2568); }); tap.test('CERR-06: Invalid email address formats', async () => { // Test various invalid email formats that should be caught by Email validation const invalidEmails = [ 'notanemail', '@example.com', 'user@', 'user@@example.com', 'user@domain..com' ]; console.log('Testing invalid email formats:'); for (const invalidEmail of invalidEmails) { console.log(`Testing: ${invalidEmail}`); try { const email = new Email({ from: 'sender@example.com', to: invalidEmail, subject: 'Invalid Recipient Test', text: 'Testing invalid email format' }); console.log('✗ Should have thrown validation error'); } catch (error: any) { console.log(`✅ Validation error caught: ${error.message}`); expect(error).toBeInstanceOf(Error); } } }); tap.test('CERR-06: SMTP 550 Invalid recipient', async () => { // Create server that rejects certain recipients const rejectServer = net.createServer((socket) => { socket.write('220 Reject Server\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); if (command.startsWith('EHLO')) { socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO')) { if (command.includes('invalid@')) { socket.write('550 5.1.1 Invalid recipient\r\n'); } else if (command.includes('unknown@')) { socket.write('550 5.1.1 User unknown\r\n'); } else { socket.write('250 OK\r\n'); } } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); }); await new Promise((resolve) => { rejectServer.listen(2569, () => resolve()); }); const smtpClient = createSmtpClient({ host: '127.0.0.1', port: 2569, secure: false, connectionTimeout: 5000 }); const email = new Email({ from: 'sender@example.com', to: 'invalid@example.com', subject: 'Invalid Recipient Test', text: 'Testing invalid recipient' }); const result = await smtpClient.sendMail(email); expect(result.success).toBeFalse(); console.log('Actual error:', result.error?.message); expect(result.error?.message).toMatch(/550|invalid|recipient/i); console.log('✅ 550 invalid recipient error handled'); await smtpClient.close(); await new Promise((resolve) => { rejectServer.close(() => resolve()); }); }); tap.test('CERR-06: SMTP 550 User unknown', async () => { // Create server that responds with user unknown const unknownServer = net.createServer((socket) => { socket.write('220 Unknown Server\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); if (command.startsWith('EHLO')) { socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO')) { socket.write('550 5.1.1 User unknown\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); }); await new Promise((resolve) => { unknownServer.listen(2570, () => resolve()); }); const smtpClient = createSmtpClient({ host: '127.0.0.1', port: 2570, secure: false, connectionTimeout: 5000 }); const email = new Email({ from: 'sender@example.com', to: 'unknown@example.com', subject: 'Unknown User Test', text: 'Testing unknown user' }); const result = await smtpClient.sendMail(email); expect(result.success).toBeFalse(); console.log('Actual error:', result.error?.message); expect(result.error?.message).toMatch(/550|unknown|recipient/i); console.log('✅ 550 user unknown error handled'); await smtpClient.close(); await new Promise((resolve) => { unknownServer.close(() => resolve()); }); }); tap.test('CERR-06: Mixed valid and invalid recipients', async () => { // Create server that accepts some recipients and rejects others const mixedServer = net.createServer((socket) => { socket.write('220 Mixed Server\r\n'); let inData = false; socket.on('data', (data) => { const lines = data.toString().split('\r\n'); lines.forEach(line => { if (!line && lines[lines.length - 1] === '') return; if (inData) { // We're in DATA mode - look for the terminating dot if (line === '.') { socket.write('250 OK\r\n'); inData = false; } // Otherwise, just consume the data } else { // We're in command mode if (line.startsWith('EHLO')) { socket.write('250 OK\r\n'); } else if (line.startsWith('MAIL FROM')) { socket.write('250 OK\r\n'); } else if (line.startsWith('RCPT TO')) { if (line.includes('valid@')) { socket.write('250 OK\r\n'); } else { socket.write('550 5.1.1 Recipient rejected\r\n'); } } else if (line === 'DATA') { socket.write('354 Send data\r\n'); inData = true; } else if (line === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } } }); }); }); await new Promise((resolve) => { mixedServer.listen(2571, () => resolve()); }); const smtpClient = createSmtpClient({ host: '127.0.0.1', port: 2571, secure: false, connectionTimeout: 5000 }); const email = new Email({ from: 'sender@example.com', to: ['valid@example.com', 'invalid@example.com'], subject: 'Mixed Recipients Test', text: 'Testing mixed valid and invalid recipients' }); const result = await smtpClient.sendMail(email); // Should fail when any recipient is rejected expect(result.success).toBeFalse(); console.log('Actual error:', result.error?.message); expect(result.error?.message).toMatch(/550|reject|recipient|timeout|transmission/i); console.log('✅ Mixed recipients error handled'); await smtpClient.close(); await new Promise((resolve) => { mixedServer.close(() => resolve()); }); }); tap.test('CERR-06: Domain not found - 550', async () => { // Create server that rejects due to domain issues const domainServer = net.createServer((socket) => { socket.write('220 Domain Server\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); if (command.startsWith('EHLO')) { socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO')) { socket.write('550 5.1.2 Domain not found\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); }); await new Promise((resolve) => { domainServer.listen(2572, () => resolve()); }); const smtpClient = createSmtpClient({ host: '127.0.0.1', port: 2572, secure: false, connectionTimeout: 5000 }); const email = new Email({ from: 'sender@example.com', to: 'user@nonexistent.domain', subject: 'Domain Not Found Test', text: 'Testing domain not found' }); const result = await smtpClient.sendMail(email); expect(result.success).toBeFalse(); console.log('Actual error:', result.error?.message); expect(result.error?.message).toMatch(/550|domain|recipient/i); console.log('✅ 550 domain not found error handled'); await smtpClient.close(); await new Promise((resolve) => { domainServer.close(() => resolve()); }); }); tap.test('CERR-06: Valid recipient succeeds', async () => { // Test successful email send with working server const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, connectionTimeout: 5000 }); const email = new Email({ from: 'sender@example.com', to: 'valid@example.com', subject: 'Valid Recipient Test', text: 'Testing valid recipient' }); const result = await smtpClient.sendMail(email); expect(result.success).toBeTrue(); console.log('✅ Valid recipient email sent successfully'); await smtpClient.close(); }); tap.test('cleanup - stop SMTP server', async () => { await stopTestServer(testServer); }); export default tap.start();