dcrouter/test/suite/smtpclient_email-composition/test.cep-03.attachment-encoding.ts
2025-05-25 19:05:43 +00:00

334 lines
10 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';
import * as crypto from 'crypto';
let testServer: ITestServer;
let smtpClient: SmtpClient;
tap.test('setup - start SMTP server for attachment encoding tests', async () => {
testServer = await startTestServer({
port: 2572,
tlsEnabled: false,
authRequired: false,
size: 50 * 1024 * 1024 // 50MB for large attachment tests
});
expect(testServer.port).toEqual(2572);
});
tap.test('setup - create SMTP client', async () => {
smtpClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 5000,
socketTimeout: 120000, // 2 minutes for large attachments
debug: true
});
const isConnected = await smtpClient.verify();
expect(isConnected).toBeTrue();
});
tap.test('CEP-03: Attachment Encoding - should encode text attachment with base64', async () => {
const textContent = 'This is a test text file.\nIt contains multiple lines.\nAnd some special characters: © ® ™';
const email = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Text Attachment Base64 Test',
text: 'Email with text attachment',
attachments: [{
filename: 'test.txt',
content: Buffer.from(textContent),
contentType: 'text/plain',
encoding: 'base64'
}]
});
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
console.log('✅ Text attachment encoded with base64');
});
tap.test('CEP-03: Attachment Encoding - should encode binary data correctly', async () => {
// Create binary data with all possible 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 Test',
text: 'Email with binary attachment',
attachments: [{
filename: 'binary.dat',
content: binaryData,
contentType: 'application/octet-stream'
}]
});
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
console.log('✅ Binary data encoded correctly');
});
tap.test('CEP-03: Attachment Encoding - should handle various file types', async () => {
const attachments = [
{
filename: 'image.jpg',
content: Buffer.from('/9j/4AAQSkZJRgABAQEASABIAAD/2wBD', 'base64'), // Partial JPEG header
contentType: 'image/jpeg'
},
{
filename: 'document.pdf',
content: Buffer.from('%PDF-1.4\n%âÃÏÓ\n', 'utf8'),
contentType: 'application/pdf'
},
{
filename: 'archive.zip',
content: Buffer.from('PK\x03\x04'), // ZIP magic number
contentType: 'application/zip'
},
{
filename: 'audio.mp3',
content: Buffer.from('ID3'), // MP3 ID3 tag
contentType: 'audio/mpeg'
}
];
const email = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Multiple File Types Test',
text: 'Testing various attachment types',
attachments
});
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
console.log('✅ Various file types encoded correctly');
});
tap.test('CEP-03: Attachment Encoding - should handle quoted-printable encoding', async () => {
const textWithSpecialChars = 'This line has special chars: café, naïve, résumé\r\nThis line is very long and might need soft line breaks when encoded with quoted-printable encoding method\r\n=This line starts with equals sign';
const email = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Quoted-Printable Test',
text: 'Email with quoted-printable attachment',
attachments: [{
filename: 'special-chars.txt',
content: Buffer.from(textWithSpecialChars, 'utf8'),
contentType: 'text/plain; charset=utf-8',
encoding: 'quoted-printable'
}]
});
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
console.log('✅ Quoted-printable encoding handled correctly');
});
tap.test('CEP-03: Attachment Encoding - should handle content-disposition', async () => {
const email = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Content-Disposition Test',
text: 'Testing attachment vs inline disposition',
html: '<p>Image below: <img src="cid:inline-image"></p>',
attachments: [
{
filename: 'attachment.txt',
content: Buffer.from('This is an attachment'),
contentType: 'text/plain'
// Default disposition is 'attachment'
},
{
filename: 'inline-image.png',
content: Buffer.from('fake png data'),
contentType: 'image/png',
contentId: 'inline-image' // Makes it inline
}
]
});
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
console.log('✅ Content-disposition handled correctly');
});
tap.test('CEP-03: Attachment Encoding - should handle large attachments efficiently', async () => {
// Create a 10MB attachment
const largeSize = 10 * 1024 * 1024;
const largeData = crypto.randomBytes(largeSize);
const email = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Large Attachment Test',
text: 'Email with large attachment',
attachments: [{
filename: 'large-file.bin',
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 (${largeSize / 1024 / 1024}MB) sent in ${duration}ms`);
console.log(` Throughput: ${(largeSize / 1024 / 1024 / (duration / 1000)).toFixed(2)} MB/s`);
});
tap.test('CEP-03: Attachment Encoding - should handle Unicode filenames', async () => {
const unicodeAttachments = [
{
filename: '文档.txt', // Chinese
content: Buffer.from('Chinese filename test'),
contentType: 'text/plain'
},
{
filename: 'файл.txt', // Russian
content: Buffer.from('Russian filename test'),
contentType: 'text/plain'
},
{
filename: 'ファイル.txt', // Japanese
content: Buffer.from('Japanese filename test'),
contentType: 'text/plain'
},
{
filename: '🎉emoji🎊.txt', // Emoji
content: Buffer.from('Emoji filename test'),
contentType: 'text/plain'
}
];
const email = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Unicode Filenames Test',
text: 'Testing Unicode characters in filenames',
attachments: unicodeAttachments
});
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
console.log('✅ Unicode filenames encoded correctly');
});
tap.test('CEP-03: Attachment Encoding - should handle special MIME headers', async () => {
const email = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'MIME Headers Test',
text: 'Testing special MIME headers',
attachments: [{
filename: 'report.xml',
content: Buffer.from('<?xml version="1.0"?><root>test</root>'),
contentType: 'application/xml; charset=utf-8',
encoding: 'base64',
headers: {
'Content-Description': 'Monthly Report',
'Content-Transfer-Encoding': 'base64',
'Content-ID': '<report-2024-01@example.com>'
}
}]
});
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
console.log('✅ Special MIME headers handled correctly');
});
tap.test('CEP-03: Attachment Encoding - should handle attachment size limits', async () => {
// Test with attachment near server limit
const nearLimitSize = 45 * 1024 * 1024; // 45MB (near 50MB limit)
const nearLimitData = Buffer.alloc(nearLimitSize);
// Fill with some pattern to avoid compression benefits
for (let i = 0; i < nearLimitSize; i++) {
nearLimitData[i] = i % 256;
}
const email = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Near Size Limit Test',
text: 'Testing attachment near size limit',
attachments: [{
filename: 'near-limit.bin',
content: nearLimitData,
contentType: 'application/octet-stream'
}]
});
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
console.log(`✅ Attachment near size limit (${nearLimitSize / 1024 / 1024}MB) accepted`);
});
tap.test('CEP-03: Attachment Encoding - should handle mixed encoding types', async () => {
const email = new Email({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Mixed Encoding Test',
text: 'Plain text body',
html: '<p>HTML body with special chars: café</p>',
attachments: [
{
filename: 'base64.bin',
content: crypto.randomBytes(1024),
contentType: 'application/octet-stream',
encoding: 'base64'
},
{
filename: 'quoted.txt',
content: Buffer.from('Text with special chars: naïve café résumé'),
contentType: 'text/plain; charset=utf-8',
encoding: 'quoted-printable'
},
{
filename: '7bit.txt',
content: Buffer.from('Simple ASCII text only'),
contentType: 'text/plain',
encoding: '7bit'
}
]
});
const result = await smtpClient.sendMail(email);
expect(result.success).toBeTrue();
console.log('✅ Mixed encoding types handled 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();