update
This commit is contained in:
281
test/suite/smtpclient_commands/test.ccmd-05.auth-mechanisms.ts
Normal file
281
test/suite/smtpclient_commands/test.ccmd-05.auth-mechanisms.ts
Normal file
@ -0,0 +1,281 @@
|
||||
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: false,
|
||||
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;
|
||||
|
||||
try {
|
||||
const noAuthClient = createSmtpClient({
|
||||
host: authServer.hostname,
|
||||
port: authServer.port,
|
||||
secure: false,
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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: true,
|
||||
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,
|
||||
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,
|
||||
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();
|
Reference in New Issue
Block a user