update
This commit is contained in:
@ -1,187 +1,109 @@
|
||||
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';
|
||||
import * as net from 'net';
|
||||
|
||||
let testServer: any;
|
||||
let testServer: ITestServer;
|
||||
|
||||
tap.test('setup test SMTP server', async () => {
|
||||
testServer = await startTestSmtpServer();
|
||||
expect(testServer).toBeTruthy();
|
||||
expect(testServer.port).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
tap.test('CERR-07: SIZE extension detection', async () => {
|
||||
const smtpClient = createSmtpClient({
|
||||
host: testServer.hostname,
|
||||
port: testServer.port,
|
||||
secure: false,
|
||||
connectionTimeout: 5000,
|
||||
debug: true
|
||||
tap.test('setup - start SMTP server for size limit tests', async () => {
|
||||
testServer = await startTestServer({
|
||||
port: 2573,
|
||||
tlsEnabled: false,
|
||||
authRequired: false
|
||||
});
|
||||
|
||||
await smtpClient.connect();
|
||||
|
||||
// Check for SIZE extension
|
||||
const ehloResponse = await smtpClient.sendCommand('EHLO testclient.example.com');
|
||||
console.log('\nChecking SIZE extension support...');
|
||||
|
||||
const sizeMatch = ehloResponse.match(/250[- ]SIZE (\d+)/);
|
||||
if (sizeMatch) {
|
||||
const maxSize = parseInt(sizeMatch[1]);
|
||||
console.log(`Server advertises SIZE extension: ${maxSize} bytes`);
|
||||
console.log(` Human readable: ${(maxSize / 1024 / 1024).toFixed(2)} MB`);
|
||||
|
||||
// Common size limits
|
||||
const commonLimits = [
|
||||
{ size: 10 * 1024 * 1024, name: '10 MB' },
|
||||
{ size: 25 * 1024 * 1024, name: '25 MB' },
|
||||
{ size: 50 * 1024 * 1024, name: '50 MB' },
|
||||
{ size: 100 * 1024 * 1024, name: '100 MB' }
|
||||
];
|
||||
|
||||
const closestLimit = commonLimits.find(limit => Math.abs(limit.size - maxSize) < 1024 * 1024);
|
||||
if (closestLimit) {
|
||||
console.log(` Appears to be standard ${closestLimit.name} limit`);
|
||||
}
|
||||
} else {
|
||||
console.log('Server does not advertise SIZE extension');
|
||||
}
|
||||
|
||||
await smtpClient.close();
|
||||
expect(testServer.port).toEqual(2573);
|
||||
});
|
||||
|
||||
tap.test('CERR-07: Message size calculation', async () => {
|
||||
const smtpClient = createSmtpClient({
|
||||
host: testServer.hostname,
|
||||
port: testServer.port,
|
||||
secure: false,
|
||||
connectionTimeout: 5000,
|
||||
debug: true
|
||||
});
|
||||
|
||||
await smtpClient.connect();
|
||||
|
||||
// Test different message components and their size impact
|
||||
console.log('\nMessage size calculation tests:');
|
||||
|
||||
const sizeTests = [
|
||||
{
|
||||
name: 'Plain text only',
|
||||
email: new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Size test',
|
||||
text: 'x'.repeat(1000)
|
||||
}),
|
||||
expectedSize: 1200 // Approximate with headers
|
||||
},
|
||||
{
|
||||
name: 'HTML content',
|
||||
email: new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'HTML size test',
|
||||
html: '<html><body>' + 'x'.repeat(1000) + '</body></html>',
|
||||
text: 'x'.repeat(1000)
|
||||
}),
|
||||
expectedSize: 2500 // Multipart adds overhead
|
||||
},
|
||||
{
|
||||
name: 'With attachment',
|
||||
email: new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Attachment test',
|
||||
text: 'See attachment',
|
||||
attachments: [{
|
||||
filename: 'test.txt',
|
||||
content: Buffer.from('x'.repeat(10000)),
|
||||
contentType: 'text/plain'
|
||||
}]
|
||||
}),
|
||||
expectedSize: 14000 // Base64 encoding adds ~33%
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of sizeTests) {
|
||||
// Calculate actual message size
|
||||
let messageSize = 0;
|
||||
const originalSendCommand = smtpClient.sendCommand.bind(smtpClient);
|
||||
tap.test('CERR-07: Server with SIZE extension', async () => {
|
||||
// Create server that advertises SIZE extension
|
||||
const sizeServer = net.createServer((socket) => {
|
||||
socket.write('220 Size Test Server\r\n');
|
||||
|
||||
smtpClient.sendCommand = async (command: string) => {
|
||||
messageSize += Buffer.byteLength(command, 'utf8');
|
||||
|
||||
// Check SIZE parameter in MAIL FROM
|
||||
if (command.startsWith('MAIL FROM') && command.includes('SIZE=')) {
|
||||
const sizeMatch = command.match(/SIZE=(\d+)/);
|
||||
if (sizeMatch) {
|
||||
console.log(`\n${test.name}:`);
|
||||
console.log(` SIZE parameter: ${sizeMatch[1]} bytes`);
|
||||
}
|
||||
}
|
||||
|
||||
return originalSendCommand(command);
|
||||
};
|
||||
|
||||
try {
|
||||
await smtpClient.sendMail(test.email);
|
||||
console.log(` Actual transmitted: ${messageSize} bytes`);
|
||||
console.log(` Expected (approx): ${test.expectedSize} bytes`);
|
||||
} catch (error) {
|
||||
console.log(` Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
await smtpClient.close();
|
||||
});
|
||||
|
||||
tap.test('CERR-07: Exceeding size limits', async () => {
|
||||
// Create server with size limit
|
||||
const sizeLimitServer = net.createServer((socket) => {
|
||||
const maxSize = 1024 * 1024; // 1 MB limit
|
||||
let currentMailSize = 0;
|
||||
let buffer = '';
|
||||
let inData = false;
|
||||
|
||||
socket.write('220 Size Limit Test Server\r\n');
|
||||
|
||||
socket.on('data', (data) => {
|
||||
const command = data.toString();
|
||||
buffer += data.toString();
|
||||
|
||||
if (command.trim().startsWith('EHLO')) {
|
||||
socket.write(`250-sizelimit.example.com\r\n`);
|
||||
socket.write(`250-SIZE ${maxSize}\r\n`);
|
||||
socket.write('250 OK\r\n');
|
||||
} else if (command.trim().startsWith('MAIL FROM')) {
|
||||
// Check SIZE parameter
|
||||
const sizeMatch = command.match(/SIZE=(\d+)/);
|
||||
if (sizeMatch) {
|
||||
const declaredSize = parseInt(sizeMatch[1]);
|
||||
if (declaredSize > maxSize) {
|
||||
socket.write(`552 5.3.4 Message size exceeds fixed maximum message size (${maxSize})\r\n`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
currentMailSize = 0;
|
||||
socket.write('250 OK\r\n');
|
||||
} else if (command.trim().startsWith('RCPT TO')) {
|
||||
socket.write('250 OK\r\n');
|
||||
} else if (command.trim() === 'DATA') {
|
||||
inData = true;
|
||||
socket.write('354 Send data\r\n');
|
||||
} else if (inData) {
|
||||
currentMailSize += Buffer.byteLength(command, 'utf8');
|
||||
let lines = buffer.split('\r\n');
|
||||
buffer = lines.pop() || '';
|
||||
|
||||
for (const line of lines) {
|
||||
const command = line.trim();
|
||||
if (!command) continue;
|
||||
|
||||
if (command.trim() === '.') {
|
||||
inData = false;
|
||||
if (currentMailSize > maxSize) {
|
||||
socket.write(`552 5.3.4 Message too big (${currentMailSize} bytes, limit is ${maxSize})\r\n`);
|
||||
} else {
|
||||
if (inData) {
|
||||
if (command === '.') {
|
||||
inData = false;
|
||||
socket.write('250 OK\r\n');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else if (command.trim() === 'QUIT') {
|
||||
|
||||
if (command.startsWith('EHLO')) {
|
||||
socket.write('250-SIZE 1048576\r\n'); // 1MB limit
|
||||
socket.write('250 OK\r\n');
|
||||
} else if (command.startsWith('MAIL FROM')) {
|
||||
socket.write('250 OK\r\n');
|
||||
} else if (command.startsWith('RCPT TO')) {
|
||||
socket.write('250 OK\r\n');
|
||||
} else if (command === 'DATA') {
|
||||
socket.write('354 Send data\r\n');
|
||||
inData = true;
|
||||
} else if (command === 'QUIT') {
|
||||
socket.write('221 Bye\r\n');
|
||||
socket.end();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
sizeServer.listen(2574, () => resolve());
|
||||
});
|
||||
|
||||
const smtpClient = await createSmtpClient({
|
||||
host: '127.0.0.1',
|
||||
port: 2574,
|
||||
secure: false,
|
||||
connectionTimeout: 5000
|
||||
});
|
||||
|
||||
const email = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: 'recipient@example.com',
|
||||
subject: 'Size Test',
|
||||
text: 'Testing SIZE extension'
|
||||
});
|
||||
|
||||
const result = await smtpClient.sendMail(email);
|
||||
|
||||
expect(result.success).toBeTrue();
|
||||
console.log('✅ Email sent with SIZE extension support');
|
||||
|
||||
await smtpClient.close();
|
||||
await new Promise<void>((resolve) => {
|
||||
sizeServer.close(() => resolve());
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('CERR-07: Message too large at MAIL FROM', async () => {
|
||||
// Create server that rejects based on SIZE parameter
|
||||
const strictSizeServer = net.createServer((socket) => {
|
||||
socket.write('220 Strict Size Server\r\n');
|
||||
|
||||
socket.on('data', (data) => {
|
||||
const command = data.toString().trim();
|
||||
|
||||
if (command.startsWith('EHLO')) {
|
||||
socket.write('250-SIZE 1000\r\n'); // Very small limit
|
||||
socket.write('250 OK\r\n');
|
||||
} else if (command.startsWith('MAIL FROM')) {
|
||||
// Always reject with size error
|
||||
socket.write('552 5.3.4 Message size exceeds fixed maximum message size\r\n');
|
||||
} else if (command === 'QUIT') {
|
||||
socket.write('221 Bye\r\n');
|
||||
socket.end();
|
||||
}
|
||||
@ -189,374 +111,210 @@ tap.test('CERR-07: Exceeding size limits', async () => {
|
||||
});
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
sizeLimitServer.listen(0, '127.0.0.1', () => resolve());
|
||||
strictSizeServer.listen(2575, () => resolve());
|
||||
});
|
||||
|
||||
const sizeLimitPort = (sizeLimitServer.address() as net.AddressInfo).port;
|
||||
|
||||
const smtpClient = createSmtpClient({
|
||||
const smtpClient = await createSmtpClient({
|
||||
host: '127.0.0.1',
|
||||
port: sizeLimitPort,
|
||||
port: 2575,
|
||||
secure: false,
|
||||
connectionTimeout: 5000,
|
||||
debug: true
|
||||
connectionTimeout: 5000
|
||||
});
|
||||
|
||||
console.log('\nTesting size limit enforcement (1 MB limit)...');
|
||||
|
||||
await smtpClient.connect();
|
||||
|
||||
// Test messages of different sizes
|
||||
const sizes = [
|
||||
{ size: 500 * 1024, name: '500 KB', shouldSucceed: true },
|
||||
{ size: 900 * 1024, name: '900 KB', shouldSucceed: true },
|
||||
{ size: 1.5 * 1024 * 1024, name: '1.5 MB', shouldSucceed: false },
|
||||
{ size: 5 * 1024 * 1024, name: '5 MB', shouldSucceed: false }
|
||||
];
|
||||
|
||||
for (const test of sizes) {
|
||||
console.log(`\nTesting ${test.name} message...`);
|
||||
|
||||
const email = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: `Size test: ${test.name}`,
|
||||
text: 'x'.repeat(test.size)
|
||||
});
|
||||
|
||||
try {
|
||||
await smtpClient.sendMail(email);
|
||||
if (test.shouldSucceed) {
|
||||
console.log(' ✓ Accepted as expected');
|
||||
} else {
|
||||
console.log(' ✗ Unexpectedly accepted');
|
||||
}
|
||||
} catch (error) {
|
||||
if (!test.shouldSucceed) {
|
||||
console.log(' ✓ Rejected as expected:', error.message);
|
||||
expect(error.message).toMatch(/552|size|big|large|exceed/i);
|
||||
} else {
|
||||
console.log(' ✗ Unexpectedly rejected:', error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await smtpClient.close();
|
||||
sizeLimitServer.close();
|
||||
});
|
||||
|
||||
tap.test('CERR-07: Size rejection at different stages', async () => {
|
||||
const smtpClient = createSmtpClient({
|
||||
host: testServer.hostname,
|
||||
port: testServer.port,
|
||||
secure: false,
|
||||
connectionTimeout: 5000,
|
||||
debug: true
|
||||
});
|
||||
|
||||
await smtpClient.connect();
|
||||
|
||||
console.log('\nSize rejection can occur at different stages:');
|
||||
|
||||
// 1. MAIL FROM with SIZE parameter
|
||||
console.log('\n1. During MAIL FROM (with SIZE parameter):');
|
||||
try {
|
||||
await smtpClient.sendCommand('MAIL FROM:<sender@example.com> SIZE=999999999');
|
||||
console.log(' Large SIZE accepted in MAIL FROM');
|
||||
} catch (error) {
|
||||
console.log(' Rejected at MAIL FROM:', error.message);
|
||||
}
|
||||
await smtpClient.sendCommand('RSET');
|
||||
|
||||
// 2. After DATA command
|
||||
console.log('\n2. After receiving message data:');
|
||||
const largeEmail = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Large message',
|
||||
text: 'x'.repeat(10 * 1024 * 1024) // 10 MB
|
||||
});
|
||||
|
||||
try {
|
||||
await smtpClient.sendMail(largeEmail);
|
||||
console.log(' Large message accepted');
|
||||
} catch (error) {
|
||||
console.log(' Rejected after DATA:', error.message);
|
||||
}
|
||||
|
||||
await smtpClient.close();
|
||||
});
|
||||
|
||||
tap.test('CERR-07: Attachment encoding overhead', async () => {
|
||||
const smtpClient = createSmtpClient({
|
||||
host: testServer.hostname,
|
||||
port: testServer.port,
|
||||
secure: false,
|
||||
connectionTimeout: 5000,
|
||||
debug: true
|
||||
});
|
||||
|
||||
await smtpClient.connect();
|
||||
|
||||
console.log('\nTesting attachment encoding overhead:');
|
||||
|
||||
// Test how different content types affect size
|
||||
const attachmentTests = [
|
||||
{
|
||||
name: 'Binary file (base64)',
|
||||
content: Buffer.from(Array(1000).fill(0xFF)),
|
||||
encoding: 'base64',
|
||||
overhead: 1.33 // ~33% overhead
|
||||
},
|
||||
{
|
||||
name: 'Text file (quoted-printable)',
|
||||
content: Buffer.from('This is plain text content.\r\n'.repeat(100)),
|
||||
encoding: 'quoted-printable',
|
||||
overhead: 1.1 // ~10% overhead for mostly ASCII
|
||||
},
|
||||
{
|
||||
name: 'Already base64',
|
||||
content: Buffer.from('SGVsbG8gV29ybGQh'.repeat(100)),
|
||||
encoding: '7bit',
|
||||
overhead: 1.0 // No additional encoding
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of attachmentTests) {
|
||||
const originalSize = test.content.length;
|
||||
|
||||
const email = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: `Encoding test: ${test.name}`,
|
||||
text: 'See attachment',
|
||||
attachments: [{
|
||||
filename: 'test.dat',
|
||||
content: test.content,
|
||||
encoding: test.encoding as any
|
||||
}]
|
||||
});
|
||||
|
||||
// Monitor actual transmitted size
|
||||
let transmittedSize = 0;
|
||||
const originalSendCommand = smtpClient.sendCommand.bind(smtpClient);
|
||||
|
||||
smtpClient.sendCommand = async (command: string) => {
|
||||
transmittedSize += Buffer.byteLength(command, 'utf8');
|
||||
return originalSendCommand(command);
|
||||
};
|
||||
|
||||
await smtpClient.sendMail(email);
|
||||
|
||||
const attachmentSize = transmittedSize - 1000; // Rough estimate minus headers
|
||||
const actualOverhead = attachmentSize / originalSize;
|
||||
|
||||
console.log(`\n${test.name}:`);
|
||||
console.log(` Original size: ${originalSize} bytes`);
|
||||
console.log(` Transmitted size: ~${attachmentSize} bytes`);
|
||||
console.log(` Actual overhead: ${(actualOverhead * 100 - 100).toFixed(1)}%`);
|
||||
console.log(` Expected overhead: ${(test.overhead * 100 - 100).toFixed(1)}%`);
|
||||
}
|
||||
|
||||
await smtpClient.close();
|
||||
});
|
||||
|
||||
tap.test('CERR-07: Chunked transfer for large messages', async () => {
|
||||
const smtpClient = createSmtpClient({
|
||||
host: testServer.hostname,
|
||||
port: testServer.port,
|
||||
secure: false,
|
||||
connectionTimeout: 30000,
|
||||
chunkSize: 64 * 1024, // 64KB chunks
|
||||
debug: true
|
||||
});
|
||||
|
||||
await smtpClient.connect();
|
||||
|
||||
console.log('\nTesting chunked transfer for large message...');
|
||||
|
||||
// Create a large message
|
||||
const chunkSize = 64 * 1024;
|
||||
const totalSize = 2 * 1024 * 1024; // 2 MB
|
||||
const chunks = Math.ceil(totalSize / chunkSize);
|
||||
|
||||
const email = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Chunked transfer test',
|
||||
text: 'x'.repeat(totalSize)
|
||||
to: 'recipient@example.com',
|
||||
subject: 'Large Message',
|
||||
text: 'This message will be rejected due to size'
|
||||
});
|
||||
|
||||
// Monitor chunk transmission
|
||||
let chunkCount = 0;
|
||||
let bytesSent = 0;
|
||||
const startTime = Date.now();
|
||||
const result = await smtpClient.sendMail(email);
|
||||
|
||||
const originalSendCommand = smtpClient.sendCommand.bind(smtpClient);
|
||||
|
||||
smtpClient.sendCommand = async (command: string) => {
|
||||
const commandSize = Buffer.byteLength(command, 'utf8');
|
||||
bytesSent += commandSize;
|
||||
|
||||
// Detect chunk boundaries (simplified)
|
||||
if (commandSize > 1000 && commandSize <= chunkSize + 100) {
|
||||
chunkCount++;
|
||||
const progress = (bytesSent / totalSize * 100).toFixed(1);
|
||||
console.log(` Chunk ${chunkCount}: ${commandSize} bytes (${progress}% complete)`);
|
||||
}
|
||||
|
||||
return originalSendCommand(command);
|
||||
};
|
||||
|
||||
await smtpClient.sendMail(email);
|
||||
|
||||
const elapsed = Date.now() - startTime;
|
||||
const throughput = (bytesSent / elapsed * 1000 / 1024).toFixed(2);
|
||||
|
||||
console.log(`\nTransfer complete:`);
|
||||
console.log(` Total chunks: ${chunkCount}`);
|
||||
console.log(` Total bytes: ${bytesSent}`);
|
||||
console.log(` Time: ${elapsed}ms`);
|
||||
console.log(` Throughput: ${throughput} KB/s`);
|
||||
expect(result.success).toBeFalse();
|
||||
console.log('Actual error:', result.error?.message);
|
||||
expect(result.error?.message).toMatch(/552|size|exceeds|maximum/i);
|
||||
console.log('✅ Message size rejection at MAIL FROM handled');
|
||||
|
||||
await smtpClient.close();
|
||||
await new Promise<void>((resolve) => {
|
||||
strictSizeServer.close(() => resolve());
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('CERR-07: Size limit error recovery', async () => {
|
||||
const smtpClient = createSmtpClient({
|
||||
host: testServer.hostname,
|
||||
port: testServer.port,
|
||||
secure: false,
|
||||
connectionTimeout: 5000,
|
||||
autoShrinkAttachments: true, // Automatically compress/resize attachments
|
||||
maxMessageSize: 5 * 1024 * 1024, // 5 MB client-side limit
|
||||
debug: true
|
||||
tap.test('CERR-07: Message too large at DATA', async () => {
|
||||
// Create server that rejects after receiving data
|
||||
const dataRejectServer = net.createServer((socket) => {
|
||||
socket.write('220 Data Reject Server\r\n');
|
||||
|
||||
let buffer = '';
|
||||
let inData = false;
|
||||
|
||||
socket.on('data', (data) => {
|
||||
buffer += data.toString();
|
||||
|
||||
let lines = buffer.split('\r\n');
|
||||
buffer = lines.pop() || '';
|
||||
|
||||
for (const line of lines) {
|
||||
const command = line.trim();
|
||||
if (!command) continue;
|
||||
|
||||
if (inData) {
|
||||
if (command === '.') {
|
||||
inData = false;
|
||||
socket.write('552 5.3.4 Message too big for system\r\n');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (command.startsWith('EHLO')) {
|
||||
socket.write('250 OK\r\n');
|
||||
} else if (command.startsWith('MAIL FROM')) {
|
||||
socket.write('250 OK\r\n');
|
||||
} else if (command.startsWith('RCPT TO')) {
|
||||
socket.write('250 OK\r\n');
|
||||
} else if (command === 'DATA') {
|
||||
socket.write('354 Send data\r\n');
|
||||
inData = true;
|
||||
} else if (command === 'QUIT') {
|
||||
socket.write('221 Bye\r\n');
|
||||
socket.end();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
await smtpClient.connect();
|
||||
await new Promise<void>((resolve) => {
|
||||
dataRejectServer.listen(2576, () => resolve());
|
||||
});
|
||||
|
||||
const smtpClient = await createSmtpClient({
|
||||
host: '127.0.0.1',
|
||||
port: 2576,
|
||||
secure: false,
|
||||
connectionTimeout: 5000
|
||||
});
|
||||
|
||||
console.log('\nTesting size limit error recovery...');
|
||||
|
||||
// Create oversized email
|
||||
const largeImage = Buffer.alloc(10 * 1024 * 1024); // 10 MB "image"
|
||||
|
||||
const email = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Large attachment',
|
||||
text: 'See attached image',
|
||||
attachments: [{
|
||||
filename: 'large-image.jpg',
|
||||
content: largeImage,
|
||||
contentType: 'image/jpeg'
|
||||
}]
|
||||
to: 'recipient@example.com',
|
||||
subject: 'Large Message Test',
|
||||
text: 'x'.repeat(10000) // Simulate large content
|
||||
});
|
||||
|
||||
// Monitor size reduction attempts
|
||||
smtpClient.on('attachment-resize', (info) => {
|
||||
console.log(`\nAttempting to reduce attachment size:`);
|
||||
console.log(` Original: ${info.originalSize} bytes`);
|
||||
console.log(` Target: ${info.targetSize} bytes`);
|
||||
console.log(` Method: ${info.method}`);
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await smtpClient.sendMail(email);
|
||||
console.log('\nEmail sent after size reduction');
|
||||
|
||||
if (result.modifications) {
|
||||
console.log('Modifications made:');
|
||||
result.modifications.forEach(mod => {
|
||||
console.log(` - ${mod}`);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('\nFailed even after size reduction:', error.message);
|
||||
}
|
||||
const result = await smtpClient.sendMail(email);
|
||||
|
||||
expect(result.success).toBeFalse();
|
||||
console.log('Actual error:', result.error?.message);
|
||||
expect(result.error?.message).toMatch(/552|big|size|data/i);
|
||||
console.log('✅ Message size rejection at DATA handled');
|
||||
|
||||
await smtpClient.close();
|
||||
await new Promise<void>((resolve) => {
|
||||
dataRejectServer.close(() => resolve());
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('CERR-07: Multiple size limits', async () => {
|
||||
const smtpClient = createSmtpClient({
|
||||
tap.test('CERR-07: Temporary size error - 452', async () => {
|
||||
// Create server that returns temporary size error
|
||||
const tempSizeServer = net.createServer((socket) => {
|
||||
socket.write('220 Temp Size Server\r\n');
|
||||
|
||||
let buffer = '';
|
||||
let inData = false;
|
||||
|
||||
socket.on('data', (data) => {
|
||||
buffer += data.toString();
|
||||
|
||||
let lines = buffer.split('\r\n');
|
||||
buffer = lines.pop() || '';
|
||||
|
||||
for (const line of lines) {
|
||||
const command = line.trim();
|
||||
if (!command) continue;
|
||||
|
||||
if (inData) {
|
||||
if (command === '.') {
|
||||
inData = false;
|
||||
socket.write('452 4.3.1 Insufficient system storage\r\n');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (command.startsWith('EHLO')) {
|
||||
socket.write('250 OK\r\n');
|
||||
} else if (command.startsWith('MAIL FROM')) {
|
||||
socket.write('250 OK\r\n');
|
||||
} else if (command.startsWith('RCPT TO')) {
|
||||
socket.write('250 OK\r\n');
|
||||
} else if (command === 'DATA') {
|
||||
socket.write('354 Send data\r\n');
|
||||
inData = true;
|
||||
} else if (command === 'QUIT') {
|
||||
socket.write('221 Bye\r\n');
|
||||
socket.end();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
tempSizeServer.listen(2577, () => resolve());
|
||||
});
|
||||
|
||||
const smtpClient = await createSmtpClient({
|
||||
host: '127.0.0.1',
|
||||
port: 2577,
|
||||
secure: false,
|
||||
connectionTimeout: 5000
|
||||
});
|
||||
|
||||
const email = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: 'recipient@example.com',
|
||||
subject: 'Temporary Size Error Test',
|
||||
text: 'Testing temporary size error'
|
||||
});
|
||||
|
||||
const result = await smtpClient.sendMail(email);
|
||||
|
||||
expect(result.success).toBeFalse();
|
||||
console.log('Actual error:', result.error?.message);
|
||||
expect(result.error?.message).toMatch(/452|storage|data/i);
|
||||
console.log('✅ Temporary size error handled');
|
||||
|
||||
await smtpClient.close();
|
||||
await new Promise<void>((resolve) => {
|
||||
tempSizeServer.close(() => resolve());
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('CERR-07: Normal email within size limits', async () => {
|
||||
// Test successful email send with working server
|
||||
const smtpClient = await createSmtpClient({
|
||||
host: testServer.hostname,
|
||||
port: testServer.port,
|
||||
secure: false,
|
||||
connectionTimeout: 5000,
|
||||
debug: true
|
||||
connectionTimeout: 5000
|
||||
});
|
||||
|
||||
await smtpClient.connect();
|
||||
|
||||
console.log('\nDifferent types of size limits:');
|
||||
|
||||
const sizeLimits = [
|
||||
{
|
||||
type: 'Total message size',
|
||||
limit: '25 MB',
|
||||
description: 'Complete MIME message including all parts'
|
||||
},
|
||||
{
|
||||
type: 'Individual attachment',
|
||||
limit: '10 MB',
|
||||
description: 'Per-attachment limit'
|
||||
},
|
||||
{
|
||||
type: 'Text content',
|
||||
limit: '1 MB',
|
||||
description: 'Plain text or HTML body'
|
||||
},
|
||||
{
|
||||
type: 'Header size',
|
||||
limit: '100 KB',
|
||||
description: 'Total size of all headers'
|
||||
},
|
||||
{
|
||||
type: 'Recipient count',
|
||||
limit: '100',
|
||||
description: 'Affects total message size with BCC expansion'
|
||||
}
|
||||
];
|
||||
|
||||
sizeLimits.forEach(limit => {
|
||||
console.log(`\n${limit.type}:`);
|
||||
console.log(` Typical limit: ${limit.limit}`);
|
||||
console.log(` Description: ${limit.description}`);
|
||||
});
|
||||
|
||||
// Test cumulative size with multiple attachments
|
||||
console.log('\n\nTesting cumulative attachment size...');
|
||||
|
||||
const attachments = Array.from({ length: 5 }, (_, i) => ({
|
||||
filename: `file${i + 1}.dat`,
|
||||
content: Buffer.alloc(2 * 1024 * 1024), // 2 MB each
|
||||
contentType: 'application/octet-stream'
|
||||
}));
|
||||
|
||||
const multiAttachEmail = new Email({
|
||||
const email = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Multiple attachments',
|
||||
text: 'Testing cumulative size',
|
||||
attachments: attachments
|
||||
to: 'recipient@example.com',
|
||||
subject: 'Normal Size Test',
|
||||
text: 'Testing normal size email that should succeed'
|
||||
});
|
||||
|
||||
console.log(`Total attachment size: ${attachments.length * 2} MB`);
|
||||
const result = await smtpClient.sendMail(email);
|
||||
|
||||
try {
|
||||
await smtpClient.sendMail(multiAttachEmail);
|
||||
console.log('Multiple attachments accepted');
|
||||
} catch (error) {
|
||||
console.log('Rejected due to cumulative size:', error.message);
|
||||
}
|
||||
expect(result.success).toBeTrue();
|
||||
console.log('✅ Normal size email sent successfully');
|
||||
|
||||
await smtpClient.close();
|
||||
});
|
||||
|
||||
tap.test('cleanup test SMTP server', async () => {
|
||||
if (testServer) {
|
||||
await testServer.stop();
|
||||
}
|
||||
tap.test('cleanup - stop SMTP server', async () => {
|
||||
await stopTestServer(testServer);
|
||||
});
|
||||
|
||||
export default tap.start();
|
Reference in New Issue
Block a user