334 lines
10 KiB
TypeScript
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(); |