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 authServer: ITestServer; tap.test('setup - start SMTP server with authentication', async () => { authServer = await startTestServer({ port: 2580, tlsEnabled: true, // Enable STARTTLS capability authRequired: true }); expect(authServer.port).toEqual(2580); expect(authServer.config.authRequired).toBeTrue(); }); tap.test('CCMD-05: AUTH - should fail without credentials', async () => { let errorCaught = false; let noAuthClient: SmtpClient | null = null; try { noAuthClient = createSmtpClient({ host: authServer.hostname, port: authServer.port, secure: false, // Start plain, upgrade with STARTTLS tls: { rejectUnauthorized: false // Accept self-signed certs for testing }, connectionTimeout: 5000 // No auth provided }); const email = new Email({ from: 'sender@example.com', to: 'recipient@example.com', subject: 'No Auth Test', text: 'Should fail without authentication' }); await noAuthClient.sendMail(email); } catch (error: any) { errorCaught = true; expect(error).toBeInstanceOf(Error); console.log('✅ Authentication required error:', error.message); } finally { // Ensure client is closed even if test fails if (noAuthClient) { await noAuthClient.close(); } } expect(errorCaught).toBeTrue(); }); tap.test('CCMD-05: AUTH - should authenticate with PLAIN mechanism', async () => { const plainAuthClient = createSmtpClient({ host: authServer.hostname, port: authServer.port, secure: false, // Start plain, upgrade with STARTTLS tls: { rejectUnauthorized: false // Accept self-signed certs for testing }, connectionTimeout: 5000, auth: { user: 'testuser', pass: 'testpass', method: 'PLAIN' }, debug: true }); const isConnected = await plainAuthClient.verify(); expect(isConnected).toBeTrue(); const email = new Email({ from: 'sender@example.com', to: 'recipient@example.com', subject: 'PLAIN Auth Test', text: 'Sent with PLAIN authentication' }); const result = await plainAuthClient.sendMail(email); expect(result.success).toBeTrue(); await plainAuthClient.close(); console.log('✅ PLAIN authentication successful'); }); tap.test('CCMD-05: AUTH - should authenticate with LOGIN mechanism', async () => { const loginAuthClient = createSmtpClient({ host: authServer.hostname, port: authServer.port, secure: false, // Start plain, upgrade with STARTTLS tls: { rejectUnauthorized: false // Accept self-signed certs for testing }, connectionTimeout: 5000, auth: { user: 'testuser', pass: 'testpass', method: 'LOGIN' }, debug: true }); const isConnected = await loginAuthClient.verify(); expect(isConnected).toBeTrue(); const email = new Email({ from: 'sender@example.com', to: 'recipient@example.com', subject: 'LOGIN Auth Test', text: 'Sent with LOGIN authentication' }); const result = await loginAuthClient.sendMail(email); expect(result.success).toBeTrue(); await loginAuthClient.close(); console.log('✅ LOGIN authentication successful'); }); tap.test('CCMD-05: AUTH - should auto-select authentication method', async () => { const autoAuthClient = createSmtpClient({ host: authServer.hostname, port: authServer.port, secure: false, // Start plain, upgrade with STARTTLS tls: { rejectUnauthorized: false // Accept self-signed certs for testing }, connectionTimeout: 5000, auth: { user: 'testuser', pass: 'testpass' // No method specified - should auto-select } }); const isConnected = await autoAuthClient.verify(); expect(isConnected).toBeTrue(); await autoAuthClient.close(); console.log('✅ Auto-selected authentication method'); }); tap.test('CCMD-05: AUTH - should handle invalid credentials', async () => { let authFailed = false; try { const badAuthClient = createSmtpClient({ host: authServer.hostname, port: authServer.port, secure: false, // Start plain, upgrade with STARTTLS tls: { rejectUnauthorized: false // Accept self-signed certs for testing }, connectionTimeout: 5000, auth: { user: 'wronguser', pass: 'wrongpass' } }); await badAuthClient.verify(); } catch (error: any) { authFailed = true; expect(error).toBeInstanceOf(Error); console.log('✅ Invalid credentials rejected:', error.message); } expect(authFailed).toBeTrue(); }); tap.test('CCMD-05: AUTH - should handle special characters in credentials', async () => { const specialAuthClient = createSmtpClient({ host: authServer.hostname, port: authServer.port, secure: false, // Start plain, upgrade with STARTTLS tls: { rejectUnauthorized: false // Accept self-signed certs for testing }, connectionTimeout: 5000, auth: { user: 'user@domain.com', pass: 'p@ssw0rd!#$%' } }); // Server might accept or reject based on implementation try { await specialAuthClient.verify(); await specialAuthClient.close(); console.log('✅ Special characters in credentials handled'); } catch (error) { console.log('ℹ️ Test server rejected special character credentials'); } }); tap.test('CCMD-05: AUTH - should prefer secure auth over TLS', async () => { // Start TLS-enabled server const tlsAuthServer = await startTestServer({ port: 2581, tlsEnabled: true, authRequired: true }); const tlsAuthClient = createSmtpClient({ host: tlsAuthServer.hostname, port: tlsAuthServer.port, secure: false, // Use STARTTLS connectionTimeout: 5000, auth: { user: 'testuser', pass: 'testpass' }, tls: { rejectUnauthorized: false } }); const isConnected = await tlsAuthClient.verify(); expect(isConnected).toBeTrue(); await tlsAuthClient.close(); await stopTestServer(tlsAuthServer); console.log('✅ Secure authentication over TLS'); }); tap.test('CCMD-05: AUTH - should maintain auth state across multiple sends', async () => { const persistentAuthClient = createSmtpClient({ host: authServer.hostname, port: authServer.port, secure: false, // Start plain, upgrade with STARTTLS tls: { rejectUnauthorized: false // Accept self-signed certs for testing }, connectionTimeout: 5000, auth: { user: 'testuser', pass: 'testpass' } }); await persistentAuthClient.verify(); // Send multiple emails without re-authenticating for (let i = 0; i < 3; i++) { const email = new Email({ from: 'sender@example.com', to: 'recipient@example.com', subject: `Persistent Auth Test ${i + 1}`, text: `Email ${i + 1} using same auth session` }); const result = await persistentAuthClient.sendMail(email); expect(result.success).toBeTrue(); } await persistentAuthClient.close(); console.log('✅ Authentication state maintained across sends'); }); tap.test('CCMD-05: AUTH - should handle auth with connection pooling', async () => { const pooledAuthClient = createSmtpClient({ host: authServer.hostname, port: authServer.port, secure: false, // Start plain, upgrade with STARTTLS tls: { rejectUnauthorized: false // Accept self-signed certs for testing }, pool: true, maxConnections: 3, connectionTimeout: 5000, auth: { user: 'testuser', pass: 'testpass' } }); // Send concurrent emails with pooled authenticated connections const promises = []; for (let i = 0; i < 5; i++) { const email = new Email({ from: 'sender@example.com', to: `recipient${i}@example.com`, subject: `Pooled Auth Test ${i}`, text: 'Testing auth with connection pooling' }); promises.push(pooledAuthClient.sendMail(email)); } const results = await Promise.all(promises); results.forEach(result => { expect(result.success).toBeTrue(); }); const poolStatus = pooledAuthClient.getPoolStatus(); console.log('📊 Auth pool status:', poolStatus); await pooledAuthClient.close(); console.log('✅ Authentication works with connection pooling'); }); tap.test('cleanup - stop auth server', async () => { await stopTestServer(authServer); }); tap.start();