update
This commit is contained in:
@ -0,0 +1,515 @@
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
import * as net from 'net';
|
||||
import { startTestServer, stopTestServer } from '../../helpers/server.loader.js'
|
||||
import type { ITestServer } from '../../helpers/server.loader.js';
|
||||
|
||||
const TEST_PORT = 2525;
|
||||
let testServer: ITestServer;
|
||||
|
||||
tap.test('setup - start test server', async () => {
|
||||
testServer = await startTestServer({ port: TEST_PORT });
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
});
|
||||
|
||||
tap.test('MIME Handling - Comprehensive multipart message', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
const socket = net.createConnection({
|
||||
host: 'localhost',
|
||||
port: TEST_PORT,
|
||||
timeout: 30000
|
||||
});
|
||||
|
||||
let dataBuffer = '';
|
||||
let step = 'greeting';
|
||||
let completed = false;
|
||||
|
||||
socket.on('data', (data) => {
|
||||
dataBuffer += data.toString();
|
||||
console.log('Server response:', data.toString());
|
||||
|
||||
if (step === 'greeting' && dataBuffer.includes('220 ')) {
|
||||
step = 'ehlo';
|
||||
socket.write('EHLO testclient\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'ehlo' && dataBuffer.includes('250')) {
|
||||
step = 'mail';
|
||||
socket.write('MAIL FROM:<sender@example.com>\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'mail' && dataBuffer.includes('250')) {
|
||||
step = 'rcpt';
|
||||
socket.write('RCPT TO:<recipient@example.com>\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'rcpt' && dataBuffer.includes('250')) {
|
||||
step = 'data';
|
||||
socket.write('DATA\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'data' && dataBuffer.includes('354')) {
|
||||
// Create comprehensive MIME test email
|
||||
const boundary = 'mime-test-boundary-12345';
|
||||
const innerBoundary = 'inner-mime-boundary-67890';
|
||||
|
||||
const email = [
|
||||
`From: sender@example.com`,
|
||||
`To: recipient@example.com`,
|
||||
`Subject: MIME Handling Test - Comprehensive`,
|
||||
`Date: ${new Date().toUTCString()}`,
|
||||
`Message-ID: <mime-test-${Date.now()}@example.com>`,
|
||||
`MIME-Version: 1.0`,
|
||||
`Content-Type: multipart/mixed; boundary="${boundary}"`,
|
||||
'',
|
||||
'This is a multi-part message in MIME format.',
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: text/plain; charset=utf-8`,
|
||||
`Content-Transfer-Encoding: 7bit`,
|
||||
'',
|
||||
'This is the plain text part of the email.',
|
||||
'It tests basic MIME text handling.',
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: text/html; charset=utf-8`,
|
||||
`Content-Transfer-Encoding: quoted-printable`,
|
||||
'',
|
||||
'<html>',
|
||||
'<head><title>MIME Test</title></head>',
|
||||
'<body>',
|
||||
'<h1>HTML MIME Content</h1>',
|
||||
'<p>This tests HTML MIME content handling.</p>',
|
||||
'<p>Special chars: =E2=98=85 =E2=9C=93 =E2=9D=A4</p>',
|
||||
'</body>',
|
||||
'</html>',
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: multipart/alternative; boundary="${innerBoundary}"`,
|
||||
'',
|
||||
`--${innerBoundary}`,
|
||||
`Content-Type: text/plain; charset=iso-8859-1`,
|
||||
`Content-Transfer-Encoding: base64`,
|
||||
'',
|
||||
'VGhpcyBpcyBiYXNlNjQgZW5jb2RlZCB0ZXh0IGNvbnRlbnQu',
|
||||
'',
|
||||
`--${innerBoundary}`,
|
||||
`Content-Type: application/json; charset=utf-8`,
|
||||
'',
|
||||
'{"message": "JSON MIME content", "test": true, "special": "àáâãäå"}',
|
||||
'',
|
||||
`--${innerBoundary}--`,
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: image/png`,
|
||||
`Content-Disposition: attachment; filename="test.png"`,
|
||||
`Content-Transfer-Encoding: base64`,
|
||||
'',
|
||||
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: text/csv`,
|
||||
`Content-Disposition: attachment; filename="data.csv"`,
|
||||
'',
|
||||
'Name,Age,Email',
|
||||
'John,25,john@example.com',
|
||||
'Jane,30,jane@example.com',
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: application/pdf`,
|
||||
`Content-Disposition: attachment; filename="document.pdf"`,
|
||||
`Content-Transfer-Encoding: base64`,
|
||||
'',
|
||||
'JVBERi0xLjQKJcOkw7zDtsOVDQo=',
|
||||
'',
|
||||
`--${boundary}--`,
|
||||
'.',
|
||||
''
|
||||
].join('\r\n');
|
||||
|
||||
console.log('Sending comprehensive MIME email with multiple parts and encodings');
|
||||
socket.write(email);
|
||||
step = 'sent';
|
||||
dataBuffer = '';
|
||||
} else if (step === 'sent' && dataBuffer.includes('250 ') && dataBuffer.includes('message queued')) {
|
||||
if (!completed) {
|
||||
completed = true;
|
||||
console.log('Complex MIME message accepted successfully');
|
||||
expect(true).toEqual(true);
|
||||
|
||||
socket.write('QUIT\r\n');
|
||||
socket.end();
|
||||
done.resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('error', (err) => {
|
||||
console.error('Socket error:', err);
|
||||
done.reject(err);
|
||||
});
|
||||
|
||||
await done.promise;
|
||||
});
|
||||
|
||||
tap.test('MIME Handling - Quoted-printable encoding', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
const socket = net.createConnection({
|
||||
host: 'localhost',
|
||||
port: TEST_PORT,
|
||||
timeout: 30000
|
||||
});
|
||||
|
||||
let dataBuffer = '';
|
||||
let step = 'greeting';
|
||||
let completed = false;
|
||||
|
||||
socket.on('data', (data) => {
|
||||
dataBuffer += data.toString();
|
||||
console.log('Server response:', data.toString());
|
||||
|
||||
if (step === 'greeting' && dataBuffer.includes('220 ')) {
|
||||
step = 'ehlo';
|
||||
socket.write('EHLO testclient\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'ehlo' && dataBuffer.includes('250')) {
|
||||
step = 'mail';
|
||||
socket.write('MAIL FROM:<sender@example.com>\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'mail' && dataBuffer.includes('250')) {
|
||||
step = 'rcpt';
|
||||
socket.write('RCPT TO:<recipient@example.com>\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'rcpt' && dataBuffer.includes('250')) {
|
||||
step = 'data';
|
||||
socket.write('DATA\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'data' && dataBuffer.includes('354')) {
|
||||
const email = [
|
||||
`From: sender@example.com`,
|
||||
`To: recipient@example.com`,
|
||||
`Subject: =?UTF-8?Q?Quoted=2DPrintable=20Test=20=F0=9F=8C=9F?=`,
|
||||
`Date: ${new Date().toUTCString()}`,
|
||||
`Message-ID: <qp-test-${Date.now()}@example.com>`,
|
||||
`MIME-Version: 1.0`,
|
||||
`Content-Type: text/plain; charset=utf-8`,
|
||||
`Content-Transfer-Encoding: quoted-printable`,
|
||||
'',
|
||||
'This is a test of quoted-printable encoding.',
|
||||
'Special characters: =C3=A9 =C3=A8 =C3=AA =C3=AB',
|
||||
'Long line that needs to be wrapped with soft line breaks at 76 character=',
|
||||
's per line to comply with MIME standards for quoted-printable encoding.',
|
||||
'Emoji: =F0=9F=98=80 =F0=9F=91=8D =F0=9F=8C=9F',
|
||||
'.',
|
||||
''
|
||||
].join('\r\n');
|
||||
|
||||
socket.write(email);
|
||||
step = 'sent';
|
||||
dataBuffer = '';
|
||||
} else if (step === 'sent' && dataBuffer.includes('250 ') && dataBuffer.includes('message queued')) {
|
||||
if (!completed) {
|
||||
completed = true;
|
||||
console.log('Quoted-printable encoded email accepted');
|
||||
expect(true).toEqual(true);
|
||||
|
||||
socket.write('QUIT\r\n');
|
||||
socket.end();
|
||||
done.resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('error', (err) => {
|
||||
console.error('Socket error:', err);
|
||||
done.reject(err);
|
||||
});
|
||||
|
||||
await done.promise;
|
||||
});
|
||||
|
||||
tap.test('MIME Handling - Base64 encoding', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
const socket = net.createConnection({
|
||||
host: 'localhost',
|
||||
port: TEST_PORT,
|
||||
timeout: 30000
|
||||
});
|
||||
|
||||
let dataBuffer = '';
|
||||
let step = 'greeting';
|
||||
let completed = false;
|
||||
|
||||
socket.on('data', (data) => {
|
||||
dataBuffer += data.toString();
|
||||
console.log('Server response:', data.toString());
|
||||
|
||||
if (step === 'greeting' && dataBuffer.includes('220 ')) {
|
||||
step = 'ehlo';
|
||||
socket.write('EHLO testclient\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'ehlo' && dataBuffer.includes('250')) {
|
||||
step = 'mail';
|
||||
socket.write('MAIL FROM:<sender@example.com>\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'mail' && dataBuffer.includes('250')) {
|
||||
step = 'rcpt';
|
||||
socket.write('RCPT TO:<recipient@example.com>\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'rcpt' && dataBuffer.includes('250')) {
|
||||
step = 'data';
|
||||
socket.write('DATA\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'data' && dataBuffer.includes('354')) {
|
||||
const boundary = 'base64-test-boundary';
|
||||
const textContent = 'This is a test of base64 encoding with various content types.\nSpecial chars: éèêë\nEmoji: 😀 👍 🌟';
|
||||
const base64Content = Buffer.from(textContent).toString('base64');
|
||||
|
||||
const email = [
|
||||
`From: sender@example.com`,
|
||||
`To: recipient@example.com`,
|
||||
`Subject: Base64 Encoding Test`,
|
||||
`Date: ${new Date().toUTCString()}`,
|
||||
`Message-ID: <base64-test-${Date.now()}@example.com>`,
|
||||
`MIME-Version: 1.0`,
|
||||
`Content-Type: multipart/mixed; boundary="${boundary}"`,
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: text/plain; charset=utf-8`,
|
||||
`Content-Transfer-Encoding: base64`,
|
||||
'',
|
||||
base64Content,
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: application/octet-stream`,
|
||||
`Content-Disposition: attachment; filename="binary.dat"`,
|
||||
`Content-Transfer-Encoding: base64`,
|
||||
'',
|
||||
'VGhpcyBpcyBiaW5hcnkgZGF0YSBmb3IgdGVzdGluZw==',
|
||||
'',
|
||||
`--${boundary}--`,
|
||||
'.',
|
||||
''
|
||||
].join('\r\n');
|
||||
|
||||
socket.write(email);
|
||||
step = 'sent';
|
||||
dataBuffer = '';
|
||||
} else if (step === 'sent' && dataBuffer.includes('250 ') && dataBuffer.includes('message queued')) {
|
||||
if (!completed) {
|
||||
completed = true;
|
||||
console.log('Base64 encoded email accepted');
|
||||
expect(true).toEqual(true);
|
||||
|
||||
socket.write('QUIT\r\n');
|
||||
socket.end();
|
||||
done.resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('error', (err) => {
|
||||
console.error('Socket error:', err);
|
||||
done.reject(err);
|
||||
});
|
||||
|
||||
await done.promise;
|
||||
});
|
||||
|
||||
tap.test('MIME Handling - Content-Disposition headers', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
const socket = net.createConnection({
|
||||
host: 'localhost',
|
||||
port: TEST_PORT,
|
||||
timeout: 30000
|
||||
});
|
||||
|
||||
let dataBuffer = '';
|
||||
let step = 'greeting';
|
||||
let completed = false;
|
||||
|
||||
socket.on('data', (data) => {
|
||||
dataBuffer += data.toString();
|
||||
console.log('Server response:', data.toString());
|
||||
|
||||
if (step === 'greeting' && dataBuffer.includes('220 ')) {
|
||||
step = 'ehlo';
|
||||
socket.write('EHLO testclient\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'ehlo' && dataBuffer.includes('250')) {
|
||||
step = 'mail';
|
||||
socket.write('MAIL FROM:<sender@example.com>\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'mail' && dataBuffer.includes('250')) {
|
||||
step = 'rcpt';
|
||||
socket.write('RCPT TO:<recipient@example.com>\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'rcpt' && dataBuffer.includes('250')) {
|
||||
step = 'data';
|
||||
socket.write('DATA\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'data' && dataBuffer.includes('354')) {
|
||||
const boundary = 'disposition-test-boundary';
|
||||
|
||||
const email = [
|
||||
`From: sender@example.com`,
|
||||
`To: recipient@example.com`,
|
||||
`Subject: Content-Disposition Test`,
|
||||
`Date: ${new Date().toUTCString()}`,
|
||||
`Message-ID: <disposition-test-${Date.now()}@example.com>`,
|
||||
`MIME-Version: 1.0`,
|
||||
`Content-Type: multipart/mixed; boundary="${boundary}"`,
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: text/plain`,
|
||||
`Content-Disposition: inline`,
|
||||
'',
|
||||
'This is inline text content.',
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: image/jpeg`,
|
||||
`Content-Disposition: attachment; filename="photo.jpg"`,
|
||||
`Content-Transfer-Encoding: base64`,
|
||||
'',
|
||||
'/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAEBAQ==',
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: application/pdf`,
|
||||
`Content-Disposition: attachment; filename="report.pdf"; size=1234`,
|
||||
`Content-Description: Monthly Report`,
|
||||
'',
|
||||
'PDF content here',
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: text/html`,
|
||||
`Content-Disposition: inline; filename="content.html"`,
|
||||
'',
|
||||
'<html><body>Inline HTML content</body></html>',
|
||||
'',
|
||||
`--${boundary}--`,
|
||||
'.',
|
||||
''
|
||||
].join('\r\n');
|
||||
|
||||
socket.write(email);
|
||||
step = 'sent';
|
||||
dataBuffer = '';
|
||||
} else if (step === 'sent' && dataBuffer.includes('250 ') && dataBuffer.includes('message queued')) {
|
||||
if (!completed) {
|
||||
completed = true;
|
||||
console.log('Email with various Content-Disposition headers accepted');
|
||||
expect(true).toEqual(true);
|
||||
|
||||
socket.write('QUIT\r\n');
|
||||
socket.end();
|
||||
done.resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('error', (err) => {
|
||||
console.error('Socket error:', err);
|
||||
done.reject(err);
|
||||
});
|
||||
|
||||
await done.promise;
|
||||
});
|
||||
|
||||
tap.test('MIME Handling - International character sets', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
const socket = net.createConnection({
|
||||
host: 'localhost',
|
||||
port: TEST_PORT,
|
||||
timeout: 30000
|
||||
});
|
||||
|
||||
let dataBuffer = '';
|
||||
let step = 'greeting';
|
||||
let completed = false;
|
||||
|
||||
socket.on('data', (data) => {
|
||||
dataBuffer += data.toString();
|
||||
console.log('Server response:', data.toString());
|
||||
|
||||
if (step === 'greeting' && dataBuffer.includes('220 ')) {
|
||||
step = 'ehlo';
|
||||
socket.write('EHLO testclient\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'ehlo' && dataBuffer.includes('250')) {
|
||||
step = 'mail';
|
||||
socket.write('MAIL FROM:<sender@example.com>\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'mail' && dataBuffer.includes('250')) {
|
||||
step = 'rcpt';
|
||||
socket.write('RCPT TO:<recipient@example.com>\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'rcpt' && dataBuffer.includes('250')) {
|
||||
step = 'data';
|
||||
socket.write('DATA\r\n');
|
||||
dataBuffer = '';
|
||||
} else if (step === 'data' && dataBuffer.includes('354')) {
|
||||
const boundary = 'intl-charset-boundary';
|
||||
|
||||
const email = [
|
||||
`From: sender@example.com`,
|
||||
`To: recipient@example.com`,
|
||||
`Subject: International Character Sets`,
|
||||
`Date: ${new Date().toUTCString()}`,
|
||||
`Message-ID: <intl-charset-${Date.now()}@example.com>`,
|
||||
`MIME-Version: 1.0`,
|
||||
`Content-Type: multipart/mixed; boundary="${boundary}"`,
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: text/plain; charset=utf-8`,
|
||||
'',
|
||||
'UTF-8: Français, Español, Deutsch, 中文, 日本語, 한국어, العربية',
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: text/plain; charset=iso-8859-1`,
|
||||
'',
|
||||
'ISO-8859-1: Français, Español, Português',
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: text/plain; charset=windows-1252`,
|
||||
'',
|
||||
'Windows-1252: Special chars: €‚ƒ„…†‡',
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: text/plain; charset=shift_jis`,
|
||||
'',
|
||||
'Shift-JIS: Japanese text',
|
||||
'',
|
||||
`--${boundary}--`,
|
||||
'.',
|
||||
''
|
||||
].join('\r\n');
|
||||
|
||||
socket.write(email);
|
||||
step = 'sent';
|
||||
dataBuffer = '';
|
||||
} else if (step === 'sent' && dataBuffer.includes('250 ') && dataBuffer.includes('message queued')) {
|
||||
if (!completed) {
|
||||
completed = true;
|
||||
console.log('Email with international character sets accepted');
|
||||
expect(true).toEqual(true);
|
||||
|
||||
socket.write('QUIT\r\n');
|
||||
socket.end();
|
||||
done.resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('error', (err) => {
|
||||
console.error('Socket error:', err);
|
||||
done.reject(err);
|
||||
});
|
||||
|
||||
await done.promise;
|
||||
});
|
||||
|
||||
tap.test('cleanup - stop test server', async () => {
|
||||
await stopTestServer(testServer);
|
||||
});
|
||||
|
||||
tap.start();
|
Reference in New Issue
Block a user