277 lines
8.0 KiB
TypeScript
277 lines
8.0 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 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 () => {
|
||
// Create authenticated client - auth requires TLS per RFC 8314
|
||
const authServer = await startTestServer({
|
||
port: 2542,
|
||
tlsEnabled: true,
|
||
authRequired: true
|
||
});
|
||
|
||
const authClient = createSmtpClient({
|
||
host: authServer.hostname,
|
||
port: authServer.port,
|
||
secure: false, // Use STARTTLS instead of direct TLS
|
||
requireTLS: true, // Require TLS upgrade
|
||
tls: {
|
||
rejectUnauthorized: false // Accept self-signed cert for testing
|
||
},
|
||
auth: {
|
||
user: 'testuser',
|
||
pass: 'testpass'
|
||
},
|
||
connectionTimeout: 5000,
|
||
debug: true
|
||
});
|
||
|
||
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);
|
||
}
|
||
});
|
||
|
||
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);
|
||
});
|
||
|
||
export default tap.start(); |