import { isAllowedWebhookUrl } from './orchestrator.js' export interface WebhookTestResult { reachable: boolean validResponse: boolean latencyMs: number error?: string } export async function testWebhook(webhookUrl: string): Promise { if (!isAllowedWebhookUrl(webhookUrl)) { return { reachable: false, validResponse: false, latencyMs: 0, error: 'URL blocked: private/internal addresses are not allowed.' } } const testPayload = JSON.stringify({ fight_id: 'test_000000', round: 0, type: 'webhook_test', challenge: 'WEBHOOK TEST: respond with {"answer": "pong"} to verify your setup.', constraints: { timeout_ms: 5000, max_tokens: 500 }, opponent: { name: 'test_bot', wins: 0, losses: 0 }, arena: 'localhost', arena_modifier: null, }) const start = Date.now() try { const controller = new AbortController() const timeout = setTimeout(() => controller.abort(), 5000) const res = await fetch(webhookUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: testPayload, signal: controller.signal, }) clearTimeout(timeout) const latencyMs = Date.now() - start if (!res.ok) { return { reachable: true, validResponse: false, latencyMs, error: `Webhook returned HTTP ${res.status}. Expected 200.` } } const text = await res.text() if (text.length > 10240) { return { reachable: true, validResponse: false, latencyMs, error: 'Response too large (>10KB).' } } let data: Record try { data = JSON.parse(text) } catch { return { reachable: true, validResponse: false, latencyMs, error: 'Response is not valid JSON. Expected {"answer": "..."}.' } } if (typeof data.answer !== 'string') { return { reachable: true, validResponse: false, latencyMs, error: 'Response JSON missing "answer" field. Expected {"answer": "pong"}.' } } return { reachable: true, validResponse: true, latencyMs } } catch (err: unknown) { const latencyMs = Date.now() - start const isAbort = err instanceof Error && err.name === 'AbortError' if (isAbort) { return { reachable: false, validResponse: false, latencyMs, error: 'Webhook timed out (5s). Is your server running?' } } const msg = err instanceof Error ? err.message : String(err) return { reachable: false, validResponse: false, latencyMs, error: `Connection failed: ${msg}` } } }