dcrouter/test/suite/smtpclient_commands/test.ccmd-02.mail-from-parameters.ts

277 lines
8.0 KiB
TypeScript
Raw Permalink 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 for MAIL FROM tests', async () => {
testServer = await startTestServer({
port: 2541,
tlsEnabled: false,
authRequired: false,
size: 10 * 1024 * 1024 // 10MB size limit
});
expect(testServer.port).toEqual(2541);
});
tap.test('setup - create SMTP client', async () => {
smtpClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 5000,
debug: true
});
const isConnected = await smtpClient.verify();
expect(isConnected).toBeTrue();
});
tap.test('CCMD-02: MAIL FROM - should send basic MAIL FROM command', async () => {
const email = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Basic MAIL FROM Test',
text: 'Testing basic MAIL FROM command'
});
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
expect(result.envelope?.from).toEqual('sender@example.com');
console.log('✅ Basic MAIL FROM command sent successfully');
});
tap.test('CCMD-02: MAIL FROM - should handle display names correctly', async () => {
const email = new Email({
from: 'John Doe <john.doe@example.com>',
to: 'Jane Smith <jane.smith@example.com>',
subject: 'Display Name Test',
text: 'Testing MAIL FROM with display names'
});
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
// Envelope should contain only email address, not display name
expect(result.envelope?.from).toEqual('john.doe@example.com');
console.log('✅ Display names handled correctly in MAIL FROM');
});
tap.test('CCMD-02: MAIL FROM - should handle SIZE parameter if server supports it', async () => {
// Send a larger email to test SIZE parameter
const largeContent = 'x'.repeat(1000000); // 1MB of content
const email = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'SIZE Parameter Test',
text: largeContent
});
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
console.log('✅ SIZE parameter handled for large email');
});
tap.test('CCMD-02: MAIL FROM - should handle international email addresses', async () => {
const email = new Email({
from: 'user@例え.jp',
to: 'recipient@example.com',
subject: 'International Domain Test',
text: 'Testing international domains in MAIL FROM'
});
try {
const result = await smtpClient.sendMail(email);
if (result.success) {
console.log('✅ International domain accepted');
expect(result.envelope?.from).toContain('@');
}
} catch (error) {
// Some servers may not support international domains
console.log(' Server does not support international domains');
}
});
tap.test('CCMD-02: MAIL FROM - should handle empty return path (bounce address)', async () => {
const email = new Email({
from: '<>', // Empty return path for bounces
to: 'recipient@example.com',
subject: 'Bounce Message Test',
text: 'This is a bounce message with empty return path'
});
try {
const result = await smtpClient.sendMail(email);
if (result.success) {
console.log('✅ Empty return path accepted for bounce');
expect(result.envelope?.from).toEqual('');
}
} catch (error) {
console.log(' Server rejected empty return path');
}
});
tap.test('CCMD-02: MAIL FROM - should handle special characters in local part', async () => {
const specialEmails = [
'user+tag@example.com',
'first.last@example.com',
'user_name@example.com',
'user-name@example.com'
];
for (const fromEmail of specialEmails) {
const email = new Email({
from: fromEmail,
to: 'recipient@example.com',
subject: 'Special Character Test',
text: `Testing special characters in: ${fromEmail}`
});
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
expect(result.envelope?.from).toEqual(fromEmail);
console.log(`✅ Special character email accepted: ${fromEmail}`);
}
});
tap.test('CCMD-02: MAIL FROM - should reject invalid sender addresses', async () => {
const invalidSenders = [
'no-at-sign',
'@example.com',
'user@',
'user@@example.com',
'user@.com',
'user@example.',
'user with spaces@example.com'
];
let rejectedCount = 0;
for (const invalidSender of invalidSenders) {
try {
const email = new Email({
from: invalidSender,
to: 'recipient@example.com',
subject: 'Invalid Sender Test',
text: 'This should fail'
});
await smtpClient.sendMail(email);
} catch (error) {
rejectedCount++;
console.log(`✅ Invalid sender rejected: ${invalidSender}`);
}
}
expect(rejectedCount).toBeGreaterThan(0);
});
tap.test('CCMD-02: MAIL FROM - should handle 8BITMIME parameter', async () => {
const email = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'UTF-8 Test with special characters',
text: 'This email contains UTF-8 characters: 你好世界 🌍',
html: '<p>UTF-8 content: <strong>你好世界</strong> 🌍</p>'
});
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
console.log('✅ 8BITMIME content handled correctly');
});
tap.test('CCMD-02: MAIL FROM - should handle AUTH parameter if authenticated', async () => {
2025-05-25 11:18:12 +00:00
// Create authenticated client - auth requires TLS per RFC 8314
2025-05-24 16:19:19 +00:00
const authServer = await startTestServer({
port: 2542,
2025-05-25 11:18:12 +00:00
tlsEnabled: true,
2025-05-24 16:19:19 +00:00
authRequired: true
});
const authClient = createSmtpClient({
host: authServer.hostname,
port: authServer.port,
2025-05-26 04:09:29 +00:00
secure: false, // Use STARTTLS instead of direct TLS
requireTLS: true, // Require TLS upgrade
2025-05-25 11:18:12 +00:00
tls: {
rejectUnauthorized: false // Accept self-signed cert for testing
},
2025-05-24 16:19:19 +00:00
auth: {
user: 'testuser',
pass: 'testpass'
},
2025-05-26 04:09:29 +00:00
connectionTimeout: 5000,
debug: true
2025-05-24 16:19:19 +00:00
});
2025-05-26 04:09:29 +00:00
try {
const email = new Email({
from: 'authenticated@example.com',
to: 'recipient@example.com',
subject: 'AUTH Parameter Test',
text: 'Sent with authentication'
});
const result = await authClient.sendMail(email);
expect(result.success).toBeTrue();
console.log('✅ AUTH parameter handled in MAIL FROM');
} catch (error) {
console.error('AUTH test error:', error);
throw error;
} finally {
await authClient.close();
await stopTestServer(authServer);
}
2025-05-24 16:19:19 +00:00
});
tap.test('CCMD-02: MAIL FROM - should handle very long email addresses', async () => {
// RFC allows up to 320 characters total (64 + @ + 255)
const longLocal = 'a'.repeat(64);
const longDomain = 'subdomain.' + 'a'.repeat(60) + '.example.com';
const longEmail = `${longLocal}@${longDomain}`;
const email = new Email({
from: longEmail,
to: 'recipient@example.com',
subject: 'Long Email Address Test',
text: 'Testing maximum length email addresses'
});
try {
const result = await smtpClient.sendMail(email);
if (result.success) {
console.log('✅ Long email address accepted');
expect(result.envelope?.from).toEqual(longEmail);
}
} catch (error) {
console.log(' Server enforces email length limits');
}
});
tap.test('cleanup - close SMTP client', async () => {
if (smtpClient && smtpClient.isConnected()) {
await smtpClient.close();
}
});
tap.test('cleanup - stop SMTP server', async () => {
await stopTestServer(testServer);
});
2025-05-25 19:05:43 +00:00
export default tap.start();