230 lines
6.9 KiB
TypeScript
230 lines
6.9 KiB
TypeScript
|
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||
|
import * as plugins from '../ts/plugins.js';
|
||
|
import * as path from 'path';
|
||
|
import * as fs from 'fs';
|
||
|
import {
|
||
|
DcRouter,
|
||
|
type IDcRouterOptions,
|
||
|
type IEmailConfig,
|
||
|
type EmailProcessingMode,
|
||
|
type IDomainRule
|
||
|
} from '../ts/classes.dcrouter.js';
|
||
|
|
||
|
// Mock platform service for testing
|
||
|
const mockPlatformService = {
|
||
|
mtaService: {
|
||
|
saveToLocalMailbox: async (email: any) => {
|
||
|
// Mock implementation
|
||
|
console.log('Mock: Saving email to local mailbox');
|
||
|
return Promise.resolve();
|
||
|
},
|
||
|
isBounceNotification: (email: any) => false,
|
||
|
processBounceNotification: async (email: any) => {
|
||
|
// Mock implementation
|
||
|
return Promise.resolve();
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
tap.test('DcRouter class - Custom email port configuration', async () => {
|
||
|
// Define custom port mapping
|
||
|
const customPortMapping = {
|
||
|
25: 11025, // Custom SMTP port mapping
|
||
|
587: 11587, // Custom submission port mapping
|
||
|
465: 11465, // Custom SMTPS port mapping
|
||
|
2525: 12525 // Additional custom port
|
||
|
};
|
||
|
|
||
|
// Create a custom email configuration
|
||
|
const emailConfig: IEmailConfig = {
|
||
|
ports: [25, 587, 465, 2525], // Added a non-standard port
|
||
|
hostname: 'mail.example.com',
|
||
|
maxMessageSize: 50 * 1024 * 1024, // 50MB
|
||
|
|
||
|
defaultMode: 'forward' as EmailProcessingMode,
|
||
|
defaultServer: 'fallback-mail.example.com',
|
||
|
defaultPort: 25,
|
||
|
defaultTls: true,
|
||
|
|
||
|
domainRules: [
|
||
|
{
|
||
|
pattern: '*@example.com',
|
||
|
mode: 'forward' as EmailProcessingMode,
|
||
|
target: {
|
||
|
server: 'mail1.example.com',
|
||
|
port: 25,
|
||
|
useTls: true
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
pattern: '*@example.org',
|
||
|
mode: 'mta' as EmailProcessingMode,
|
||
|
mtaOptions: {
|
||
|
domain: 'example.org',
|
||
|
allowLocalDelivery: true
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
};
|
||
|
|
||
|
// Create custom email storage path
|
||
|
const customEmailsPath = path.join(process.cwd(), 'email');
|
||
|
|
||
|
// Ensure directory exists and is empty
|
||
|
if (fs.existsSync(customEmailsPath)) {
|
||
|
try {
|
||
|
fs.rmdirSync(customEmailsPath, { recursive: true });
|
||
|
} catch (e) {
|
||
|
console.warn('Could not remove test directory:', e);
|
||
|
}
|
||
|
}
|
||
|
fs.mkdirSync(customEmailsPath, { recursive: true });
|
||
|
|
||
|
// Create DcRouter options with custom email port configuration
|
||
|
const options: IDcRouterOptions = {
|
||
|
emailConfig,
|
||
|
emailPortConfig: {
|
||
|
portMapping: customPortMapping,
|
||
|
portSettings: {
|
||
|
2525: {
|
||
|
terminateTls: false,
|
||
|
routeName: 'custom-smtp-route'
|
||
|
}
|
||
|
},
|
||
|
receivedEmailsPath: customEmailsPath
|
||
|
},
|
||
|
tls: {
|
||
|
contactEmail: 'test@example.com'
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Create DcRouter instance with mock platform service
|
||
|
const router = new DcRouter(options, mockPlatformService);
|
||
|
|
||
|
// Verify the options are correctly set
|
||
|
expect(router.options.emailPortConfig).toBeTruthy();
|
||
|
expect(router.options.emailPortConfig.portMapping).toEqual(customPortMapping);
|
||
|
expect(router.options.emailPortConfig.receivedEmailsPath).toEqual(customEmailsPath);
|
||
|
|
||
|
// Test the generateEmailRoutes method
|
||
|
if (typeof router['generateEmailRoutes'] === 'function') {
|
||
|
const routes = router['generateEmailRoutes'](emailConfig);
|
||
|
|
||
|
// Verify that all ports are configured
|
||
|
expect(routes.length).toBeGreaterThan(0); // At least some routes are configured
|
||
|
|
||
|
// Check the custom port configuration
|
||
|
const customPortRoute = routes.find(r => r.match.ports?.includes(2525));
|
||
|
expect(customPortRoute).toBeTruthy();
|
||
|
expect(customPortRoute?.name).toEqual('custom-smtp-route');
|
||
|
expect(customPortRoute?.action.target.port).toEqual(12525);
|
||
|
|
||
|
// Check standard port mappings
|
||
|
const smtpRoute = routes.find(r => r.match.ports?.includes(25));
|
||
|
expect(smtpRoute?.action.target.port).toEqual(11025);
|
||
|
|
||
|
const submissionRoute = routes.find(r => r.match.ports?.includes(587));
|
||
|
expect(submissionRoute?.action.target.port).toEqual(11587);
|
||
|
}
|
||
|
|
||
|
// Clean up
|
||
|
try {
|
||
|
fs.rmdirSync(customEmailsPath, { recursive: true });
|
||
|
} catch (e) {
|
||
|
console.warn('Could not remove test directory in cleanup:', e);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
tap.test('DcRouter class - Custom email storage path', async () => {
|
||
|
// Create custom email storage path
|
||
|
const customEmailsPath = path.join(process.cwd(), 'email');
|
||
|
|
||
|
// Ensure directory exists and is empty
|
||
|
if (fs.existsSync(customEmailsPath)) {
|
||
|
try {
|
||
|
fs.rmdirSync(customEmailsPath, { recursive: true });
|
||
|
} catch (e) {
|
||
|
console.warn('Could not remove test directory:', e);
|
||
|
}
|
||
|
}
|
||
|
fs.mkdirSync(customEmailsPath, { recursive: true });
|
||
|
|
||
|
// Create a basic email configuration
|
||
|
const emailConfig: IEmailConfig = {
|
||
|
ports: [25],
|
||
|
hostname: 'mail.example.com',
|
||
|
defaultMode: 'mta' as EmailProcessingMode,
|
||
|
domainRules: []
|
||
|
};
|
||
|
|
||
|
// Create DcRouter options with custom email storage path
|
||
|
const options: IDcRouterOptions = {
|
||
|
emailConfig,
|
||
|
emailPortConfig: {
|
||
|
receivedEmailsPath: customEmailsPath
|
||
|
},
|
||
|
tls: {
|
||
|
contactEmail: 'test@example.com'
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Create a mock MTA service with a trackable saveToLocalMailbox method
|
||
|
let savedToCustomPath = false;
|
||
|
const mockEmail = {
|
||
|
to: ['test@example.com'],
|
||
|
toRFC822String: () => 'From: sender@example.com\nTo: test@example.com\nSubject: Test\n\nTest email'
|
||
|
};
|
||
|
|
||
|
const mockMtaService = {
|
||
|
saveToLocalMailbox: async (email: any) => {
|
||
|
console.log('Original saveToLocalMailbox called');
|
||
|
return Promise.resolve();
|
||
|
},
|
||
|
isBounceNotification: (email: any) => false,
|
||
|
processBounceNotification: async (email: any) => {
|
||
|
return Promise.resolve();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const mockPlatformServiceWithMta = {
|
||
|
mtaService: mockMtaService
|
||
|
};
|
||
|
|
||
|
// Create DcRouter instance with mock platform service
|
||
|
const router = new DcRouter(options, mockPlatformServiceWithMta);
|
||
|
|
||
|
// Import the patch function and apply it directly for testing
|
||
|
const { configureEmailStorage } = await import('../ts/mail/delivery/classes.mta.patch.js');
|
||
|
configureEmailStorage(mockMtaService, { receivedEmailsPath: customEmailsPath });
|
||
|
|
||
|
// Call the patched method
|
||
|
await mockMtaService.saveToLocalMailbox(mockEmail);
|
||
|
|
||
|
// Check if a file was created in the custom path
|
||
|
const files = fs.readdirSync(customEmailsPath);
|
||
|
expect(files.length).toBeGreaterThan(0);
|
||
|
expect(files[0].endsWith('.eml')).toEqual(true);
|
||
|
|
||
|
// Verify file contents
|
||
|
const fileContent = fs.readFileSync(path.join(customEmailsPath, files[0]), 'utf8');
|
||
|
expect(fileContent).toContain('From: sender@example.com');
|
||
|
|
||
|
// Clean up
|
||
|
try {
|
||
|
fs.rmdirSync(customEmailsPath, { recursive: true });
|
||
|
} catch (e) {
|
||
|
console.warn('Could not remove test directory in cleanup:', e);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Final clean-up test
|
||
|
tap.test('clean up after tests', async () => {
|
||
|
// No-op - just to make sure everything is cleaned up properly
|
||
|
});
|
||
|
|
||
|
tap.test('stop', async () => {
|
||
|
await tap.stopForcefully();
|
||
|
});
|
||
|
|
||
|
// Export a function to run all tests
|
||
|
export default tap.start();
|