dcrouter/test/suite/edge-cases/test.very-small-email.ts

389 lines
11 KiB
TypeScript
Raw Normal View History

2025-05-23 19:03:44 +00:00
import { tap, expect } from '@git.zone/tapbundle';
import * as net from 'net';
import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.js';
const TEST_PORT = 30034;
const TEST_TIMEOUT = 30000;
tap.test('Very Small Email - should handle minimal email with single character body', async (tools) => {
const done = tools.defer();
// Start test server
const testServer = await startTestServer({ port: TEST_PORT });
try {
const socket = net.createConnection({
host: 'localhost',
port: TEST_PORT,
timeout: TEST_TIMEOUT
});
await new Promise<void>((resolve, reject) => {
socket.once('connect', () => resolve());
socket.once('error', reject);
});
// Get banner
await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
// Send EHLO
socket.write('EHLO testhost\r\n');
await new Promise<string>((resolve) => {
let data = '';
const handler = (chunk: Buffer) => {
data += chunk.toString();
if (data.includes('\r\n') && (data.match(/^250 /m) || data.match(/^250-.*\r\n250 /ms))) {
socket.removeListener('data', handler);
resolve(data);
}
};
socket.on('data', handler);
});
// Send MAIL FROM
socket.write('MAIL FROM:<sender@example.com>\r\n');
const mailResponse = await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
expect(mailResponse).toInclude('250');
// Send RCPT TO
socket.write('RCPT TO:<recipient@example.com>\r\n');
const rcptResponse = await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
expect(rcptResponse).toInclude('250');
// Send DATA
socket.write('DATA\r\n');
const dataResponse = await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
expect(dataResponse).toInclude('354');
// Send minimal email - just required headers and single character body
const minimalEmail = 'From: sender@example.com\r\nTo: recipient@example.com\r\nSubject: \r\n\r\nX\r\n.\r\n';
socket.write(minimalEmail);
const finalResponse = await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
expect(finalResponse).toInclude('250');
console.log(`Minimal email (${minimalEmail.length} bytes) processed successfully`);
// Clean up
socket.write('QUIT\r\n');
socket.end();
} finally {
await stopTestServer(testServer);
done.resolve();
}
});
tap.test('Very Small Email - should handle email with empty body', async (tools) => {
const done = tools.defer();
// Start test server
const testServer = await startTestServer({ port: TEST_PORT });
try {
const socket = net.createConnection({
host: 'localhost',
port: TEST_PORT,
timeout: TEST_TIMEOUT
});
await new Promise<void>((resolve, reject) => {
socket.once('connect', () => resolve());
socket.once('error', reject);
});
// Get banner
await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
// Send EHLO
socket.write('EHLO testhost\r\n');
await new Promise<string>((resolve) => {
let data = '';
const handler = (chunk: Buffer) => {
data += chunk.toString();
if (data.includes('\r\n') && (data.match(/^250 /m) || data.match(/^250-.*\r\n250 /ms))) {
socket.removeListener('data', handler);
resolve(data);
}
};
socket.on('data', handler);
});
// Complete envelope
socket.write('MAIL FROM:<sender@example.com>\r\n');
await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
socket.write('RCPT TO:<recipient@example.com>\r\n');
await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
socket.write('DATA\r\n');
await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
// Send email with empty body
const emptyBodyEmail = 'From: sender@example.com\r\nTo: recipient@example.com\r\n\r\n.\r\n';
socket.write(emptyBodyEmail);
const finalResponse = await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
expect(finalResponse).toInclude('250');
console.log('Email with empty body processed successfully');
// Clean up
socket.write('QUIT\r\n');
socket.end();
} finally {
await stopTestServer(testServer);
done.resolve();
}
});
tap.test('Very Small Email - should handle email with minimal headers only', async (tools) => {
const done = tools.defer();
// Start test server
const testServer = await startTestServer({ port: TEST_PORT });
try {
const socket = net.createConnection({
host: 'localhost',
port: TEST_PORT,
timeout: TEST_TIMEOUT
});
await new Promise<void>((resolve, reject) => {
socket.once('connect', () => resolve());
socket.once('error', reject);
});
// Get banner and send EHLO
await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
socket.write('EHLO testhost\r\n');
await new Promise<string>((resolve) => {
let data = '';
const handler = (chunk: Buffer) => {
data += chunk.toString();
if (data.includes('\r\n') && (data.match(/^250 /m) || data.match(/^250-.*\r\n250 /ms))) {
socket.removeListener('data', handler);
resolve(data);
}
};
socket.on('data', handler);
});
// Complete envelope
socket.write('MAIL FROM:<a@b.c>\r\n');
await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
socket.write('RCPT TO:<x@y.z>\r\n');
await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
socket.write('DATA\r\n');
await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
// Send absolutely minimal valid email
const minimalHeaders = 'From: a@b.c\r\n\r\n.\r\n';
socket.write(minimalHeaders);
const finalResponse = await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
expect(finalResponse).toInclude('250');
console.log(`Ultra-minimal email (${minimalHeaders.length} bytes) processed`);
// Clean up
socket.write('QUIT\r\n');
socket.end();
} finally {
await stopTestServer(testServer);
done.resolve();
}
});
tap.test('Very Small Email - should handle single dot line correctly', async (tools) => {
const done = tools.defer();
// Start test server
const testServer = await startTestServer({ port: TEST_PORT });
try {
const socket = net.createConnection({
host: 'localhost',
port: TEST_PORT,
timeout: TEST_TIMEOUT
});
await new Promise<void>((resolve, reject) => {
socket.once('connect', () => resolve());
socket.once('error', reject);
});
// Setup connection
await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
socket.write('EHLO testhost\r\n');
await new Promise<string>((resolve) => {
let data = '';
const handler = (chunk: Buffer) => {
data += chunk.toString();
if (data.includes('\r\n') && (data.match(/^250 /m) || data.match(/^250-.*\r\n250 /ms))) {
socket.removeListener('data', handler);
resolve(data);
}
};
socket.on('data', handler);
});
// Complete envelope
socket.write('MAIL FROM:<sender@example.com>\r\n');
await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
socket.write('RCPT TO:<recipient@example.com>\r\n');
await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
socket.write('DATA\r\n');
const dataResponse = await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
expect(dataResponse).toInclude('354');
// Test edge case: just the terminating dot
socket.write('.\r\n');
const finalResponse = await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
// Server should accept this as an email with no headers or body
expect(finalResponse).toMatch(/^[2-5]\d{2}/);
console.log('Single dot terminator handled:', finalResponse.trim());
// Clean up
socket.write('QUIT\r\n');
socket.end();
} finally {
await stopTestServer(testServer);
done.resolve();
}
});
tap.test('Very Small Email - should handle email with empty subject', async (tools) => {
const done = tools.defer();
// Start test server
const testServer = await startTestServer({ port: TEST_PORT });
try {
const socket = net.createConnection({
host: 'localhost',
port: TEST_PORT,
timeout: TEST_TIMEOUT
});
await new Promise<void>((resolve, reject) => {
socket.once('connect', () => resolve());
socket.once('error', reject);
});
// Setup connection
await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
socket.write('EHLO testhost\r\n');
await new Promise<string>((resolve) => {
let data = '';
const handler = (chunk: Buffer) => {
data += chunk.toString();
if (data.includes('\r\n') && (data.match(/^250 /m) || data.match(/^250-.*\r\n250 /ms))) {
socket.removeListener('data', handler);
resolve(data);
}
};
socket.on('data', handler);
});
// Complete envelope
socket.write('MAIL FROM:<sender@example.com>\r\n');
await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
socket.write('RCPT TO:<recipient@example.com>\r\n');
await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
socket.write('DATA\r\n');
await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
// Send email with empty subject line
const emptySubjectEmail =
'From: sender@example.com\r\n' +
'To: recipient@example.com\r\n' +
'Subject: \r\n' +
'Date: ' + new Date().toUTCString() + '\r\n' +
'\r\n' +
'Email with empty subject.\r\n' +
'.\r\n';
socket.write(emptySubjectEmail);
const finalResponse = await new Promise<string>((resolve) => {
socket.once('data', (chunk) => resolve(chunk.toString()));
});
expect(finalResponse).toInclude('250');
console.log('Email with empty subject processed successfully');
// Clean up
socket.write('QUIT\r\n');
socket.end();
} finally {
await stopTestServer(testServer);
done.resolve();
}
});
tap.start();