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 greylisting tests', async () => { testServer = await startTestServer({ port: 2559, tlsEnabled: false, authRequired: false }); expect(testServer.port).toEqual(2559); }); tap.test('CERR-04: Basic greylisting response handling', async () => { // Create server that simulates greylisting const greylistServer = net.createServer((socket) => { socket.write('220 Greylist Test Server\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); if (command.startsWith('EHLO') || command.startsWith('HELO')) { socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO')) { // Simulate greylisting response socket.write('451 4.7.1 Greylisting in effect, please retry later\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } else { socket.write('250 OK\r\n'); } }); }); await new Promise((resolve) => { greylistServer.listen(2560, () => resolve()); }); const smtpClient = await createSmtpClient({ host: '127.0.0.1', port: 2560, secure: false, connectionTimeout: 5000 }); const email = new Email({ from: 'sender@example.com', to: 'recipient@example.com', subject: 'Greylisting Test', text: 'Testing greylisting response handling' }); const result = await smtpClient.sendMail(email); // Should get a failed result due to greylisting expect(result.success).toBeFalse(); console.log('Actual error:', result.error?.message); expect(result.error?.message).toMatch(/451|greylist|rejected/i); console.log('✅ Greylisting response handled correctly'); await smtpClient.close(); await new Promise((resolve) => { greylistServer.close(() => resolve()); }); }); tap.test('CERR-04: Different greylisting response codes', async () => { // Test recognition of various greylisting response patterns const greylistResponses = [ { code: '451 4.7.1', message: 'Greylisting in effect, please retry', isGreylist: true }, { code: '450 4.7.1', message: 'Try again later', isGreylist: true }, { code: '451 4.7.0', message: 'Temporary rejection', isGreylist: true }, { code: '421 4.7.0', message: 'Too many connections, try later', isGreylist: false }, { code: '452 4.2.2', message: 'Mailbox full', isGreylist: false }, { code: '451', message: 'Requested action aborted', isGreylist: false } ]; console.log('Testing greylisting response recognition:'); for (const response of greylistResponses) { console.log(`Response: ${response.code} ${response.message}`); // Check if response matches greylisting patterns const isGreylistPattern = (response.code.startsWith('450') || response.code.startsWith('451')) && (response.message.toLowerCase().includes('grey') || response.message.toLowerCase().includes('try') || response.message.toLowerCase().includes('later') || response.message.toLowerCase().includes('temporary') || response.code.includes('4.7.')); console.log(` Detected as greylisting: ${isGreylistPattern}`); console.log(` Expected: ${response.isGreylist}`); expect(isGreylistPattern).toEqual(response.isGreylist); } }); tap.test('CERR-04: Greylisting with temporary failure', async () => { // Create server that sends 450 response (temporary failure) const tempFailServer = net.createServer((socket) => { socket.write('220 Temp Fail 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('450 4.7.1 Mailbox temporarily unavailable\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); }); await new Promise((resolve) => { tempFailServer.listen(2561, () => resolve()); }); const smtpClient = await createSmtpClient({ host: '127.0.0.1', port: 2561, secure: false, connectionTimeout: 5000 }); const email = new Email({ from: 'sender@example.com', to: 'recipient@example.com', subject: '450 Test', text: 'Testing 450 temporary failure response' }); const result = await smtpClient.sendMail(email); expect(result.success).toBeFalse(); console.log('Actual error:', result.error?.message); expect(result.error?.message).toMatch(/450|temporary|rejected/i); console.log('✅ 450 temporary failure handled'); await smtpClient.close(); await new Promise((resolve) => { tempFailServer.close(() => resolve()); }); }); tap.test('CERR-04: Greylisting with multiple recipients', async () => { // Test successful email send to multiple recipients on working server const smtpClient = await createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, connectionTimeout: 5000 }); const email = new Email({ from: 'sender@example.com', to: ['user1@normal.com', 'user2@example.com'], subject: 'Multi-recipient Test', text: 'Testing multiple recipients' }); const result = await smtpClient.sendMail(email); expect(result.success).toBeTrue(); console.log('✅ Multiple recipients handled correctly'); await smtpClient.close(); }); tap.test('CERR-04: Basic connection verification', async () => { const smtpClient = await createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, connectionTimeout: 5000 }); const result = await smtpClient.verify(); expect(result).toBeTrue(); console.log('✅ Connection verification successful'); await smtpClient.close(); }); tap.test('CERR-04: Server with RCPT rejection', async () => { // Test server rejecting at RCPT TO stage 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')) { socket.write('451 4.2.1 Recipient rejected temporarily\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); }); await new Promise((resolve) => { rejectServer.listen(2562, () => resolve()); }); const smtpClient = await createSmtpClient({ host: '127.0.0.1', port: 2562, secure: false, connectionTimeout: 5000 }); const email = new Email({ from: 'sender@example.com', to: 'recipient@example.com', subject: 'RCPT Rejection Test', text: 'Testing RCPT TO rejection' }); const result = await smtpClient.sendMail(email); expect(result.success).toBeFalse(); console.log('Actual error:', result.error?.message); expect(result.error?.message).toMatch(/451|reject|recipient/i); console.log('✅ RCPT rejection handled correctly'); await smtpClient.close(); await new Promise((resolve) => { rejectServer.close(() => resolve()); }); }); tap.test('cleanup - stop SMTP server', async () => { await stopTestServer(testServer); }); export default tap.start();