// tests/api/04-live.ts import { api, log, runTest, saveImage, wait } from './utils'; import { endpoints } from './config'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); async function main() { log.section('LIVE ENDPOINT TESTS'); // Test 1: First call (cache MISS) await runTest('Live generation - cache MISS', async () => { const params = new URLSearchParams({ prompt: 'A serene zen garden with rocks and sand', aspectRatio: '16:9', }); const startTime = Date.now(); const result = await api(`${endpoints.live}?${params}`, { timeout: 60000, // Longer timeout for generation }); const duration = Date.now() - startTime; // Should return image buffer if (!(result.data instanceof ArrayBuffer)) { throw new Error('Expected image buffer'); } // Check cache status header const cacheStatus = result.headers.get('X-Cache-Status'); if (cacheStatus !== 'MISS') { throw new Error(`Expected cache MISS, got: ${cacheStatus}`); } log.detail('Cache status', cacheStatus); log.detail('Duration', `${duration}ms`); log.detail('Content-Type', result.headers.get('Content-Type')); log.detail('Image size', `${result.data.byteLength} bytes`); await saveImage(result.data, 'live-cache-miss.png'); }); // Test 2: Second call with same params (cache HIT) await runTest('Live generation - cache HIT', async () => { const params = new URLSearchParams({ prompt: 'A serene zen garden with rocks and sand', aspectRatio: '16:9', }); const startTime = Date.now(); const result = await api(`${endpoints.live}?${params}`); const duration = Date.now() - startTime; // Check cache status header const cacheStatus = result.headers.get('X-Cache-Status'); if (cacheStatus !== 'HIT') { throw new Error(`Expected cache HIT, got: ${cacheStatus}`); } log.detail('Cache status', cacheStatus); log.detail('Duration', `${duration}ms (should be faster)`); log.detail('Image size', `${result.data.byteLength} bytes`); await saveImage(result.data, 'live-cache-hit.png'); }); // Test 3: Different aspect ratio (new cache entry) await runTest('Live generation - different params', async () => { const params = new URLSearchParams({ prompt: 'A serene zen garden with rocks and sand', aspectRatio: '1:1', // Different aspect ratio }); const result = await api(`${endpoints.live}?${params}`, { timeout: 60000, }); const cacheStatus = result.headers.get('X-Cache-Status'); if (cacheStatus !== 'MISS') { throw new Error(`Expected cache MISS for different params, got: ${cacheStatus}`); } log.detail('Cache status', cacheStatus); log.detail('Aspect ratio', '1:1'); await saveImage(result.data, 'live-different-aspect.png'); }); // Test 4: With reference image await runTest('Live generation - with reference', async () => { const params = new URLSearchParams({ prompt: 'Product photo featuring @brand-logo', aspectRatio: '16:9', reference: '@brand-logo', }); const result = await api(`${endpoints.live}?${params}`, { timeout: 60000, }); const cacheStatus = result.headers.get('X-Cache-Status'); log.detail('Cache status', cacheStatus); log.detail('With reference', '@brand-logo'); await saveImage(result.data, 'live-with-reference.png'); }); // Test 5: Multiple references await runTest('Live generation - multiple references', async () => { const params = new URLSearchParams({ prompt: 'Combine @brand-logo and @generated-logo', aspectRatio: '1:1', }); params.append('reference', '@brand-logo'); params.append('reference', '@generated-logo'); const result = await api(`${endpoints.live}?${params}`, { timeout: 60000, }); const cacheStatus = result.headers.get('X-Cache-Status'); log.detail('Cache status', cacheStatus); log.detail('References', '[@brand-logo, @generated-logo]'); await saveImage(result.data, 'live-multiple-refs.png'); }); // Test 6: Custom dimensions await runTest('Live generation - custom dimensions', async () => { const params = new URLSearchParams({ prompt: 'A landscape painting', width: '1024', height: '768', }); const result = await api(`${endpoints.live}?${params}`, { timeout: 60000, }); const cacheStatus = result.headers.get('X-Cache-Status'); log.detail('Cache status', cacheStatus); log.detail('Dimensions', '1024x768'); await saveImage(result.data, 'live-custom-dims.png'); }); // Test 7: Verify cache works as URL await runTest('Live as direct URL (browser-like)', async () => { const url = `${endpoints.live}?prompt=${encodeURIComponent('A beautiful sunset')}&aspectRatio=16:9`; log.info('Testing URL format:'); log.detail('URL', url); const result = await api(url, { timeout: 60000 }); if (!(result.data instanceof ArrayBuffer)) { throw new Error('Should return image directly'); } const cacheStatus = result.headers.get('X-Cache-Status'); log.detail('Cache status', cacheStatus); log.detail('Works as direct URL', '✓'); await saveImage(result.data, 'live-direct-url.png'); }); // Test 8: Verify Cache-Control header for CDN await runTest('Check Cache-Control headers', async () => { const params = new URLSearchParams({ prompt: 'Test cache control', aspectRatio: '1:1', }); const result = await api(`${endpoints.live}?${params}`, { timeout: 60000, }); const cacheControl = result.headers.get('Cache-Control'); const contentType = result.headers.get('Content-Type'); log.detail('Cache-Control', cacheControl || 'NOT SET'); log.detail('Content-Type', contentType || 'NOT SET'); if (!cacheControl || !cacheControl.includes('public')) { log.warning('Cache-Control should be set for CDN optimization'); } }); // Test 9: Rapid repeated calls (verify cache performance) await runTest('Cache performance test', async () => { const params = new URLSearchParams({ prompt: 'Performance test image', aspectRatio: '1:1', }); // First call (MISS) log.info('Making first call (MISS)...'); const firstCall = await api(`${endpoints.live}?${params}`, { timeout: 60000, }); const firstDuration = firstCall.duration; await wait(1000); // Rapid subsequent calls (all HITs) log.info('Making 5 rapid cache HIT calls...'); const durations: number[] = []; for (let i = 0; i < 5; i++) { const result = await api(`${endpoints.live}?${params}`); durations.push(result.duration); const cacheStatus = result.headers.get('X-Cache-Status'); if (cacheStatus !== 'HIT') { throw new Error(`Call ${i + 1} expected HIT, got ${cacheStatus}`); } } const avgHitDuration = durations.reduce((a, b) => a + b, 0) / durations.length; log.detail('First call (MISS)', `${firstDuration}ms`); log.detail('Avg HIT calls', `${avgHitDuration.toFixed(0)}ms`); log.detail('Speedup', `${(firstDuration / avgHitDuration).toFixed(1)}x faster`); }); log.section('LIVE ENDPOINT TESTS COMPLETED'); } main().catch(console.error);