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 type { SmtpClient } from '../../../ts/mail/delivery/smtpclient/smtp-client.js'; import { Email } from '../../../ts/mail/core/classes.email.js'; let testServer: ITestServer; let smtpClient: SmtpClient; tap.test('setup - start SMTP server with STARTTLS support', async () => { testServer = await startTestServer({ port: 2528, tlsEnabled: true, // Enables STARTTLS capability authRequired: false }); expect(testServer.port).toEqual(2528); }); tap.test('CCM-03: STARTTLS Upgrade - should upgrade plain connection to TLS', async () => { const startTime = Date.now(); try { // Create SMTP client starting with plain connection smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, // Start with plain connection connectionTimeout: 10000, tls: { rejectUnauthorized: false // For self-signed test certificates }, debug: true }); // The client should automatically upgrade to TLS via STARTTLS const isConnected = await smtpClient.verify(); expect(isConnected).toBeTrue(); const duration = Date.now() - startTime; console.log(`✅ STARTTLS upgrade completed in ${duration}ms`); } catch (error) { const duration = Date.now() - startTime; console.error(`❌ STARTTLS upgrade failed after ${duration}ms:`, error); throw error; } }); tap.test('CCM-03: STARTTLS Upgrade - should send email after upgrade', async () => { const email = new Email({ from: 'test@example.com', to: 'recipient@example.com', subject: 'STARTTLS Upgrade Test', text: 'This email was sent after STARTTLS upgrade', html: '
This email was sent after STARTTLS upgrade
' }); const result = await smtpClient.sendMail(email); expect(result.success).toBeTrue(); expect(result.acceptedRecipients).toContain('recipient@example.com'); expect(result.rejectedRecipients.length).toEqual(0); console.log('✅ Email sent successfully after STARTTLS upgrade'); console.log('📧 Message ID:', result.messageId); }); tap.test('CCM-03: STARTTLS Upgrade - should handle servers without STARTTLS', async () => { // Start a server without TLS support const plainServer = await startTestServer({ port: 2529, tlsEnabled: false // No STARTTLS support }); try { const plainClient = createSmtpClient({ host: plainServer.hostname, port: plainServer.port, secure: false, connectionTimeout: 5000, debug: true }); // Should still connect but without TLS const isConnected = await plainClient.verify(); expect(isConnected).toBeTrue(); // Send test email over plain connection const email = new Email({ from: 'test@example.com', to: 'recipient@example.com', subject: 'Plain Connection Test', text: 'This email was sent over plain connection' }); const result = await plainClient.sendMail(email); expect(result.success).toBeTrue(); await plainClient.close(); console.log('✅ Successfully handled server without STARTTLS'); } finally { await stopTestServer(plainServer); } }); tap.test('CCM-03: STARTTLS Upgrade - should respect TLS options during upgrade', async () => { const customTlsClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, // Start plain connectionTimeout: 10000, tls: { rejectUnauthorized: false // Removed specific TLS version and cipher requirements that might not be supported } }); const isConnected = await customTlsClient.verify(); expect(isConnected).toBeTrue(); // Test that we can send email with custom TLS client const email = new Email({ from: 'tls-test@example.com', to: 'recipient@example.com', subject: 'Custom TLS Options Test', text: 'Testing with custom TLS configuration' }); const result = await customTlsClient.sendMail(email); expect(result.success).toBeTrue(); await customTlsClient.close(); console.log('✅ Custom TLS options applied during STARTTLS upgrade'); }); tap.test('CCM-03: STARTTLS Upgrade - should handle upgrade failures gracefully', async () => { // Create a scenario where STARTTLS might fail // verify() returns false on failure, doesn't throw const strictTlsClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, connectionTimeout: 5000, tls: { rejectUnauthorized: true, // Strict validation with self-signed cert servername: 'wrong.hostname.com' // Wrong hostname } }); // Should return false due to certificate validation failure const result = await strictTlsClient.verify(); expect(result).toBeFalse(); await strictTlsClient.close(); console.log('✅ STARTTLS upgrade failure handled gracefully'); }); tap.test('CCM-03: STARTTLS Upgrade - should maintain connection state after upgrade', async () => { const stateClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, connectionTimeout: 10000, tls: { rejectUnauthorized: false } }); // verify() closes the connection after testing, so isConnected will be false const verified = await stateClient.verify(); expect(verified).toBeTrue(); expect(stateClient.isConnected()).toBeFalse(); // Connection closed after verify // Send multiple emails to verify connection pooling works correctly for (let i = 0; i < 3; i++) { const email = new Email({ from: 'test@example.com', to: 'recipient@example.com', subject: `STARTTLS State Test ${i + 1}`, text: `Message ${i + 1} after STARTTLS upgrade` }); const result = await stateClient.sendMail(email); expect(result.success).toBeTrue(); } // Check pool status to understand connection management const poolStatus = stateClient.getPoolStatus(); console.log('Connection pool status:', poolStatus); await stateClient.close(); console.log('✅ Connection state maintained after STARTTLS upgrade'); }); tap.test('cleanup - close SMTP client', async () => { if (smtpClient && smtpClient.isConnected()) { await smtpClient.close(); } }); tap.test('cleanup - stop SMTP server', async () => { await stopTestServer(testServer); }); tap.start();