import { tap, expect } from '@git.zone/tstest/tapbundle'; import { DcRouter } from '../ts/classes.dcrouter.js'; import * as plugins from '../ts/plugins.js'; let dcRouter: DcRouter; tap.test('should run both DNS and email with socket-handlers simultaneously', async () => { dcRouter = new DcRouter({ dnsDomain: 'dns.integration.test', emailConfig: { ports: [25, 587, 465], hostname: 'mail.integration.test', domains: ['integration.test'], routes: [], useSocketHandler: true }, smartProxyConfig: { routes: [] } }); await dcRouter.start(); // Verify both services are running const dnsServer = (dcRouter as any).dnsServer; const emailServer = (dcRouter as any).emailServer; expect(dnsServer).toBeDefined(); expect(emailServer).toBeDefined(); // Verify SmartProxy has routes for both services const smartProxy = (dcRouter as any).smartProxy; const routes = smartProxy?.options?.routes || []; // Count DNS routes const dnsRoutes = routes.filter((route: any) => route.name?.includes('dns-over-https') ); expect(dnsRoutes.length).toEqual(2); // Count email routes const emailRoutes = routes.filter((route: any) => route.name?.includes('-route') && !route.name?.includes('dns') ); expect(emailRoutes.length).toEqual(3); // All routes should be socket-handler type [...dnsRoutes, ...emailRoutes].forEach((route: any) => { expect(route.action.type).toEqual('socket-handler'); expect(route.action.socketHandler).toBeDefined(); }); await dcRouter.stop(); }); tap.test('should handle mixed configuration (DNS socket-handler, email traditional)', async () => { dcRouter = new DcRouter({ dnsDomain: 'dns.mixed.test', emailConfig: { ports: [25, 587], hostname: 'mail.mixed.test', domains: ['mixed.test'], routes: [], useSocketHandler: false // Traditional mode }, smartProxyConfig: { routes: [] } }); await dcRouter.start(); const smartProxy = (dcRouter as any).smartProxy; const routes = smartProxy?.options?.routes || []; // DNS routes should be socket-handler const dnsRoutes = routes.filter((route: any) => route.name?.includes('dns-over-https') ); dnsRoutes.forEach((route: any) => { expect(route.action.type).toEqual('socket-handler'); }); // Email routes should be forward const emailRoutes = routes.filter((route: any) => route.name?.includes('-route') && !route.name?.includes('dns') ); emailRoutes.forEach((route: any) => { expect(route.action.type).toEqual('forward'); expect(route.action.target.port).toBeGreaterThan(10000); // Internal port }); await dcRouter.stop(); }); tap.test('should properly clean up resources on stop', async () => { dcRouter = new DcRouter({ dnsDomain: 'dns.cleanup.test', emailConfig: { ports: [25], hostname: 'mail.cleanup.test', domains: ['cleanup.test'], routes: [], useSocketHandler: true } }); await dcRouter.start(); // Services should be running expect((dcRouter as any).dnsServer).toBeDefined(); expect((dcRouter as any).emailServer).toBeDefined(); expect((dcRouter as any).smartProxy).toBeDefined(); await dcRouter.stop(); // After stop, services should still be defined but stopped // (The stop method doesn't null out the properties, just stops the services) expect((dcRouter as any).dnsServer).toBeDefined(); expect((dcRouter as any).emailServer).toBeDefined(); }); tap.test('should handle configuration updates correctly', async () => { // Start with minimal config dcRouter = new DcRouter({ smartProxyConfig: { routes: [] } }); await dcRouter.start(); // Initially no DNS or email expect((dcRouter as any).dnsServer).toBeUndefined(); expect((dcRouter as any).emailServer).toBeUndefined(); // Update to add email config await dcRouter.updateEmailConfig({ ports: [25], hostname: 'mail.update.test', domains: ['update.test'], routes: [], useSocketHandler: true }); // Now email should be running expect((dcRouter as any).emailServer).toBeDefined(); await dcRouter.stop(); }); tap.test('performance: socket-handler should not create internal listeners', async () => { dcRouter = new DcRouter({ dnsDomain: 'dns.perf.test', emailConfig: { ports: [25, 587, 465], hostname: 'mail.perf.test', domains: ['perf.test'], routes: [], useSocketHandler: true } }); await dcRouter.start(); // Get the number of listeners before creating handlers const eventCounts: { [key: string]: number } = {}; // DNS server should not have HTTPS listeners const dnsServer = (dcRouter as any).dnsServer; // The DNS server should exist but not bind to HTTPS port expect(dnsServer).toBeDefined(); // Email server should not have any server listeners const emailServer = (dcRouter as any).emailServer; expect(emailServer.servers.length).toEqual(0); await dcRouter.stop(); }); tap.test('should handle errors gracefully', async () => { dcRouter = new DcRouter({ dnsDomain: 'dns.error.test', emailConfig: { ports: [25], hostname: 'mail.error.test', domains: ['error.test'], routes: [], useSocketHandler: true } }); await dcRouter.start(); // Test DNS error handling const dnsHandler = (dcRouter as any).createDnsSocketHandler(); const errorSocket = new plugins.net.Socket(); let errorThrown = false; try { // This should handle the error gracefully await dnsHandler(errorSocket); } catch (error) { errorThrown = true; } // Should not throw, should handle gracefully expect(errorThrown).toBeFalsy(); await dcRouter.stop(); }); tap.test('should correctly identify secure connections', async () => { dcRouter = new DcRouter({ emailConfig: { ports: [465], hostname: 'mail.secure.test', domains: ['secure.test'], routes: [], useSocketHandler: true } }); await dcRouter.start(); // The email socket handler for port 465 should handle TLS const handler = (dcRouter as any).createMailSocketHandler(465); expect(handler).toBeDefined(); // Port 465 requires immediate TLS, which is handled in the socket handler // This is different from ports 25/587 which use STARTTLS await dcRouter.stop(); }); tap.test('stop', async () => { await tap.stopForcefully(); }); export default tap.start();