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 Version

This is the HTML version of the email.

' }); 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: '

This email contains attachments.

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

Here is an inline image: Red Pixel

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

HTML version with Logo

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

HTML with specific charset

', attachments: [ { filename: 'utf8-text.txt', content: Buffer.from('UTF-8 text: 你好世界'), contentType: 'text/plain; charset=utf-8' }, { filename: 'data.xml', content: Buffer.from('Test'), 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); }); tap.start();