/** * This is a simple test script that can be run locally using wrangler dev. * It simulates the behavior of the load balancer by mocking backend responses. */ import { Env } from './index'; // Mock fetch responses for each backend const mockResponses = new Map(); // Helper to register a mock response for a backend function mockBackendResponse(backend: string, status: number, body: string): void { mockResponses.set(backend, new Response(body, { status })); } // Overrides the global fetch for testing // @ts-ignore globalThis.fetch = async (request: Request): Promise => { const url = new URL(request.url); const hostname = url.hostname; if (mockResponses.has(hostname)) { return mockResponses.get(hostname)!; } return new Response(`Unmocked backend: ${hostname}`, { status: 404 }); }; // Mock the location object for development detection // @ts-ignore globalThis.location = { hostname: 'localhost:8787', protocol: 'http:' }; // Test scenarios async function runTests() { // Set up mock environment const env: Env = { PRIMARY_DOMAIN: 'aiostreams.example.com', BACKEND_CF: 'aiostreams-cf.example.com', BACKEND_KOYEB: 'aiostreams-koyeb.example.com', BACKEND_DUCK: 'aiostreams.example.duckdns.org', STICKY_SESSIONS: true, SESSION_COOKIE_NAME: 'aiostreams_backend', SESSION_COOKIE_TTL: '86400', BACKEND_DOWN_TIME: '30000', MAX_RETRIES: '3' }; console.log('=== AIOStreams Load Balancer Tests ==='); // Test 1: All backends healthy console.log('\nTest 1: All backends healthy'); mockBackendResponse(env.BACKEND_CF, 200, 'Response from CF'); mockBackendResponse(env.BACKEND_KOYEB, 200, 'Response from Koyeb'); mockBackendResponse(env.BACKEND_DUCK, 200, 'Response from DuckDNS'); // Simulate a request const request1 = new Request(`https://${env.PRIMARY_DOMAIN}/test`); // @ts-ignore - Import the actual handler from index.ts const response1 = await require('./index').default.fetch(request1, env, {}); console.log(`Status: ${response1.status}`); console.log(`Body: ${await response1.text()}`); console.log(`Cookie: ${response1.headers.get('Set-Cookie')}`); // Test 2: One backend down console.log('\nTest 2: One backend down (Cloudflare)'); mockBackendResponse(env.BACKEND_CF, 503, 'Service Unavailable'); const request2 = new Request(`https://${env.PRIMARY_DOMAIN}/test`); // @ts-ignore - Import the actual handler from index.ts const response2 = await require('./index').default.fetch(request2, env, {}); console.log(`Status: ${response2.status}`); console.log(`Body: ${await response2.text()}`); // Test 3: Sticky session console.log('\nTest 3: Sticky session (should use Koyeb)'); const request3 = new Request(`https://${env.PRIMARY_DOMAIN}/test`, { headers: { 'Cookie': `${env.SESSION_COOKIE_NAME}=${env.BACKEND_KOYEB}` } }); // @ts-ignore - Import the actual handler from index.ts const response3 = await require('./index').default.fetch(request3, env, {}); console.log(`Status: ${response3.status}`); console.log(`Body: ${await response3.text()}`); // Test 4: All backends down console.log('\nTest 4: All backends down'); mockBackendResponse(env.BACKEND_CF, 503, 'Service Unavailable'); mockBackendResponse(env.BACKEND_KOYEB, 502, 'Bad Gateway'); mockBackendResponse(env.BACKEND_DUCK, 500, 'Internal Server Error'); const request4 = new Request(`https://${env.PRIMARY_DOMAIN}/test`); // @ts-ignore - Import the actual handler from index.ts const response4 = await require('./index').default.fetch(request4, env, {}); console.log(`Status: ${response4.status}`); console.log(`Body: ${await response4.text()}`); // Test 5: HTTP to HTTPS redirect console.log('\nTest 5: HTTP to HTTPS redirect'); const request5 = new Request(`http://${env.PRIMARY_DOMAIN}/test`); // @ts-ignore - Import the actual handler from index.ts const response5 = await require('./index').default.fetch(request5, env, {}); console.log(`Status: ${response5.status}`); console.log(`Location: ${response5.headers.get('Location')}`); // Test 6: WebSocket handling console.log('\nTest 6: WebSocket handling'); mockBackendResponse(env.BACKEND_CF, 101, ''); // WebSocket upgrade response const webSocketHeaders = new Headers(); webSocketHeaders.set('Upgrade', 'websocket'); webSocketHeaders.set('Connection', 'Upgrade'); const request6 = new Request(`https://${env.PRIMARY_DOMAIN}/ws`, { headers: webSocketHeaders }); // @ts-ignore - Import the actual handler from index.ts const response6 = await require('./index').default.fetch(request6, env, {}); console.log(`Status: ${response6.status}`); console.log(`Upgrade header: ${response6.headers.get('Upgrade')}`); console.log(`Connection header: ${response6.headers.get('Connection')}`); // Test 7: Wrong hostname (should be ignored in dev environment) console.log('\nTest 7: Wrong hostname (should be handled in dev)'); const request7 = new Request('https://wrong-hostname.example.com/test'); // @ts-ignore - Import the actual handler from index.ts const response7 = await require('./index').default.fetch(request7, env, {}); console.log(`Status: ${response7.status}`); console.log(`Body: ${await response7.text().then((body: string) => body.substring(0, 50) + '...')}`); console.log('\n=== Tests Complete ==='); } // Run the tests runTests().catch(error => { console.error('Test error:', error); });