208 lines
6.5 KiB
TypeScript
208 lines
6.5 KiB
TypeScript
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
|
|
// 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);
|
|
});
|
|
|
|
export default tap.start(); |