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: '

Image below:

', 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('test'), contentType: 'application/xml; charset=utf-8', encoding: 'base64', headers: { 'Content-Description': 'Monthly Report', 'Content-Transfer-Encoding': 'base64', 'Content-ID': '' } }] }); 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: '

HTML body with special chars: café

', 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); }); tap.start();