import { expect, tap } from '@push.rocks/tapbundle'; import * as smartproxy from '../ts/index.js'; import { testCertificates } from './helpers/certificates.js'; import * as https from 'https'; import * as http from 'http'; import { WebSocket, WebSocketServer } from 'ws'; let testProxy: smartproxy.NetworkProxy; let testServer: http.Server; let wsServer: WebSocketServer; // Helper function to make HTTPS requests async function makeHttpsRequest( options: https.RequestOptions, ): Promise<{ statusCode: number; headers: http.IncomingHttpHeaders; body: string }> { return new Promise((resolve, reject) => { const req = https.request(options, (res) => { let data = ''; res.on('data', (chunk) => (data += chunk)); res.on('end', () => resolve({ statusCode: res.statusCode!, headers: res.headers, body: data, }), ); }); req.on('error', reject); req.end(); }); } // Setup test environment tap.test('setup test environment', async () => { // Create a test HTTP server testServer = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Hello from test server!'); }); // Create a WebSocket server wsServer = new WebSocketServer({ noServer: true }); wsServer.on('connection', (ws) => { ws.on('message', (message) => { ws.send('Echo: ' + message); }); }); // Handle upgrade requests testServer.on('upgrade', (request, socket, head) => { wsServer.handleUpgrade(request, socket, head, (ws) => { wsServer.emit('connection', ws, request); }); }); await new Promise((resolve) => testServer.listen(3000, resolve)); }); tap.test('should create proxy instance', async () => { testProxy = new smartproxy.NetworkProxy({ port: 3001, }); expect(testProxy).toEqual(testProxy); // Instance equality check }); tap.test('should start the proxy server', async () => { await testProxy.start(); // Configure proxy with test certificates testProxy.updateProxyConfigs([ { destinationIp: '127.0.0.1', destinationPort: '3000', hostName: 'push.rocks', publicKey: testCertificates.publicKey, privateKey: testCertificates.privateKey, }, ]); }); tap.test('should route HTTPS requests based on host header', async () => { const response = await makeHttpsRequest({ hostname: 'push.rocks', port: 3001, path: '/', method: 'GET', rejectUnauthorized: false, }); expect(response.statusCode).toEqual(200); expect(response.body).toEqual('Hello from test server!'); }); tap.test('should handle unknown host headers', async () => { const response = await makeHttpsRequest({ hostname: 'unknown.host', port: 3001, path: '/', method: 'GET', rejectUnauthorized: false, }).catch((e) => e); expect(response instanceof Error).toEqual(true); }); tap.test('should support WebSocket connections', async () => { return new Promise((resolve, reject) => { console.log('Starting WebSocket test...'); const ws = new WebSocket('wss://localhost:3001', { rejectUnauthorized: false, headers: { 'Host': 'push.rocks' } }); const timeout = setTimeout(() => { ws.close(); reject(new Error('WebSocket test timed out after 5 seconds')); }, 5000); ws.on('open', () => { console.log('WebSocket connection opened'); ws.send('Hello WebSocket'); }); ws.on('message', (data) => { console.log('Received message:', data.toString()); expect(data.toString()).toEqual('Echo: Hello WebSocket'); clearTimeout(timeout); ws.close(); resolve(); }); ws.on('error', (err) => { console.error('WebSocket error:', err); clearTimeout(timeout); reject(err); }); ws.on('close', () => { console.log('WebSocket connection closed'); }); }); }); tap.test('should handle custom headers', async () => { testProxy.addDefaultHeaders({ 'X-Proxy-Header': 'test-value', }); const response = await makeHttpsRequest({ hostname: 'push.rocks', port: 3001, path: '/', method: 'GET', rejectUnauthorized: false, }); expect(response.headers['x-proxy-header']).toEqual('test-value'); }); tap.test('cleanup', async () => { // Clean up all servers await new Promise((resolve) => wsServer.close(() => resolve())); await new Promise((resolve) => testServer.close(() => resolve())); await testProxy.stop(); }); tap.start();