321 lines
9.6 KiB
TypeScript
321 lines
9.6 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 MIME tests', async () => {
|
|
testServer = await startTestServer({
|
|
port: 2571,
|
|
tlsEnabled: false,
|
|
authRequired: false,
|
|
size: 25 * 1024 * 1024 // 25MB for attachment tests
|
|
});
|
|
|
|
expect(testServer.port).toEqual(2571);
|
|
});
|
|
|
|
tap.test('setup - create SMTP client', async () => {
|
|
smtpClient = createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
connectionTimeout: 5000,
|
|
socketTimeout: 60000, // Longer timeout for large attachments
|
|
debug: true
|
|
});
|
|
|
|
const isConnected = await smtpClient.verify();
|
|
expect(isConnected).toBeTrue();
|
|
});
|
|
|
|
tap.test('CEP-02: MIME Multipart - should send multipart/alternative (text + HTML)', async () => {
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Multipart Alternative Test',
|
|
text: 'This is the plain text version of the email.',
|
|
html: '<html><body><h1>HTML Version</h1><p>This is the <strong>HTML version</strong> of the email.</p></body></html>'
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
|
|
expect(result.success).toBeTrue();
|
|
console.log('✅ Multipart/alternative email sent successfully');
|
|
});
|
|
|
|
tap.test('CEP-02: MIME Multipart - should send multipart/mixed with attachments', async () => {
|
|
const textAttachment = Buffer.from('This is a text file attachment content.');
|
|
const csvData = 'Name,Email,Score\nJohn Doe,john@example.com,95\nJane Smith,jane@example.com,87';
|
|
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Multipart Mixed with Attachments',
|
|
text: 'This email contains attachments.',
|
|
html: '<p>This email contains <strong>attachments</strong>.</p>',
|
|
attachments: [
|
|
{
|
|
filename: 'document.txt',
|
|
content: textAttachment,
|
|
contentType: 'text/plain'
|
|
},
|
|
{
|
|
filename: 'data.csv',
|
|
content: Buffer.from(csvData),
|
|
contentType: 'text/csv'
|
|
}
|
|
]
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
|
|
expect(result.success).toBeTrue();
|
|
console.log('✅ Multipart/mixed with attachments sent successfully');
|
|
});
|
|
|
|
tap.test('CEP-02: MIME Multipart - should handle inline images', async () => {
|
|
// Create a small test image (1x1 red pixel PNG)
|
|
const redPixelPng = Buffer.from(
|
|
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
|
|
'base64'
|
|
);
|
|
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Inline Image Test',
|
|
text: 'This email contains an inline image.',
|
|
html: '<p>Here is an inline image: <img src="cid:red-pixel" alt="Red Pixel"></p>',
|
|
attachments: [
|
|
{
|
|
filename: 'red-pixel.png',
|
|
content: redPixelPng,
|
|
contentType: 'image/png',
|
|
contentId: 'red-pixel' // Content-ID for inline reference
|
|
}
|
|
]
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
|
|
expect(result.success).toBeTrue();
|
|
console.log('✅ Email with inline image sent successfully');
|
|
});
|
|
|
|
tap.test('CEP-02: MIME Multipart - should handle multiple attachment types', async () => {
|
|
const attachments = [
|
|
{
|
|
filename: 'text.txt',
|
|
content: Buffer.from('Plain text file'),
|
|
contentType: 'text/plain'
|
|
},
|
|
{
|
|
filename: 'data.json',
|
|
content: Buffer.from(JSON.stringify({ test: 'data', value: 123 })),
|
|
contentType: 'application/json'
|
|
},
|
|
{
|
|
filename: 'binary.bin',
|
|
content: Buffer.from([0x00, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD]),
|
|
contentType: 'application/octet-stream'
|
|
},
|
|
{
|
|
filename: 'document.pdf',
|
|
content: Buffer.from('%PDF-1.4\n%fake pdf content for testing'),
|
|
contentType: 'application/pdf'
|
|
}
|
|
];
|
|
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Multiple Attachment Types',
|
|
text: 'Testing various attachment types',
|
|
attachments
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
|
|
expect(result.success).toBeTrue();
|
|
console.log('✅ Multiple attachment types handled correctly');
|
|
});
|
|
|
|
tap.test('CEP-02: MIME Multipart - should encode binary attachments with base64', async () => {
|
|
// Create binary data with all byte values
|
|
const binaryData = Buffer.alloc(256);
|
|
for (let i = 0; i < 256; i++) {
|
|
binaryData[i] = i;
|
|
}
|
|
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Binary Attachment Encoding Test',
|
|
text: 'This email contains binary data that must be base64 encoded',
|
|
attachments: [
|
|
{
|
|
filename: 'binary-data.bin',
|
|
content: binaryData,
|
|
contentType: 'application/octet-stream',
|
|
encoding: 'base64' // Explicitly specify encoding
|
|
}
|
|
]
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
|
|
expect(result.success).toBeTrue();
|
|
console.log('✅ Binary attachment base64 encoded correctly');
|
|
});
|
|
|
|
tap.test('CEP-02: MIME Multipart - should handle large attachments', async () => {
|
|
// Create a 5MB attachment
|
|
const largeData = Buffer.alloc(5 * 1024 * 1024);
|
|
for (let i = 0; i < largeData.length; i++) {
|
|
largeData[i] = i % 256;
|
|
}
|
|
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Large Attachment Test',
|
|
text: 'This email contains a large attachment',
|
|
attachments: [
|
|
{
|
|
filename: 'large-file.dat',
|
|
content: largeData,
|
|
contentType: 'application/octet-stream'
|
|
}
|
|
]
|
|
});
|
|
|
|
const startTime = Date.now();
|
|
const result = await smtpClient.sendMail(email);
|
|
const duration = Date.now() - startTime;
|
|
|
|
expect(result.success).toBeTrue();
|
|
console.log(`✅ Large attachment (5MB) sent in ${duration}ms`);
|
|
});
|
|
|
|
tap.test('CEP-02: MIME Multipart - should handle nested multipart structures', async () => {
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Complex Multipart Structure',
|
|
text: 'Plain text version',
|
|
html: '<p>HTML version with <img src="cid:logo" alt="Logo"></p>',
|
|
attachments: [
|
|
{
|
|
filename: 'logo.png',
|
|
content: Buffer.from('fake png data'),
|
|
contentType: 'image/png',
|
|
contentId: 'logo' // Inline image
|
|
},
|
|
{
|
|
filename: 'attachment.txt',
|
|
content: Buffer.from('Regular attachment'),
|
|
contentType: 'text/plain'
|
|
}
|
|
]
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
|
|
expect(result.success).toBeTrue();
|
|
console.log('✅ Nested multipart structure (mixed + related + alternative) handled');
|
|
});
|
|
|
|
tap.test('CEP-02: MIME Multipart - should handle attachment filenames with special characters', async () => {
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Special Filename Test',
|
|
text: 'Testing attachments with special filenames',
|
|
attachments: [
|
|
{
|
|
filename: 'file with spaces.txt',
|
|
content: Buffer.from('Content 1'),
|
|
contentType: 'text/plain'
|
|
},
|
|
{
|
|
filename: 'файл.txt', // Cyrillic
|
|
content: Buffer.from('Content 2'),
|
|
contentType: 'text/plain'
|
|
},
|
|
{
|
|
filename: '文件.txt', // Chinese
|
|
content: Buffer.from('Content 3'),
|
|
contentType: 'text/plain'
|
|
}
|
|
]
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
|
|
expect(result.success).toBeTrue();
|
|
console.log('✅ Special characters in filenames handled correctly');
|
|
});
|
|
|
|
tap.test('CEP-02: MIME Multipart - should handle empty attachments', async () => {
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Empty Attachment Test',
|
|
text: 'This email has an empty attachment',
|
|
attachments: [
|
|
{
|
|
filename: 'empty.txt',
|
|
content: Buffer.from(''), // Empty content
|
|
contentType: 'text/plain'
|
|
}
|
|
]
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
|
|
expect(result.success).toBeTrue();
|
|
console.log('✅ Empty attachment handled correctly');
|
|
});
|
|
|
|
tap.test('CEP-02: MIME Multipart - should respect content-type parameters', async () => {
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Content-Type Parameters Test',
|
|
text: 'Testing content-type with charset',
|
|
html: '<p>HTML with specific charset</p>',
|
|
attachments: [
|
|
{
|
|
filename: 'utf8-text.txt',
|
|
content: Buffer.from('UTF-8 text: 你好世界'),
|
|
contentType: 'text/plain; charset=utf-8'
|
|
},
|
|
{
|
|
filename: 'data.xml',
|
|
content: Buffer.from('<?xml version="1.0" encoding="UTF-8"?><root>Test</root>'),
|
|
contentType: 'application/xml; charset=utf-8'
|
|
}
|
|
]
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
|
|
expect(result.success).toBeTrue();
|
|
console.log('✅ Content-type parameters preserved correctly');
|
|
});
|
|
|
|
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(); |