// tests/api/utils.ts import { writeFile, mkdir } from 'fs/promises'; import { join } from 'path'; import { config } from './config'; // Colors for console output const colors = { reset: '\x1b[0m', green: '\x1b[32m', red: '\x1b[31m', yellow: '\x1b[33m', blue: '\x1b[34m', gray: '\x1b[90m', cyan: '\x1b[36m', }; // Logging utilities export const log = { success: (msg: string) => console.log(`${colors.green}✓${colors.reset} ${msg}`), error: (msg: string) => console.log(`${colors.red}✗${colors.reset} ${msg}`), info: (msg: string) => console.log(`${colors.blue}→${colors.reset} ${msg}`), warning: (msg: string) => console.log(`${colors.yellow}⚠${colors.reset} ${msg}`), section: (msg: string) => console.log(`\n${colors.cyan}━━━ ${msg} ━━━${colors.reset}`), detail: (key: string, value: any) => { const valueStr = typeof value === 'object' ? JSON.stringify(value, null, 2) : value; console.log(` ${colors.gray}${key}:${colors.reset} ${valueStr}`); }, }; // API fetch wrapper export async function api( endpoint: string, options: RequestInit & { expectError?: boolean; timeout?: number; } = {} ): Promise<{ data: T; status: number; headers: Headers; duration: number; }> { const { expectError = false, timeout = config.requestTimeout, ...fetchOptions } = options; const url = `${config.baseURL}${endpoint}`; const startTime = Date.now(); try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); const response = await fetch(url, { ...fetchOptions, headers: { 'X-API-Key': config.apiKey, ...fetchOptions.headers, }, signal: controller.signal, }); clearTimeout(timeoutId); const duration = Date.now() - startTime; let data: any; const contentType = response.headers.get('content-type'); if (contentType?.includes('application/json')) { data = await response.json(); } else if (contentType?.includes('image/')) { data = await response.arrayBuffer(); } else { data = await response.text(); } if (!response.ok && !expectError) { throw new Error(`HTTP ${response.status}: ${JSON.stringify(data)}`); } if (config.verbose) { const method = fetchOptions.method || 'GET'; log.detail('Request', `${method} ${endpoint}`); log.detail('Status', response.status); log.detail('Duration', `${duration}ms`); } return { data, status: response.status, headers: response.headers, duration, }; } catch (error) { const duration = Date.now() - startTime; if (!expectError) { log.error(`Request failed: ${error}`); log.detail('Endpoint', endpoint); log.detail('Duration', `${duration}ms`); } throw error; } } // Save image to results directory export async function saveImage( buffer: ArrayBuffer, filename: string ): Promise { const resultsPath = join(__dirname, config.resultsDir); try { await mkdir(resultsPath, { recursive: true }); } catch (err) { // Directory exists, ignore } const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5); const fullFilename = `${timestamp}_${filename}`; const filepath = join(resultsPath, fullFilename); await writeFile(filepath, Buffer.from(buffer)); if (config.saveImages) { log.info(`Saved image: ${fullFilename}`); } return filepath; } // Upload file helper export async function uploadFile( filepath: string, fields: Record = {} ): Promise { const formData = new FormData(); // Read file const fs = await import('fs/promises'); const fileBuffer = await fs.readFile(filepath); const blob = new Blob([fileBuffer]); formData.append('file', blob, 'test-image.png'); // Add other fields for (const [key, value] of Object.entries(fields)) { formData.append(key, value); } const result = await api(config.endpoints.images + '/upload', { method: 'POST', body: formData, headers: { // Don't set Content-Type, let fetch set it with boundary }, }); return result.data; } // Wait helper export async function wait(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } // Poll for generation completion export async function waitForGeneration( generationId: string, maxAttempts = 20 ): Promise { for (let i = 0; i < maxAttempts; i++) { const result = await api(`${config.endpoints.generations}/${generationId}`); const generation = result.data.generation; if (generation.status === 'success' || generation.status === 'failed') { return generation; } await wait(1000); } throw new Error('Generation timeout'); } // Test context to share data between tests export const testContext: { imageId?: string; generationId?: string; flowId?: string; uploadedImageId?: string; } = {}; // Test runner helper export async function runTest( name: string, fn: () => Promise ): Promise { try { const startTime = Date.now(); await fn(); const duration = Date.now() - startTime; log.success(`${name} (${duration}ms)`); return true; } catch (error) { log.error(`${name}`); console.error(error); return false; } }