File size: 5,714 Bytes
0bfe2e3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/**
 * 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<string, Response>();

// 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<Response> => {
    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);
});