import { expect, tap } from '@git.zone/tstest/tapbundle'; import type { ISmartProxyOptions } from '../ts/proxies/smart-proxy/models/interfaces.js'; import type { IRouteConfig } from '../ts/proxies/smart-proxy/models/route-types.js'; import { RoutePreprocessor } from '../ts/proxies/smart-proxy/route-preprocessor.js'; import { buildRustProxyOptions } from '../ts/proxies/smart-proxy/utils/rust-config.js'; tap.test('Rust contract - preprocessor serializes regex headers for Rust', async () => { const route: IRouteConfig = { name: 'contract-route', match: { ports: [443, { from: 8443, to: 8444 }], domains: ['api.example.com', '*.example.com'], transport: 'udp', protocol: 'http3', headers: { 'Content-Type': /^application\/json$/i, }, }, action: { type: 'forward', targets: [{ match: { ports: [443], path: '/api/*', method: ['GET'], headers: { 'X-Env': /^(prod|stage)$/, }, }, host: ['backend-a', 'backend-b'], port: 'preserve', sendProxyProtocol: true, backendTransport: 'tcp', }], tls: { mode: 'terminate', certificate: 'auto', }, sendProxyProtocol: true, udp: { maxSessionsPerIP: 321, quic: { enableHttp3: true, }, }, }, security: { ipAllowList: [{ ip: '10.0.0.0/8', domains: ['api.example.com'], }], }, }; const preprocessor = new RoutePreprocessor(); const [rustRoute] = preprocessor.preprocessForRust([route]); expect(rustRoute.match.headers?.['Content-Type']).toEqual('/^application\\/json$/i'); expect(rustRoute.match.transport).toEqual('udp'); expect(rustRoute.match.protocol).toEqual('http3'); expect(rustRoute.action.targets?.[0].match?.headers?.['X-Env']).toEqual('/^(prod|stage)$/'); expect(rustRoute.action.targets?.[0].port).toEqual('preserve'); expect(rustRoute.action.targets?.[0].backendTransport).toEqual('tcp'); expect(rustRoute.action.sendProxyProtocol).toBeTrue(); expect(rustRoute.action.udp?.maxSessionsPerIp).toEqual(321); }); tap.test('Rust contract - preprocessor converts dynamic targets to relay-safe payloads', async () => { const route: IRouteConfig = { name: 'dynamic-contract-route', match: { ports: 8080, }, action: { type: 'forward', targets: [{ host: () => 'dynamic-backend.internal', port: () => 9443, }], }, }; const preprocessor = new RoutePreprocessor(); const [rustRoute] = preprocessor.preprocessForRust([route]); expect(rustRoute.action.type).toEqual('socket-handler'); expect(rustRoute.action.targets?.[0].host).toEqual('localhost'); expect(rustRoute.action.targets?.[0].port).toEqual(0); expect(preprocessor.getOriginalRoute('dynamic-contract-route')).toEqual(route); }); tap.test('Rust contract - top-level config keeps shared SmartProxy settings', async () => { const settings: ISmartProxyOptions = { routes: [{ name: 'top-level-contract-route', match: { ports: 443, domains: 'api.example.com', }, action: { type: 'forward', targets: [{ host: 'backend.internal', port: 8443, }], tls: { mode: 'terminate', certificate: 'auto', }, }, }], preserveSourceIP: true, proxyIPs: ['10.0.0.1'], acceptProxyProtocol: true, sendProxyProtocol: true, noDelay: true, keepAlive: true, keepAliveInitialDelay: 1500, maxPendingDataSize: 4096, disableInactivityCheck: true, enableKeepAliveProbes: true, enableDetailedLogging: true, enableTlsDebugLogging: true, enableRandomizedTimeouts: true, connectionTimeout: 5000, initialDataTimeout: 7000, socketTimeout: 9000, inactivityCheckInterval: 1100, maxConnectionLifetime: 13000, inactivityTimeout: 15000, gracefulShutdownTimeout: 17000, maxConnectionsPerIP: 20, connectionRateLimitPerMinute: 30, keepAliveTreatment: 'extended', keepAliveInactivityMultiplier: 2, extendedKeepAliveLifetime: 19000, metrics: { enabled: true, sampleIntervalMs: 250, retentionSeconds: 60, }, acme: { enabled: true, email: 'ops@example.com', environment: 'staging', useProduction: false, skipConfiguredCerts: true, renewThresholdDays: 14, renewCheckIntervalHours: 12, autoRenew: true, port: 80, }, }; const preprocessor = new RoutePreprocessor(); const routes = preprocessor.preprocessForRust(settings.routes); const config = buildRustProxyOptions(settings, routes); expect(config.preserveSourceIp).toBeTrue(); expect(config.proxyIps).toEqual(['10.0.0.1']); expect(config.acceptProxyProtocol).toBeTrue(); expect(config.sendProxyProtocol).toBeTrue(); expect(config.noDelay).toBeTrue(); expect(config.keepAlive).toBeTrue(); expect(config.keepAliveInitialDelay).toEqual(1500); expect(config.maxPendingDataSize).toEqual(4096); expect(config.disableInactivityCheck).toBeTrue(); expect(config.enableKeepAliveProbes).toBeTrue(); expect(config.enableDetailedLogging).toBeTrue(); expect(config.enableTlsDebugLogging).toBeTrue(); expect(config.enableRandomizedTimeouts).toBeTrue(); expect(config.connectionTimeout).toEqual(5000); expect(config.initialDataTimeout).toEqual(7000); expect(config.socketTimeout).toEqual(9000); expect(config.inactivityCheckInterval).toEqual(1100); expect(config.maxConnectionLifetime).toEqual(13000); expect(config.inactivityTimeout).toEqual(15000); expect(config.gracefulShutdownTimeout).toEqual(17000); expect(config.maxConnectionsPerIp).toEqual(20); expect(config.connectionRateLimitPerMinute).toEqual(30); expect(config.keepAliveTreatment).toEqual('extended'); expect(config.keepAliveInactivityMultiplier).toEqual(2); expect(config.extendedKeepAliveLifetime).toEqual(19000); expect(config.metrics?.sampleIntervalMs).toEqual(250); expect(config.acme?.email).toEqual('ops@example.com'); expect(config.acme?.environment).toEqual('staging'); expect(config.acme?.skipConfiguredCerts).toBeTrue(); expect(config.acme?.renewThresholdDays).toEqual(14); }); export default tap.start();