409 lines
13 KiB
TypeScript
409 lines
13 KiB
TypeScript
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
|
import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.js';
|
|
import { createSmtpClient } from '../../../ts/mail/delivery/smtpclient/index.js';
|
|
import type { SmtpClient } from '../../../ts/mail/delivery/smtpclient/smtp-client.js';
|
|
import { Email } from '../../../ts/mail/core/classes.email.js';
|
|
|
|
let testServer: ITestServer;
|
|
let smtpClient: SmtpClient;
|
|
|
|
tap.test('setup test SMTP server', async () => {
|
|
testServer = await startTestServer({
|
|
port: 2551,
|
|
tlsEnabled: false,
|
|
authRequired: false
|
|
});
|
|
expect(testServer).toBeTruthy();
|
|
expect(testServer.port).toBeGreaterThan(0);
|
|
});
|
|
|
|
tap.test('CCMD-11: Server capabilities discovery', async () => {
|
|
// Test server capabilities which is what HELP provides info about
|
|
smtpClient = createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
connectionTimeout: 5000
|
|
});
|
|
|
|
console.log('Testing server capabilities discovery (HELP equivalent):\n');
|
|
|
|
// Send a test email to see server capabilities in action
|
|
const testEmail = new Email({
|
|
from: 'sender@example.com',
|
|
to: ['recipient@example.com'],
|
|
subject: 'Capability test',
|
|
text: 'Testing server capabilities'
|
|
});
|
|
|
|
await smtpClient.sendMail(testEmail);
|
|
console.log('Email sent successfully - server supports basic SMTP commands');
|
|
|
|
// Test different configurations to understand server behavior
|
|
const capabilities = {
|
|
basicSMTP: true,
|
|
multiplRecipients: false,
|
|
largeMessages: false,
|
|
internationalDomains: false
|
|
};
|
|
|
|
// Test multiple recipients
|
|
try {
|
|
const multiEmail = new Email({
|
|
from: 'sender@example.com',
|
|
to: ['recipient1@example.com', 'recipient2@example.com', 'recipient3@example.com'],
|
|
subject: 'Multi-recipient test',
|
|
text: 'Testing multiple recipients'
|
|
});
|
|
await smtpClient.sendMail(multiEmail);
|
|
capabilities.multiplRecipients = true;
|
|
console.log('✓ Server supports multiple recipients');
|
|
} catch (error) {
|
|
console.log('✗ Multiple recipients not supported');
|
|
}
|
|
|
|
console.log('\nDetected capabilities:', capabilities);
|
|
});
|
|
|
|
tap.test('CCMD-11: Error message diagnostics', async () => {
|
|
// Test error messages which HELP would explain
|
|
console.log('Testing error message diagnostics:\n');
|
|
|
|
const errorTests = [
|
|
{
|
|
description: 'Invalid sender address',
|
|
email: {
|
|
from: 'invalid-sender',
|
|
to: ['recipient@example.com'],
|
|
subject: 'Test',
|
|
text: 'Test'
|
|
}
|
|
},
|
|
{
|
|
description: 'Empty recipient list',
|
|
email: {
|
|
from: 'sender@example.com',
|
|
to: [],
|
|
subject: 'Test',
|
|
text: 'Test'
|
|
}
|
|
},
|
|
{
|
|
description: 'Null subject',
|
|
email: {
|
|
from: 'sender@example.com',
|
|
to: ['recipient@example.com'],
|
|
subject: null as any,
|
|
text: 'Test'
|
|
}
|
|
}
|
|
];
|
|
|
|
for (const test of errorTests) {
|
|
console.log(`Testing: ${test.description}`);
|
|
try {
|
|
const email = new Email(test.email);
|
|
await smtpClient.sendMail(email);
|
|
console.log(' Unexpectedly succeeded');
|
|
} catch (error) {
|
|
console.log(` Error: ${error.message}`);
|
|
console.log(` This would be explained in HELP documentation`);
|
|
}
|
|
console.log('');
|
|
}
|
|
});
|
|
|
|
tap.test('CCMD-11: Connection configuration help', async () => {
|
|
// Test different connection configurations
|
|
console.log('Testing connection configurations:\n');
|
|
|
|
const configs = [
|
|
{
|
|
name: 'Standard connection',
|
|
config: {
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
connectionTimeout: 5000
|
|
},
|
|
shouldWork: true
|
|
},
|
|
{
|
|
name: 'With greeting timeout',
|
|
config: {
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
connectionTimeout: 5000,
|
|
greetingTimeout: 3000
|
|
},
|
|
shouldWork: true
|
|
},
|
|
{
|
|
name: 'With socket timeout',
|
|
config: {
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
connectionTimeout: 5000,
|
|
socketTimeout: 10000
|
|
},
|
|
shouldWork: true
|
|
}
|
|
];
|
|
|
|
for (const testConfig of configs) {
|
|
console.log(`Testing: ${testConfig.name}`);
|
|
try {
|
|
const client = createSmtpClient(testConfig.config);
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: ['recipient@example.com'],
|
|
subject: 'Config test',
|
|
text: `Testing ${testConfig.name}`
|
|
});
|
|
|
|
await client.sendMail(email);
|
|
console.log(` ✓ Configuration works`);
|
|
} catch (error) {
|
|
console.log(` ✗ Error: ${error.message}`);
|
|
}
|
|
}
|
|
});
|
|
|
|
tap.test('CCMD-11: Protocol flow documentation', async () => {
|
|
// Document the protocol flow (what HELP would explain)
|
|
console.log('SMTP Protocol Flow (as HELP would document):\n');
|
|
|
|
const protocolSteps = [
|
|
'1. Connection established',
|
|
'2. Server sends greeting (220)',
|
|
'3. Client sends EHLO',
|
|
'4. Server responds with capabilities',
|
|
'5. Client sends MAIL FROM',
|
|
'6. Server accepts sender (250)',
|
|
'7. Client sends RCPT TO',
|
|
'8. Server accepts recipient (250)',
|
|
'9. Client sends DATA',
|
|
'10. Server ready for data (354)',
|
|
'11. Client sends message content',
|
|
'12. Client sends . to end',
|
|
'13. Server accepts message (250)',
|
|
'14. Client can send more or QUIT'
|
|
];
|
|
|
|
console.log('Standard SMTP transaction flow:');
|
|
protocolSteps.forEach(step => console.log(` ${step}`));
|
|
|
|
// Demonstrate the flow
|
|
console.log('\nDemonstrating flow with actual email:');
|
|
const email = new Email({
|
|
from: 'demo@example.com',
|
|
to: ['recipient@example.com'],
|
|
subject: 'Protocol flow demo',
|
|
text: 'Demonstrating SMTP protocol flow'
|
|
});
|
|
|
|
await smtpClient.sendMail(email);
|
|
console.log('✓ Protocol flow completed successfully');
|
|
});
|
|
|
|
tap.test('CCMD-11: Command availability matrix', async () => {
|
|
// Test what commands are available (HELP info)
|
|
console.log('Testing command availability:\n');
|
|
|
|
// Test various email features to determine support
|
|
const features = {
|
|
plainText: { supported: false, description: 'Plain text emails' },
|
|
htmlContent: { supported: false, description: 'HTML emails' },
|
|
attachments: { supported: false, description: 'File attachments' },
|
|
multipleRecipients: { supported: false, description: 'Multiple recipients' },
|
|
ccRecipients: { supported: false, description: 'CC recipients' },
|
|
bccRecipients: { supported: false, description: 'BCC recipients' },
|
|
customHeaders: { supported: false, description: 'Custom headers' },
|
|
priorities: { supported: false, description: 'Email priorities' }
|
|
};
|
|
|
|
// Test plain text
|
|
try {
|
|
await smtpClient.sendMail(new Email({
|
|
from: 'sender@example.com',
|
|
to: ['recipient@example.com'],
|
|
subject: 'Plain text test',
|
|
text: 'Plain text content'
|
|
}));
|
|
features.plainText.supported = true;
|
|
} catch (e) {}
|
|
|
|
// Test HTML
|
|
try {
|
|
await smtpClient.sendMail(new Email({
|
|
from: 'sender@example.com',
|
|
to: ['recipient@example.com'],
|
|
subject: 'HTML test',
|
|
html: '<p>HTML content</p>'
|
|
}));
|
|
features.htmlContent.supported = true;
|
|
} catch (e) {}
|
|
|
|
// Test multiple recipients
|
|
try {
|
|
await smtpClient.sendMail(new Email({
|
|
from: 'sender@example.com',
|
|
to: ['recipient1@example.com', 'recipient2@example.com'],
|
|
subject: 'Multiple recipients test',
|
|
text: 'Test'
|
|
}));
|
|
features.multipleRecipients.supported = true;
|
|
} catch (e) {}
|
|
|
|
// Test CC
|
|
try {
|
|
await smtpClient.sendMail(new Email({
|
|
from: 'sender@example.com',
|
|
to: ['recipient@example.com'],
|
|
cc: ['cc@example.com'],
|
|
subject: 'CC test',
|
|
text: 'Test'
|
|
}));
|
|
features.ccRecipients.supported = true;
|
|
} catch (e) {}
|
|
|
|
// Test BCC
|
|
try {
|
|
await smtpClient.sendMail(new Email({
|
|
from: 'sender@example.com',
|
|
to: ['recipient@example.com'],
|
|
bcc: ['bcc@example.com'],
|
|
subject: 'BCC test',
|
|
text: 'Test'
|
|
}));
|
|
features.bccRecipients.supported = true;
|
|
} catch (e) {}
|
|
|
|
console.log('Feature support matrix:');
|
|
Object.entries(features).forEach(([key, value]) => {
|
|
console.log(` ${value.description}: ${value.supported ? '✓ Supported' : '✗ Not supported'}`);
|
|
});
|
|
});
|
|
|
|
tap.test('CCMD-11: Error code reference', async () => {
|
|
// Document error codes (HELP would explain these)
|
|
console.log('SMTP Error Code Reference (as HELP would provide):\n');
|
|
|
|
const errorCodes = [
|
|
{ code: '220', meaning: 'Service ready', type: 'Success' },
|
|
{ code: '221', meaning: 'Service closing transmission channel', type: 'Success' },
|
|
{ code: '250', meaning: 'Requested action completed', type: 'Success' },
|
|
{ code: '251', meaning: 'User not local; will forward', type: 'Success' },
|
|
{ code: '354', meaning: 'Start mail input', type: 'Intermediate' },
|
|
{ code: '421', meaning: 'Service not available', type: 'Temporary failure' },
|
|
{ code: '450', meaning: 'Mailbox unavailable', type: 'Temporary failure' },
|
|
{ code: '451', meaning: 'Local error in processing', type: 'Temporary failure' },
|
|
{ code: '452', meaning: 'Insufficient storage', type: 'Temporary failure' },
|
|
{ code: '500', meaning: 'Syntax error', type: 'Permanent failure' },
|
|
{ code: '501', meaning: 'Syntax error in parameters', type: 'Permanent failure' },
|
|
{ code: '502', meaning: 'Command not implemented', type: 'Permanent failure' },
|
|
{ code: '503', meaning: 'Bad sequence of commands', type: 'Permanent failure' },
|
|
{ code: '550', meaning: 'Mailbox not found', type: 'Permanent failure' },
|
|
{ code: '551', meaning: 'User not local', type: 'Permanent failure' },
|
|
{ code: '552', meaning: 'Storage allocation exceeded', type: 'Permanent failure' },
|
|
{ code: '553', meaning: 'Mailbox name not allowed', type: 'Permanent failure' },
|
|
{ code: '554', meaning: 'Transaction failed', type: 'Permanent failure' }
|
|
];
|
|
|
|
console.log('Common SMTP response codes:');
|
|
errorCodes.forEach(({ code, meaning, type }) => {
|
|
console.log(` ${code} - ${meaning} (${type})`);
|
|
});
|
|
|
|
// Test triggering some errors
|
|
console.log('\nDemonstrating error handling:');
|
|
|
|
// Invalid email format
|
|
try {
|
|
await smtpClient.sendMail(new Email({
|
|
from: 'invalid-email-format',
|
|
to: ['recipient@example.com'],
|
|
subject: 'Test',
|
|
text: 'Test'
|
|
}));
|
|
} catch (error) {
|
|
console.log(`Invalid format error: ${error.message}`);
|
|
}
|
|
});
|
|
|
|
tap.test('CCMD-11: Debugging assistance', async () => {
|
|
// Test debugging features (HELP assists with debugging)
|
|
console.log('Debugging assistance features:\n');
|
|
|
|
// Create client with debug enabled
|
|
const debugClient = createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
connectionTimeout: 5000,
|
|
debug: true
|
|
});
|
|
|
|
console.log('Sending email with debug mode enabled:');
|
|
console.log('(Debug output would show full SMTP conversation)\n');
|
|
|
|
const debugEmail = new Email({
|
|
from: 'debug@example.com',
|
|
to: ['recipient@example.com'],
|
|
subject: 'Debug test',
|
|
text: 'Testing with debug mode'
|
|
});
|
|
|
|
// The debug output will be visible in the console
|
|
await debugClient.sendMail(debugEmail);
|
|
|
|
console.log('\nDebug mode helps troubleshoot:');
|
|
console.log('- Connection issues');
|
|
console.log('- Authentication problems');
|
|
console.log('- Message formatting errors');
|
|
console.log('- Server response codes');
|
|
console.log('- Protocol violations');
|
|
});
|
|
|
|
tap.test('CCMD-11: Performance benchmarks', async () => {
|
|
// Performance info (HELP might mention performance tips)
|
|
console.log('Performance benchmarks:\n');
|
|
|
|
const messageCount = 10;
|
|
const startTime = Date.now();
|
|
|
|
for (let i = 0; i < messageCount; i++) {
|
|
const email = new Email({
|
|
from: 'perf@example.com',
|
|
to: ['recipient@example.com'],
|
|
subject: `Performance test ${i + 1}`,
|
|
text: 'Testing performance'
|
|
});
|
|
|
|
await smtpClient.sendMail(email);
|
|
}
|
|
|
|
const totalTime = Date.now() - startTime;
|
|
const avgTime = totalTime / messageCount;
|
|
|
|
console.log(`Sent ${messageCount} emails in ${totalTime}ms`);
|
|
console.log(`Average time per email: ${avgTime.toFixed(2)}ms`);
|
|
console.log(`Throughput: ${(1000 / avgTime).toFixed(2)} emails/second`);
|
|
|
|
console.log('\nPerformance tips:');
|
|
console.log('- Use connection pooling for multiple emails');
|
|
console.log('- Enable pipelining when supported');
|
|
console.log('- Batch recipients when possible');
|
|
console.log('- Use appropriate timeouts');
|
|
console.log('- Monitor connection limits');
|
|
});
|
|
|
|
tap.test('cleanup test SMTP server', async () => {
|
|
if (testServer) {
|
|
await stopTestServer(testServer);
|
|
}
|
|
});
|
|
|
|
export default tap.start(); |