dcrouter/test/test.email.router.ts
Philipp Kunz 2e75961d1c feat: implement comprehensive route-based email routing system
Replace legacy domain-rule based routing with flexible route-based system that supports:
- Multi-criteria matching (recipients, senders, IPs, authentication)
- Four action types (forward, process, deliver, reject)
- Moved DKIM signing to delivery phase for signature validity
- Connection pooling for efficient email forwarding
- Pattern caching for improved performance

This provides more granular control over email routing with priority-based matching and comprehensive test coverage.
2025-05-28 13:23:45 +00:00

283 lines
6.1 KiB
TypeScript

import { expect, tap } from '@git.zone/tstest/tapbundle';
import { EmailRouter, type IEmailRoute, type IEmailContext } from '../ts/mail/routing/index.js';
import { Email } from '../ts/mail/core/classes.email.js';
tap.test('EmailRouter - should create and manage routes', async () => {
const router = new EmailRouter([]);
// Test initial state
expect(router.getRoutes()).toEqual([]);
// Add some test routes
const routes: IEmailRoute[] = [
{
name: 'forward-example',
priority: 10,
match: {
recipients: '*@example.com'
},
action: {
type: 'forward',
forward: {
host: 'mail.example.com',
port: 25
}
}
},
{
name: 'reject-spam',
priority: 20,
match: {
senders: '*@spammer.com'
},
action: {
type: 'reject',
reject: {
code: 550,
message: 'Spam not allowed'
}
}
}
];
router.updateRoutes(routes);
expect(router.getRoutes().length).toEqual(2);
});
tap.test('EmailRouter - should evaluate routes based on priority', async () => {
const router = new EmailRouter([]);
const routes: IEmailRoute[] = [
{
name: 'low-priority',
priority: 5,
match: {
recipients: '*@test.com'
},
action: {
type: 'deliver'
}
},
{
name: 'high-priority',
priority: 10,
match: {
recipients: 'admin@test.com'
},
action: {
type: 'process',
process: {
scan: true
}
}
}
];
router.updateRoutes(routes);
// Create test context
const email = new Email({
from: 'sender@example.com',
to: 'admin@test.com',
subject: 'Test email',
text: 'Test email content'
});
const context: IEmailContext = {
email,
session: {
id: 'test-session',
remoteAddress: '192.168.1.1',
matchedRoute: null
} as any
};
const route = await router.evaluateRoutes(context);
expect(route).not.toEqual(null);
expect(route?.name).toEqual('high-priority');
});
tap.test('EmailRouter - should match recipient patterns', async () => {
const router = new EmailRouter([]);
const routes: IEmailRoute[] = [
{
name: 'exact-match',
match: {
recipients: 'admin@example.com'
},
action: {
type: 'forward',
forward: {
host: 'admin-server.com',
port: 25
}
}
},
{
name: 'wildcard-match',
match: {
recipients: '*@example.com'
},
action: {
type: 'deliver'
}
}
];
router.updateRoutes(routes);
// Test exact match
const email1 = new Email({
from: 'sender@test.com',
to: 'admin@example.com',
subject: 'Admin email',
text: 'Admin email content'
});
const context1: IEmailContext = {
email: email1,
session: { id: 'test1', remoteAddress: '10.0.0.1' } as any
};
const route1 = await router.evaluateRoutes(context1);
expect(route1?.name).toEqual('exact-match');
// Test wildcard match
const email2 = new Email({
from: 'sender@test.com',
to: 'user@example.com',
subject: 'User email',
text: 'User email content'
});
const context2: IEmailContext = {
email: email2,
session: { id: 'test2', remoteAddress: '10.0.0.2' } as any
};
const route2 = await router.evaluateRoutes(context2);
expect(route2?.name).toEqual('wildcard-match');
});
tap.test('EmailRouter - should match IP ranges with CIDR notation', async () => {
const router = new EmailRouter([]);
const routes: IEmailRoute[] = [
{
name: 'internal-network',
match: {
clientIp: '10.0.0.0/24'
},
action: {
type: 'deliver'
}
},
{
name: 'external-network',
match: {
clientIp: ['192.168.1.0/24', '172.16.0.0/16']
},
action: {
type: 'process',
process: {
scan: true
}
}
}
];
router.updateRoutes(routes);
// Test internal network match
const email = new Email({
from: 'internal@company.com',
to: 'user@company.com',
subject: 'Internal email',
text: 'Internal email content'
});
const context1: IEmailContext = {
email,
session: { id: 'test1', remoteAddress: '10.0.0.15' } as any
};
const route1 = await router.evaluateRoutes(context1);
expect(route1?.name).toEqual('internal-network');
// Test external network match
const context2: IEmailContext = {
email,
session: { id: 'test2', remoteAddress: '192.168.1.100' } as any
};
const route2 = await router.evaluateRoutes(context2);
expect(route2?.name).toEqual('external-network');
});
tap.test('EmailRouter - should handle authentication matching', async () => {
const router = new EmailRouter([]);
const routes: IEmailRoute[] = [
{
name: 'authenticated-users',
match: {
authenticated: true
},
action: {
type: 'deliver'
}
},
{
name: 'unauthenticated-users',
match: {
authenticated: false
},
action: {
type: 'reject',
reject: {
code: 550,
message: 'Authentication required'
}
}
}
];
router.updateRoutes(routes);
const email = new Email({
from: 'user@example.com',
to: 'recipient@test.com',
subject: 'Test',
text: 'Test content'
});
// Test authenticated session
const context1: IEmailContext = {
email,
session: {
id: 'test1',
remoteAddress: '10.0.0.1',
authenticated: true,
authenticatedUser: 'user@example.com'
} as any
};
const route1 = await router.evaluateRoutes(context1);
expect(route1?.name).toEqual('authenticated-users');
// Test unauthenticated session
const context2: IEmailContext = {
email,
session: {
id: 'test2',
remoteAddress: '10.0.0.2',
authenticated: false
} as any
};
const route2 = await router.evaluateRoutes(context2);
expect(route2?.name).toEqual('unauthenticated-users');
});
export default tap.start();