import { tap, expect } from '@git.zone/tstest/tapbundle'; import * as plugins from '../ts/plugins.js'; import * as paths from '../ts/paths.js'; import { smtpClientMod } from '../ts/mail/delivery/index.js'; import type { ISmtpClientOptions, SmtpClient } from '../ts/mail/delivery/smtpclient/index.js'; import { Email } from '../ts/mail/core/classes.email.js'; /** * Tests for the SMTP client class */ tap.test('verify SMTP client initialization', async () => { // Create test configuration const options: ISmtpClientOptions = { host: 'smtp.example.com', port: 587, secure: false, connectionTimeout: 10000, domain: 'test.example.com' }; // Create SMTP client instance const smtpClient = smtpClientMod.createSmtpClient(options); // Verify instance was created correctly expect(smtpClient).toBeTruthy(); expect(smtpClient.isConnected()).toBeFalsy(); // Should start disconnected }); tap.test('test SMTP client configuration update', async () => { // Create test configuration const options: ISmtpClientOptions = { host: 'smtp.example.com', port: 587, secure: false }; // Create SMTP client instance const smtpClient = smtpClientMod.createSmtpClient(options); // Update configuration smtpClient.updateOptions({ host: 'new-smtp.example.com', port: 465, secure: true }); // Can't directly test private fields, but we can verify it doesn't throw expect(() => smtpClient.updateOptions({ tls: { rejectUnauthorized: false } })).not.toThrow(); }); // Mocked SMTP server for testing class MockSmtpServer { private responses: Map; constructor() { this.responses = new Map(); // Default responses this.responses.set('connect', '220 smtp.example.com ESMTP ready'); this.responses.set('EHLO', '250-smtp.example.com\r\n250-PIPELINING\r\n250-SIZE 10240000\r\n250-STARTTLS\r\n250-AUTH PLAIN LOGIN\r\n250 HELP'); this.responses.set('MAIL FROM', '250 OK'); this.responses.set('RCPT TO', '250 OK'); this.responses.set('DATA', '354 Start mail input; end with .'); this.responses.set('data content', '250 OK: message accepted'); this.responses.set('QUIT', '221 Bye'); } public setResponse(command: string, response: string): void { this.responses.set(command, response); } public getResponse(command: string): string { if (command.startsWith('MAIL FROM')) { return this.responses.get('MAIL FROM') || '250 OK'; } else if (command.startsWith('RCPT TO')) { return this.responses.get('RCPT TO') || '250 OK'; } else if (command.startsWith('EHLO') || command.startsWith('HELO')) { return this.responses.get('EHLO') || '250 OK'; } else if (command === 'DATA') { return this.responses.get('DATA') || '354 Start mail input; end with .'; } else if (command.includes('Content-Type')) { return this.responses.get('data content') || '250 OK: message accepted'; } else if (command === 'QUIT') { return this.responses.get('QUIT') || '221 Bye'; } return this.responses.get(command) || '250 OK'; } } /** * This test validates the SMTP client public interface */ tap.test('verify SMTP client email delivery functionality with mock', async () => { // Create a test email const testEmail = new Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'Test Email', text: 'This is a test email' }); // Create SMTP client options const options: ISmtpClientOptions = { host: 'smtp.example.com', port: 587, secure: false, domain: 'test.example.com', auth: { user: 'testuser', pass: 'testpass' } }; // Create SMTP client instance const smtpClient = smtpClientMod.createSmtpClient(options); // Test public methods exist and have correct signatures expect(typeof smtpClient.sendMail).toEqual('function'); expect(typeof smtpClient.verify).toEqual('function'); expect(typeof smtpClient.isConnected).toEqual('function'); expect(typeof smtpClient.getPoolStatus).toEqual('function'); expect(typeof smtpClient.updateOptions).toEqual('function'); expect(typeof smtpClient.close).toEqual('function'); // Test connection status before any operation expect(smtpClient.isConnected()).toBeFalsy(); // Test pool status const poolStatus = smtpClient.getPoolStatus(); expect(poolStatus).toBeTruthy(); expect(typeof poolStatus.active).toEqual('number'); expect(typeof poolStatus.idle).toEqual('number'); expect(typeof poolStatus.total).toEqual('number'); // Since we can't connect to a real server, we'll skip the actual send test // and just verify the client was created correctly expect(smtpClient).toBeTruthy(); }); tap.test('test SMTP client error handling with mock', async () => { // Create SMTP client instance const smtpClient = smtpClientMod.createSmtpClient({ host: 'smtp.example.com', port: 587, secure: false }); // Test with valid email (Email class might allow any string) const testEmail = new Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'Test Email', text: 'This is a test email' }); // Test event listener methods const mockListener = () => {}; smtpClient.on('test-event', mockListener); smtpClient.off('test-event', mockListener); // Test update options smtpClient.updateOptions({ auth: { user: 'newuser', pass: 'newpass' } }); // Verify client is still functional expect(smtpClient.isConnected()).toBeFalsy(); // Test close on a non-connected client await smtpClient.close(); expect(smtpClient.isConnected()).toBeFalsy(); }); // Final clean-up test tap.test('clean up after tests', async () => { // No-op - just to make sure everything is cleaned up properly }); tap.test('stop', async () => { await tap.stopForcefully(); }); export default tap.start();