dcrouter/test/suite/smtpclient_connection/test.ccm-03.starttls-upgrade.ts

200 lines
6.0 KiB
TypeScript
Raw Normal View History

2025-05-24 16:19:19 +00:00
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: '<p>This email was sent after <strong>STARTTLS upgrade</strong></p>'
});
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,
minVersion: 'TLSv1.2',
ciphers: 'HIGH:!aNULL:!MD5'
}
});
const isConnected = await customTlsClient.verify();
expect(isConnected).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 mock scenario where STARTTLS might fail
// This would typically happen with certificate issues or protocol mismatches
let errorCaught = false;
try {
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
}
});
await strictTlsClient.verify();
await strictTlsClient.close();
} catch (error) {
errorCaught = true;
expect(error).toBeInstanceOf(Error);
console.log('✅ STARTTLS upgrade failure handled gracefully');
}
// Should fail due to certificate validation
expect(errorCaught).toBeTrue();
});
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
}
});
// Connect and verify
await stateClient.verify();
expect(stateClient.isConnected()).toBeTrue();
// Send multiple emails to verify connection remains stable
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();
}
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();