Files
botfights/server/src/engine/webhook-test.ts
T

73 lines
2.4 KiB
TypeScript
Raw Normal View History

import { isAllowedWebhookUrl } from './orchestrator.js'
export interface WebhookTestResult {
reachable: boolean
validResponse: boolean
latencyMs: number
error?: string
}
export async function testWebhook(webhookUrl: string): Promise<WebhookTestResult> {
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<string, unknown>
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}` }
}
}