update
This commit is contained in:
459
test/suite/email-processing/test.special-character-handling.ts
Normal file
459
test/suite/email-processing/test.special-character-handling.ts
Normal file
@ -0,0 +1,459 @@
|
||||
import { tap, expect } from '@push.rocks/tapbundle';
|
||||
import * as net from 'net';
|
||||
import { startTestServer, stopTestServer, TEST_PORT, sendEmailWithRawSocket } from '../server.loader.js';
|
||||
|
||||
let testServer: any;
|
||||
|
||||
tap.test('setup - start test server', async () => {
|
||||
testServer = await startTestServer();
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
});
|
||||
|
||||
tap.test('Special Character Handling - Comprehensive Unicode test', 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: Special Character Test - Unicode & Symbols ñáéíóú`,
|
||||
`Date: ${new Date().toUTCString()}`,
|
||||
`Message-ID: <special-chars-${Date.now()}@example.com>`,
|
||||
`MIME-Version: 1.0`,
|
||||
`Content-Type: text/plain; charset=utf-8`,
|
||||
`Content-Transfer-Encoding: 8bit`,
|
||||
'',
|
||||
'This email tests special character handling:',
|
||||
'',
|
||||
'=== UNICODE CHARACTERS ===',
|
||||
'Accented letters: àáâãäåæçèéêëìíîïñòóôõöøùúûüý',
|
||||
'German umlauts: äöüÄÖÜß',
|
||||
'Scandinavian: åäöÅÄÖ',
|
||||
'French: àâéèêëïîôœùûüÿç',
|
||||
'Spanish: ñáéíóúü¿¡',
|
||||
'Polish: ąćęłńóśźż',
|
||||
'Russian: абвгдеёжзийклмнопрстуфхцчшщъыьэюя',
|
||||
'Greek: αβγδεζηθικλμνξοπρστυφχψω',
|
||||
'Arabic: العربية',
|
||||
'Hebrew: עברית',
|
||||
'Chinese: 中文测试',
|
||||
'Japanese: 日本語テスト',
|
||||
'Korean: 한국어 테스트',
|
||||
'Thai: ภาษาไทย',
|
||||
'',
|
||||
'=== MATHEMATICAL SYMBOLS ===',
|
||||
'Math: ∑∏∫∆∇∂∞±×÷≠≤≥≈∝∪∩⊂⊃∈∀∃',
|
||||
'Greek letters: αβγδεζηθικλμνξοπρστυφχψω',
|
||||
'Arrows: ←→↑↓↔↕⇐⇒⇑⇓⇔⇕',
|
||||
'',
|
||||
'=== CURRENCY & SYMBOLS ===',
|
||||
'Currency: $€£¥¢₹₽₩₪₫₨₦₡₵₴₸₼₲₱',
|
||||
'Symbols: ©®™§¶†‡•…‰‱°℃℉№',
|
||||
'Punctuation: «»""''‚„‹›–—―‖‗''""‚„…‰′″‴‵‶‷‸‹›※‼‽⁇⁈⁉⁏⁐⁑⁒⁓⁔⁕⁖⁗⁘⁙⁚⁛⁜⁝⁞',
|
||||
'',
|
||||
'=== EMOJI & SYMBOLS ===',
|
||||
'Common: ☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓☔☕☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷',
|
||||
'Smileys: ☺☻☹☿♀♁♂♃♄♅♆♇',
|
||||
'Hearts: ♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯',
|
||||
'',
|
||||
'=== SPECIAL FORMATTING ===',
|
||||
'Zero-width chars: ',
|
||||
'Combining: e̊åa̋o̧ç',
|
||||
'Ligatures: fffiflffifflſtst',
|
||||
'Fractions: ½⅓⅔¼¾⅛⅜⅝⅞',
|
||||
'Superscript: ⁰¹²³⁴⁵⁶⁷⁸⁹',
|
||||
'Subscript: ₀₁₂₃₄₅₆₇₈₉',
|
||||
'',
|
||||
'End of special character test.',
|
||||
'.',
|
||||
''
|
||||
].join('\r\n');
|
||||
|
||||
console.log('Sending email with comprehensive Unicode characters');
|
||||
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 special characters accepted successfully');
|
||||
expect(true).toBeTrue();
|
||||
|
||||
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('Special Character Handling - Control characters', 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: Control Character Test`,
|
||||
`Date: ${new Date().toUTCString()}`,
|
||||
`Message-ID: <control-chars-${Date.now()}@example.com>`,
|
||||
`MIME-Version: 1.0`,
|
||||
`Content-Type: text/plain; charset=utf-8`,
|
||||
'',
|
||||
'=== CONTROL CHARACTERS TEST ===',
|
||||
'Tab character: (between words)',
|
||||
'Non-breaking space: word word',
|
||||
'Soft hyphen: supercalifragilisticexpialidocious',
|
||||
'Vertical tab: word\x0Bword',
|
||||
'Form feed: word\x0Cword',
|
||||
'Backspace: word\x08word',
|
||||
'',
|
||||
'=== LINE ENDING TESTS ===',
|
||||
'Unix LF: Line1\nLine2',
|
||||
'Windows CRLF: Line3\r\nLine4',
|
||||
'Mac CR: Line5\rLine6',
|
||||
'',
|
||||
'=== BOUNDARY CHARACTERS ===',
|
||||
'SMTP boundary test: . (dot at start)',
|
||||
'Double dots: .. (escaped in SMTP)',
|
||||
'CRLF.CRLF sequence test',
|
||||
'.',
|
||||
''
|
||||
].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 control characters accepted');
|
||||
expect(true).toBeTrue();
|
||||
|
||||
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('Special Character Handling - Subject header 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?B?8J+YgCBFbW9qaSBpbiBTdWJqZWN0IOKcqCDwn4yI?=`,
|
||||
`Subject: =?UTF-8?Q?Quoted=2DPrintable=20Subject=20=C3=A1=C3=A9=C3=AD=C3=B3=C3=BA?=`,
|
||||
`Date: ${new Date().toUTCString()}`,
|
||||
`Message-ID: <encoded-subject-${Date.now()}@example.com>`,
|
||||
'',
|
||||
'Testing encoded subject headers with special characters.',
|
||||
'.',
|
||||
''
|
||||
].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 encoded subject headers accepted');
|
||||
expect(true).toBeTrue();
|
||||
|
||||
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('Special Character Handling - Address headers with special chars', 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: "José García" <jose@example.com>`,
|
||||
`To: "François Müller" <francois@example.com>, "北京用户" <beijing@example.com>`,
|
||||
`Cc: =?UTF-8?B?IkFubmEgw4XDpMO2Ig==?= <anna@example.com>`,
|
||||
`Reply-To: "Søren Ñoño" <soren@example.com>`,
|
||||
`Subject: Special names in address headers`,
|
||||
`Date: ${new Date().toUTCString()}`,
|
||||
`Message-ID: <special-addrs-${Date.now()}@example.com>`,
|
||||
'',
|
||||
'Testing special characters in email addresses and display names.',
|
||||
'.',
|
||||
''
|
||||
].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 special characters in addresses accepted');
|
||||
expect(true).toBeTrue();
|
||||
|
||||
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('Special Character Handling - Mixed encodings', 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 = 'mixed-encoding-boundary';
|
||||
|
||||
const email = [
|
||||
`From: sender@example.com`,
|
||||
`To: recipient@example.com`,
|
||||
`Subject: Mixed Encoding Test`,
|
||||
`Date: ${new Date().toUTCString()}`,
|
||||
`Message-ID: <mixed-enc-${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: 8bit`,
|
||||
'',
|
||||
'UTF-8 part: ñáéíóú 中文 日本語',
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: text/plain; charset=iso-8859-1`,
|
||||
`Content-Transfer-Encoding: quoted-printable`,
|
||||
'',
|
||||
'ISO-8859-1 part: =F1=E1=E9=ED=F3=FA',
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: text/plain; charset=windows-1252`,
|
||||
'',
|
||||
'Windows-1252 part: €‚ƒ„…†‡',
|
||||
'',
|
||||
`--${boundary}`,
|
||||
`Content-Type: text/plain; charset=utf-16`,
|
||||
`Content-Transfer-Encoding: base64`,
|
||||
'',
|
||||
Buffer.from('UTF-16 text: ñoño', 'utf16le').toString('base64'),
|
||||
'',
|
||||
`--${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 mixed character encodings accepted');
|
||||
expect(true).toBeTrue();
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
tap.start();
|
Reference in New Issue
Block a user