dcrouter/test/suite/smtpclient_commands/test.ccmd-05.auth-mechanisms.ts
2025-05-26 04:09:29 +00:00

306 lines
8.8 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 () => {
const 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'
});
const result = await noAuthClient.sendMail(email);
expect(result.success).toBeFalse();
expect(result.error).toBeInstanceOf(Error);
expect(result.error?.message).toContain('Authentication required');
console.log('✅ Authentication required error:', result.error?.message);
await noAuthClient.close();
});
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 () => {
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'
}
});
const isConnected = await badAuthClient.verify();
expect(isConnected).toBeFalse();
console.log('✅ Invalid credentials rejected');
await badAuthClient.close();
});
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);
// Debug output to understand failures
results.forEach((result, index) => {
if (!result.success) {
console.log(`❌ Email ${index} failed:`, result.error?.message);
}
});
const successCount = results.filter(r => r.success).length;
console.log(`📧 Sent ${successCount} of ${results.length} emails successfully`);
const poolStatus = pooledAuthClient.getPoolStatus();
console.log('📊 Auth pool status:', poolStatus);
// Check that at least one email was sent (connection pooling might limit concurrent sends)
expect(successCount).toBeGreaterThan(0);
await pooledAuthClient.close();
console.log('✅ Authentication works with connection pooling');
});
tap.test('cleanup - stop auth server', async () => {
await stopTestServer(authServer);
});
export default tap.start();