import { tap, expect } from '@git.zone/tstest/tapbundle'; import { SmartProxy } from '../ts/index.js'; /** * Test that verifies ACME challenge routes are properly created */ tap.test('should create ACME challenge route with high ports', async (tools) => { tools.timeout(5000); const capturedRoutes: any[] = []; const settings = { routes: [ { name: 'secure-route', match: { ports: [18443], // High port to avoid permission issues domains: 'test.local' }, action: { type: 'forward' as const, target: { host: 'localhost', port: 8080 }, tls: { mode: 'terminate' as const, certificate: 'auto' } } } ], acme: { email: 'test@test.local', port: 18080 // High port for ACME challenges } }; const proxy = new SmartProxy(settings); // Capture route updates const originalUpdateRoutes = (proxy as any).updateRoutesInternal.bind(proxy); (proxy as any).updateRoutesInternal = async function(routes: any[]) { capturedRoutes.push([...routes]); return originalUpdateRoutes(routes); }; await proxy.start(); // Check that ACME challenge route was added const finalRoutes = capturedRoutes[capturedRoutes.length - 1]; const challengeRoute = finalRoutes.find((r: any) => r.name === 'acme-challenge'); expect(challengeRoute).toBeDefined(); expect(challengeRoute.match.path).toEqual('/.well-known/acme-challenge/*'); expect(challengeRoute.match.ports).toEqual(18080); expect(challengeRoute.action.type).toEqual('static'); expect(challengeRoute.priority).toEqual(1000); await proxy.stop(); }); tap.test('should handle HTTP request parsing correctly', async (tools) => { tools.timeout(5000); let handlerCalled = false; let receivedContext: any; const settings = { routes: [ { name: 'test-static', match: { ports: [18090], path: '/test/*' }, action: { type: 'static' as const, handler: async (context) => { handlerCalled = true; receivedContext = context; return { status: 200, headers: { 'Content-Type': 'text/plain' }, body: 'OK' }; } } } ] }; const proxy = new SmartProxy(settings); // Mock NFTables manager (proxy as any).nftablesManager = { ensureNFTablesSetup: async () => {}, stop: async () => {} }; await proxy.start(); // Create a simple HTTP request const client = new plugins.net.Socket(); await new Promise((resolve, reject) => { client.connect(18090, 'localhost', () => { // Send HTTP request const request = [ 'GET /test/example HTTP/1.1', 'Host: localhost:18090', 'User-Agent: test-client', '', '' ].join('\r\n'); client.write(request); // Wait for response client.on('data', (data) => { const response = data.toString(); expect(response).toContain('HTTP/1.1 200'); expect(response).toContain('OK'); client.end(); resolve(); }); }); client.on('error', reject); }); // Verify handler was called expect(handlerCalled).toBeTrue(); expect(receivedContext).toBeDefined(); expect(receivedContext.path).toEqual('/test/example'); expect(receivedContext.method).toEqual('GET'); expect(receivedContext.headers.host).toEqual('localhost:18090'); await proxy.stop(); }); tap.start();