274 lines
8.3 KiB
TypeScript
274 lines
8.3 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 DATA command tests', async () => {
|
||
testServer = await startTestServer({
|
||
port: 2544,
|
||
tlsEnabled: false,
|
||
authRequired: false,
|
||
size: 10 * 1024 * 1024 // 10MB message size limit
|
||
});
|
||
|
||
expect(testServer.port).toEqual(2544);
|
||
});
|
||
|
||
tap.test('setup - create SMTP client', async () => {
|
||
smtpClient = createSmtpClient({
|
||
host: testServer.hostname,
|
||
port: testServer.port,
|
||
secure: false,
|
||
connectionTimeout: 5000,
|
||
socketTimeout: 30000, // Longer timeout for data transmission
|
||
debug: true
|
||
});
|
||
|
||
const isConnected = await smtpClient.verify();
|
||
expect(isConnected).toBeTrue();
|
||
});
|
||
|
||
tap.test('CCMD-04: DATA - should transmit simple text email', async () => {
|
||
const email = new Email({
|
||
from: 'sender@example.com',
|
||
to: 'recipient@example.com',
|
||
subject: 'Simple DATA Test',
|
||
text: 'This is a simple text email transmitted via DATA command.'
|
||
});
|
||
|
||
const result = await smtpClient.sendMail(email);
|
||
|
||
expect(result.success).toBeTrue();
|
||
expect(result.response).toBeTypeofString();
|
||
|
||
console.log('✅ Simple text email transmitted successfully');
|
||
console.log('📧 Server response:', result.response);
|
||
});
|
||
|
||
tap.test('CCMD-04: DATA - should handle dot stuffing', async () => {
|
||
// Lines starting with dots should be escaped
|
||
const email = new Email({
|
||
from: 'sender@example.com',
|
||
to: 'recipient@example.com',
|
||
subject: 'Dot Stuffing Test',
|
||
text: 'This email tests dot stuffing:\n.This line starts with a dot\n..So does this one\n...And this one'
|
||
});
|
||
|
||
const result = await smtpClient.sendMail(email);
|
||
|
||
expect(result.success).toBeTrue();
|
||
console.log('✅ Dot stuffing handled correctly');
|
||
});
|
||
|
||
tap.test('CCMD-04: DATA - should transmit HTML email', async () => {
|
||
const email = new Email({
|
||
from: 'sender@example.com',
|
||
to: 'recipient@example.com',
|
||
subject: 'HTML Email Test',
|
||
text: 'This is the plain text version',
|
||
html: `
|
||
<html>
|
||
<head>
|
||
<title>HTML Email Test</title>
|
||
</head>
|
||
<body>
|
||
<h1>HTML Email</h1>
|
||
<p>This is an <strong>HTML</strong> email with:</p>
|
||
<ul>
|
||
<li>Lists</li>
|
||
<li>Formatting</li>
|
||
<li>Links: <a href="https://example.com">Example</a></li>
|
||
</ul>
|
||
</body>
|
||
</html>
|
||
`
|
||
});
|
||
|
||
const result = await smtpClient.sendMail(email);
|
||
|
||
expect(result.success).toBeTrue();
|
||
console.log('✅ HTML email transmitted successfully');
|
||
});
|
||
|
||
tap.test('CCMD-04: DATA - should handle large message body', async () => {
|
||
// Create a large message (1MB)
|
||
const largeText = 'This is a test line that will be repeated many times.\n'.repeat(20000);
|
||
|
||
const email = new Email({
|
||
from: 'sender@example.com',
|
||
to: 'recipient@example.com',
|
||
subject: 'Large Message Test',
|
||
text: largeText
|
||
});
|
||
|
||
const startTime = Date.now();
|
||
const result = await smtpClient.sendMail(email);
|
||
const duration = Date.now() - startTime;
|
||
|
||
expect(result.success).toBeTrue();
|
||
console.log(`✅ Large message (${Math.round(largeText.length / 1024)}KB) transmitted in ${duration}ms`);
|
||
});
|
||
|
||
tap.test('CCMD-04: DATA - should handle binary attachments', async () => {
|
||
// Create a binary attachment
|
||
const binaryData = Buffer.alloc(1024);
|
||
for (let i = 0; i < binaryData.length; i++) {
|
||
binaryData[i] = i % 256;
|
||
}
|
||
|
||
const email = new Email({
|
||
from: 'sender@example.com',
|
||
to: 'recipient@example.com',
|
||
subject: 'Binary Attachment Test',
|
||
text: 'This email contains a binary attachment',
|
||
attachments: [{
|
||
filename: 'test.bin',
|
||
content: binaryData,
|
||
contentType: 'application/octet-stream'
|
||
}]
|
||
});
|
||
|
||
const result = await smtpClient.sendMail(email);
|
||
|
||
expect(result.success).toBeTrue();
|
||
console.log('✅ Binary attachment transmitted successfully');
|
||
});
|
||
|
||
tap.test('CCMD-04: DATA - should handle special characters and encoding', async () => {
|
||
const email = new Email({
|
||
from: 'sender@example.com',
|
||
to: 'recipient@example.com',
|
||
subject: 'Special Characters Test – "Quotes" & More',
|
||
text: 'Special characters: © ® ™ € £ ¥ • … « » " " \' \'',
|
||
html: '<p>Unicode: 你好世界 🌍 🚀 ✉️</p>'
|
||
});
|
||
|
||
const result = await smtpClient.sendMail(email);
|
||
|
||
expect(result.success).toBeTrue();
|
||
console.log('✅ Special characters and Unicode handled correctly');
|
||
});
|
||
|
||
tap.test('CCMD-04: DATA - should handle line length limits', async () => {
|
||
// RFC 5321 specifies 1000 character line limit (including CRLF)
|
||
const longLine = 'a'.repeat(990); // Leave room for CRLF and safety
|
||
|
||
const email = new Email({
|
||
from: 'sender@example.com',
|
||
to: 'recipient@example.com',
|
||
subject: 'Long Line Test',
|
||
text: `Short line\n${longLine}\nAnother short line`
|
||
});
|
||
|
||
const result = await smtpClient.sendMail(email);
|
||
|
||
expect(result.success).toBeTrue();
|
||
console.log('✅ Long lines handled within RFC limits');
|
||
});
|
||
|
||
tap.test('CCMD-04: DATA - should handle empty message body', async () => {
|
||
const email = new Email({
|
||
from: 'sender@example.com',
|
||
to: 'recipient@example.com',
|
||
subject: 'Empty Body Test',
|
||
text: '' // Empty body
|
||
});
|
||
|
||
const result = await smtpClient.sendMail(email);
|
||
|
||
expect(result.success).toBeTrue();
|
||
console.log('✅ Empty message body handled correctly');
|
||
});
|
||
|
||
tap.test('CCMD-04: DATA - should handle CRLF line endings', async () => {
|
||
const email = new Email({
|
||
from: 'sender@example.com',
|
||
to: 'recipient@example.com',
|
||
subject: 'CRLF Test',
|
||
text: 'Line 1\r\nLine 2\r\nLine 3\nLine 4 (LF only)\r\nLine 5'
|
||
});
|
||
|
||
const result = await smtpClient.sendMail(email);
|
||
|
||
expect(result.success).toBeTrue();
|
||
console.log('✅ Mixed line endings normalized to CRLF');
|
||
});
|
||
|
||
tap.test('CCMD-04: DATA - should handle message headers correctly', async () => {
|
||
const email = new Email({
|
||
from: 'sender@example.com',
|
||
to: 'recipient@example.com',
|
||
cc: 'cc@example.com',
|
||
subject: 'Header Test',
|
||
text: 'Testing header transmission',
|
||
priority: 'high',
|
||
headers: {
|
||
'X-Custom-Header': 'custom-value',
|
||
'X-Mailer': 'SMTP Client Test Suite',
|
||
'Reply-To': 'replies@example.com'
|
||
}
|
||
});
|
||
|
||
const result = await smtpClient.sendMail(email);
|
||
|
||
expect(result.success).toBeTrue();
|
||
console.log('✅ All headers transmitted in DATA command');
|
||
});
|
||
|
||
tap.test('CCMD-04: DATA - should handle timeout for slow transmission', async () => {
|
||
// Create a very large message to test timeout handling
|
||
const hugeText = 'x'.repeat(5 * 1024 * 1024); // 5MB
|
||
|
||
const email = new Email({
|
||
from: 'sender@example.com',
|
||
to: 'recipient@example.com',
|
||
subject: 'Timeout Test',
|
||
text: hugeText
|
||
});
|
||
|
||
// Should complete within socket timeout
|
||
const startTime = Date.now();
|
||
const result = await smtpClient.sendMail(email);
|
||
const duration = Date.now() - startTime;
|
||
|
||
expect(result.success).toBeTrue();
|
||
expect(duration).toBeLessThan(30000); // Should complete within socket timeout
|
||
|
||
console.log(`✅ Large data transmission completed in ${duration}ms`);
|
||
});
|
||
|
||
tap.test('CCMD-04: DATA - should handle server rejection after DATA', async () => {
|
||
// Some servers might reject after seeing content
|
||
const email = new Email({
|
||
from: 'spam@spammer.com',
|
||
to: 'recipient@example.com',
|
||
subject: 'Potential Spam Test',
|
||
text: 'BUY NOW! SPECIAL OFFER! CLICK HERE!',
|
||
mightBeSpam: true // Flag as potential spam
|
||
});
|
||
|
||
const result = await smtpClient.sendMail(email);
|
||
|
||
// Test server might accept or reject
|
||
if (result.success) {
|
||
console.log('ℹ️ Test server accepted potential spam (normal for test)');
|
||
} else {
|
||
console.log('✅ Server can reject messages after DATA inspection');
|
||
}
|
||
});
|
||
|
||
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(); |