update
This commit is contained in:
@ -1,31 +1,32 @@
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
import { startTestSmtpServer } from '../../helpers/server.loader.js';
|
||||
import { createSmtpClient } from '../../helpers/smtp.client.js';
|
||||
import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.js';
|
||||
import { createSmtpClient } from '../../../ts/mail/delivery/smtpclient/index.js';
|
||||
import { Email } from '../../../ts/mail/core/classes.email.js';
|
||||
|
||||
let testServer: any;
|
||||
let testServer: ITestServer;
|
||||
|
||||
tap.test('setup test SMTP server', async () => {
|
||||
testServer = await startTestSmtpServer();
|
||||
testServer = await startTestServer({
|
||||
port: 2567,
|
||||
tlsEnabled: false,
|
||||
authRequired: false
|
||||
});
|
||||
expect(testServer).toBeTruthy();
|
||||
expect(testServer.port).toBeGreaterThan(0);
|
||||
expect(testServer.port).toEqual(2567);
|
||||
});
|
||||
|
||||
tap.test('CEP-07: Basic HTML email', async () => {
|
||||
const smtpClient = createSmtpClient({
|
||||
const smtpClient = await createSmtpClient({
|
||||
host: testServer.hostname,
|
||||
port: testServer.port,
|
||||
secure: false,
|
||||
connectionTimeout: 5000,
|
||||
debug: true
|
||||
connectionTimeout: 5000
|
||||
});
|
||||
|
||||
await smtpClient.connect();
|
||||
|
||||
// Create HTML email
|
||||
const email = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
to: 'recipient@example.com',
|
||||
subject: 'HTML Email Test',
|
||||
html: `
|
||||
<!DOCTYPE html>
|
||||
@ -59,52 +60,26 @@ tap.test('CEP-07: Basic HTML email', async () => {
|
||||
text: 'Welcome! This is an HTML email with formatting. Features: 1, 2, 3. © 2024 Example Corp'
|
||||
});
|
||||
|
||||
// Monitor content type
|
||||
let contentType = '';
|
||||
let boundary = '';
|
||||
const originalSendCommand = smtpClient.sendCommand.bind(smtpClient);
|
||||
|
||||
smtpClient.sendCommand = async (command: string) => {
|
||||
if (command.toLowerCase().includes('content-type:')) {
|
||||
contentType = command;
|
||||
const boundaryMatch = command.match(/boundary="?([^";\s]+)"?/i);
|
||||
if (boundaryMatch) {
|
||||
boundary = boundaryMatch[1];
|
||||
}
|
||||
}
|
||||
return originalSendCommand(command);
|
||||
};
|
||||
|
||||
const result = await smtpClient.sendMail(email);
|
||||
expect(result).toBeTruthy();
|
||||
|
||||
console.log('Content-Type:', contentType.trim());
|
||||
console.log('Multipart boundary:', boundary || 'not found');
|
||||
|
||||
// Should be multipart/alternative for HTML+text
|
||||
expect(contentType.toLowerCase()).toInclude('multipart');
|
||||
|
||||
await smtpClient.close();
|
||||
expect(result.success).toBeTruthy();
|
||||
console.log('Basic HTML email sent successfully');
|
||||
});
|
||||
|
||||
tap.test('CEP-07: HTML email with inline images', async () => {
|
||||
const smtpClient = createSmtpClient({
|
||||
const smtpClient = await createSmtpClient({
|
||||
host: testServer.hostname,
|
||||
port: testServer.port,
|
||||
secure: false,
|
||||
connectionTimeout: 10000,
|
||||
debug: true
|
||||
connectionTimeout: 10000
|
||||
});
|
||||
|
||||
await smtpClient.connect();
|
||||
|
||||
// Create a simple 1x1 red pixel PNG
|
||||
const redPixelBase64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==';
|
||||
|
||||
// Create HTML email with inline image
|
||||
const email = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
to: 'recipient@example.com',
|
||||
subject: 'Email with Inline Images',
|
||||
html: `
|
||||
<html>
|
||||
@ -133,57 +108,23 @@ tap.test('CEP-07: HTML email with inline images', async () => {
|
||||
]
|
||||
});
|
||||
|
||||
// Monitor multipart structure
|
||||
let multipartType = '';
|
||||
let partCount = 0;
|
||||
let hasContentId = false;
|
||||
const originalSendCommand = smtpClient.sendCommand.bind(smtpClient);
|
||||
|
||||
smtpClient.sendCommand = async (command: string) => {
|
||||
if (command.toLowerCase().includes('content-type:')) {
|
||||
if (command.toLowerCase().includes('multipart/related')) {
|
||||
multipartType = 'related';
|
||||
} else if (command.toLowerCase().includes('multipart/mixed')) {
|
||||
multipartType = 'mixed';
|
||||
}
|
||||
if (command.includes('--')) {
|
||||
partCount++;
|
||||
}
|
||||
}
|
||||
if (command.toLowerCase().includes('content-id:')) {
|
||||
hasContentId = true;
|
||||
}
|
||||
return originalSendCommand(command);
|
||||
};
|
||||
|
||||
const result = await smtpClient.sendMail(email);
|
||||
expect(result).toBeTruthy();
|
||||
|
||||
console.log('Multipart type:', multipartType);
|
||||
console.log('Has Content-ID headers:', hasContentId);
|
||||
|
||||
// Should use multipart/related for inline images
|
||||
expect(multipartType).toEqual('related');
|
||||
expect(hasContentId).toBeTruthy();
|
||||
|
||||
await smtpClient.close();
|
||||
expect(result.success).toBeTruthy();
|
||||
console.log('HTML email with inline images sent successfully');
|
||||
});
|
||||
|
||||
tap.test('CEP-07: Complex HTML with multiple inline resources', async () => {
|
||||
const smtpClient = createSmtpClient({
|
||||
const smtpClient = await createSmtpClient({
|
||||
host: testServer.hostname,
|
||||
port: testServer.port,
|
||||
secure: false,
|
||||
connectionTimeout: 10000,
|
||||
debug: true
|
||||
connectionTimeout: 10000
|
||||
});
|
||||
|
||||
await smtpClient.connect();
|
||||
|
||||
// Create email with multiple inline resources
|
||||
const email = new Email({
|
||||
from: 'newsletter@example.com',
|
||||
to: ['subscriber@example.com'],
|
||||
to: 'subscriber@example.com',
|
||||
subject: 'Newsletter with Rich Content',
|
||||
html: `
|
||||
<html>
|
||||
@ -261,52 +202,23 @@ tap.test('CEP-07: Complex HTML with multiple inline resources', async () => {
|
||||
]
|
||||
});
|
||||
|
||||
// Count inline attachments
|
||||
let inlineAttachments = 0;
|
||||
let contentIds: string[] = [];
|
||||
const originalSendCommand = smtpClient.sendCommand.bind(smtpClient);
|
||||
|
||||
smtpClient.sendCommand = async (command: string) => {
|
||||
if (command.toLowerCase().includes('content-disposition: inline')) {
|
||||
inlineAttachments++;
|
||||
}
|
||||
if (command.toLowerCase().includes('content-id:')) {
|
||||
const cidMatch = command.match(/content-id:\s*<([^>]+)>/i);
|
||||
if (cidMatch) {
|
||||
contentIds.push(cidMatch[1]);
|
||||
}
|
||||
}
|
||||
return originalSendCommand(command);
|
||||
};
|
||||
|
||||
const result = await smtpClient.sendMail(email);
|
||||
expect(result).toBeTruthy();
|
||||
|
||||
console.log(`Inline attachments: ${inlineAttachments}`);
|
||||
console.log(`Content-IDs found: ${contentIds.length}`);
|
||||
console.log('CIDs:', contentIds);
|
||||
|
||||
// Should have all inline attachments
|
||||
expect(contentIds.length).toEqual(6);
|
||||
|
||||
await smtpClient.close();
|
||||
expect(result.success).toBeTruthy();
|
||||
console.log('Complex HTML with multiple inline resources sent successfully');
|
||||
});
|
||||
|
||||
tap.test('CEP-07: HTML with external and inline images mixed', async () => {
|
||||
const smtpClient = createSmtpClient({
|
||||
const smtpClient = await createSmtpClient({
|
||||
host: testServer.hostname,
|
||||
port: testServer.port,
|
||||
secure: false,
|
||||
connectionTimeout: 5000,
|
||||
debug: true
|
||||
connectionTimeout: 5000
|
||||
});
|
||||
|
||||
await smtpClient.connect();
|
||||
|
||||
// Mix of inline and external images
|
||||
const email = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
to: 'recipient@example.com',
|
||||
subject: 'Mixed Image Sources',
|
||||
html: `
|
||||
<html>
|
||||
@ -333,28 +245,22 @@ tap.test('CEP-07: HTML with external and inline images mixed', async () => {
|
||||
});
|
||||
|
||||
const result = await smtpClient.sendMail(email);
|
||||
expect(result).toBeTruthy();
|
||||
|
||||
expect(result.success).toBeTruthy();
|
||||
console.log('Successfully sent email with mixed image sources');
|
||||
|
||||
await smtpClient.close();
|
||||
});
|
||||
|
||||
tap.test('CEP-07: HTML email responsive design', async () => {
|
||||
const smtpClient = createSmtpClient({
|
||||
const smtpClient = await createSmtpClient({
|
||||
host: testServer.hostname,
|
||||
port: testServer.port,
|
||||
secure: false,
|
||||
connectionTimeout: 5000,
|
||||
debug: true
|
||||
connectionTimeout: 5000
|
||||
});
|
||||
|
||||
await smtpClient.connect();
|
||||
|
||||
// Responsive HTML email
|
||||
const email = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
to: 'recipient@example.com',
|
||||
subject: 'Responsive HTML Email',
|
||||
html: `
|
||||
<!DOCTYPE html>
|
||||
@ -406,28 +312,22 @@ tap.test('CEP-07: HTML email responsive design', async () => {
|
||||
});
|
||||
|
||||
const result = await smtpClient.sendMail(email);
|
||||
expect(result).toBeTruthy();
|
||||
|
||||
expect(result.success).toBeTruthy();
|
||||
console.log('Successfully sent responsive HTML email');
|
||||
|
||||
await smtpClient.close();
|
||||
});
|
||||
|
||||
tap.test('CEP-07: HTML sanitization and security', async () => {
|
||||
const smtpClient = createSmtpClient({
|
||||
const smtpClient = await createSmtpClient({
|
||||
host: testServer.hostname,
|
||||
port: testServer.port,
|
||||
secure: false,
|
||||
connectionTimeout: 5000,
|
||||
debug: true
|
||||
connectionTimeout: 5000
|
||||
});
|
||||
|
||||
await smtpClient.connect();
|
||||
|
||||
// Email with potentially dangerous HTML
|
||||
const email = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
to: 'recipient@example.com',
|
||||
subject: 'HTML Security Test',
|
||||
html: `
|
||||
<html>
|
||||
@ -458,28 +358,21 @@ tap.test('CEP-07: HTML sanitization and security', async () => {
|
||||
]
|
||||
});
|
||||
|
||||
// Note: The Email class should handle dangerous content appropriately
|
||||
const result = await smtpClient.sendMail(email);
|
||||
expect(result).toBeTruthy();
|
||||
|
||||
console.log('Sent email with potentially dangerous HTML (should be handled by Email class)');
|
||||
|
||||
await smtpClient.close();
|
||||
expect(result.success).toBeTruthy();
|
||||
console.log('HTML security test sent successfully');
|
||||
});
|
||||
|
||||
tap.test('CEP-07: Large HTML email with many inline images', async () => {
|
||||
const smtpClient = createSmtpClient({
|
||||
const smtpClient = await createSmtpClient({
|
||||
host: testServer.hostname,
|
||||
port: testServer.port,
|
||||
secure: false,
|
||||
connectionTimeout: 30000,
|
||||
debug: false // Quiet for performance
|
||||
connectionTimeout: 30000
|
||||
});
|
||||
|
||||
await smtpClient.connect();
|
||||
|
||||
// Create email with many inline images
|
||||
const imageCount = 20;
|
||||
const imageCount = 10; // Reduced for testing
|
||||
const attachments: any[] = [];
|
||||
let htmlContent = '<html><body><h1>Performance Test</h1>';
|
||||
|
||||
@ -499,40 +392,29 @@ tap.test('CEP-07: Large HTML email with many inline images', async () => {
|
||||
|
||||
const email = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
to: 'recipient@example.com',
|
||||
subject: `Email with ${imageCount} inline images`,
|
||||
html: htmlContent,
|
||||
attachments: attachments
|
||||
});
|
||||
|
||||
console.log(`Sending email with ${imageCount} inline images...`);
|
||||
const startTime = Date.now();
|
||||
|
||||
const result = await smtpClient.sendMail(email);
|
||||
const elapsed = Date.now() - startTime;
|
||||
|
||||
expect(result).toBeTruthy();
|
||||
|
||||
console.log(`Sent in ${elapsed}ms (${(elapsed/imageCount).toFixed(2)}ms per image)`);
|
||||
|
||||
await smtpClient.close();
|
||||
expect(result.success).toBeTruthy();
|
||||
console.log(`Performance test with ${imageCount} inline images sent successfully`);
|
||||
});
|
||||
|
||||
tap.test('CEP-07: Alternative content for non-HTML clients', async () => {
|
||||
const smtpClient = createSmtpClient({
|
||||
const smtpClient = await createSmtpClient({
|
||||
host: testServer.hostname,
|
||||
port: testServer.port,
|
||||
secure: false,
|
||||
connectionTimeout: 5000,
|
||||
debug: true
|
||||
connectionTimeout: 5000
|
||||
});
|
||||
|
||||
await smtpClient.connect();
|
||||
|
||||
// Email with rich HTML and good plain text alternative
|
||||
const email = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
to: 'recipient@example.com',
|
||||
subject: 'Newsletter - March 2024',
|
||||
html: `
|
||||
<html>
|
||||
@ -593,42 +475,14 @@ Unsubscribe: https://example.com/unsubscribe`,
|
||||
]
|
||||
});
|
||||
|
||||
// Check multipart/alternative structure
|
||||
let hasAlternative = false;
|
||||
let hasTextPart = false;
|
||||
let hasHtmlPart = false;
|
||||
const originalSendCommand = smtpClient.sendCommand.bind(smtpClient);
|
||||
|
||||
smtpClient.sendCommand = async (command: string) => {
|
||||
if (command.toLowerCase().includes('content-type: multipart/alternative')) {
|
||||
hasAlternative = true;
|
||||
}
|
||||
if (command.toLowerCase().includes('content-type: text/plain')) {
|
||||
hasTextPart = true;
|
||||
}
|
||||
if (command.toLowerCase().includes('content-type: text/html')) {
|
||||
hasHtmlPart = true;
|
||||
}
|
||||
return originalSendCommand(command);
|
||||
};
|
||||
|
||||
const result = await smtpClient.sendMail(email);
|
||||
expect(result).toBeTruthy();
|
||||
|
||||
console.log('Multipart/alternative:', hasAlternative);
|
||||
console.log('Has text part:', hasTextPart);
|
||||
console.log('Has HTML part:', hasHtmlPart);
|
||||
|
||||
// Should have both versions
|
||||
expect(hasTextPart).toBeTruthy();
|
||||
expect(hasHtmlPart).toBeTruthy();
|
||||
|
||||
await smtpClient.close();
|
||||
expect(result.success).toBeTruthy();
|
||||
console.log('Newsletter with alternative content sent successfully');
|
||||
});
|
||||
|
||||
tap.test('cleanup test SMTP server', async () => {
|
||||
if (testServer) {
|
||||
await testServer.stop();
|
||||
await stopTestServer(testServer);
|
||||
}
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user