dcrouter/test/suite/smtpclient_error-handling/test.cerr-01.4xx-errors.ts
2025-05-24 16:19:19 +00:00

231 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 - start SMTP server for error handling tests', async () => {
testServer = await startTestServer({
port: 2550,
tlsEnabled: false,
authRequired: false,
maxRecipients: 5 // Low limit to trigger errors
});
expect(testServer.port).toEqual(2550);
});
tap.test('CERR-01: 4xx Errors - should handle invalid recipient (450)', async () => {
smtpClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 5000,
debug: true
});
// Create email with invalid recipient format
const email = new Email({
from: 'test@example.com',
to: 'invalid@address@multiple@signs.com', // Invalid format
subject: 'Testing 4xx Error',
text: 'This should trigger a 4xx error'
});
let errorCaught = false;
try {
await smtpClient.sendMail(email);
} catch (error) {
errorCaught = true;
expect(error).toBeInstanceOf(Error);
console.log('✅ Invalid recipient error caught:', error.message);
}
expect(errorCaught).toBeTrue();
});
tap.test('CERR-01: 4xx Errors - should handle mailbox unavailable (450)', async () => {
const email = new Email({
from: 'test@example.com',
to: 'nonexistent@localhost', // Local domain should trigger mailbox check
subject: 'Mailbox Unavailable Test',
text: 'Testing mailbox unavailable error'
});
const result = await smtpClient.sendMail(email);
// Depending on server configuration, this might be accepted or rejected
if (!result.success) {
expect(result.error).toBeInstanceOf(Error);
console.log('✅ Mailbox unavailable handled:', result.error?.message);
} else {
// Some test servers accept all recipients
expect(result.acceptedRecipients.length).toBeGreaterThan(0);
console.log(' Test server accepted recipient (common in test environments)');
}
});
tap.test('CERR-01: 4xx Errors - should handle quota exceeded (452)', async () => {
// Send multiple emails to trigger quota/limit errors
const emails = [];
for (let i = 0; i < 10; i++) {
emails.push(new Email({
from: 'test@example.com',
to: `recipient${i}@example.com`,
subject: `Quota Test ${i}`,
text: 'Testing quota limits'
}));
}
let quotaErrorCount = 0;
const results = await Promise.allSettled(
emails.map(email => smtpClient.sendMail(email))
);
results.forEach((result, index) => {
if (result.status === 'rejected') {
quotaErrorCount++;
console.log(`Email ${index} rejected:`, result.reason);
}
});
console.log(`✅ Handled ${quotaErrorCount} quota-related errors`);
});
tap.test('CERR-01: 4xx Errors - should handle too many recipients (452)', async () => {
// Create email with many recipients to exceed limit
const recipients = [];
for (let i = 0; i < 10; i++) {
recipients.push(`recipient${i}@example.com`);
}
const email = new Email({
from: 'test@example.com',
to: recipients, // Many recipients
subject: 'Too Many Recipients Test',
text: 'Testing recipient limit'
});
const result = await smtpClient.sendMail(email);
// Check if some recipients were rejected due to limits
if (result.rejectedRecipients.length > 0) {
console.log(`✅ Rejected ${result.rejectedRecipients.length} recipients due to limits`);
expect(result.rejectedRecipients).toBeArray();
} else {
// Server might accept all
expect(result.acceptedRecipients.length).toEqual(recipients.length);
console.log(' Server accepted all recipients');
}
});
tap.test('CERR-01: 4xx Errors - should handle authentication required (450)', async () => {
// Create new server requiring auth
const authServer = await startTestServer({
port: 2551,
authRequired: true // This will reject unauthenticated commands
});
const unauthClient = createSmtpClient({
host: authServer.hostname,
port: authServer.port,
secure: false,
// No auth credentials provided
connectionTimeout: 5000
});
const email = new Email({
from: 'test@example.com',
to: 'recipient@example.com',
subject: 'Auth Required Test',
text: 'Should fail without auth'
});
let authError = false;
try {
await unauthClient.sendMail(email);
} catch (error) {
authError = true;
expect(error).toBeInstanceOf(Error);
console.log('✅ Authentication required error caught');
}
expect(authError).toBeTrue();
await unauthClient.close();
await stopTestServer(authServer);
});
tap.test('CERR-01: 4xx Errors - should parse enhanced status codes', async () => {
// 4xx errors often include enhanced status codes (e.g., 4.7.1)
const email = new Email({
from: 'test@blocked-domain.com', // Might trigger policy rejection
to: 'recipient@example.com',
subject: 'Enhanced Status Code Test',
text: 'Testing enhanced status codes'
});
try {
const result = await smtpClient.sendMail(email);
if (!result.success && result.error) {
console.log('✅ Error details:', {
message: result.error.message,
response: result.response
});
}
} catch (error: any) {
// Check if error includes status information
expect(error.message).toBeTypeofString();
console.log('✅ Error with potential enhanced status:', error.message);
}
});
tap.test('CERR-01: 4xx Errors - should not retry permanent 4xx errors', async () => {
// Track retry attempts
let attemptCount = 0;
const trackingClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
connectionTimeout: 5000,
debug: true
});
// Monitor connection attempts
trackingClient.on('connect', () => attemptCount++);
const email = new Email({
from: 'invalid sender format', // Clearly invalid
to: 'recipient@example.com',
subject: 'Permanent Error Test',
text: 'Should not retry'
});
try {
await trackingClient.sendMail(email);
} catch (error) {
console.log('✅ Permanent error not retried');
}
// Should not have retried
expect(attemptCount).toBeLessThanOrEqual(1);
await trackingClient.close();
});
tap.test('cleanup - close SMTP client', async () => {
if (smtpClient && smtpClient.isConnected()) {
await smtpClient.close();
}
});
tap.test('cleanup - stop SMTP server', async () => {
await stopTestServer(testServer);
});
tap.start();