import { describe, expect, test } from "bun:test"; import { createCaptchaHttpHandlers } from "../src/http.ts"; import type { CaptchaEngine } from "../src/types.ts"; const engine: CaptchaEngine = { provider: "self-hosted", basePath: "/api/captcha", async create(options) { return { id: "c1", provider: "self-hosted", type: "number", presentation: "visual", action: options.action, prompt: "Enter 1", createdAt: 1, expiresAt: 2, responseField: "wrn-captcha-response", }; }, async verify(input) { return { success: input.answer === "1", provider: "self-hosted", action: input.action, code: input.answer === "1" ? undefined : "incorrect-answer", }; }, async verifyResponseToken(input) { return { success: true, provider: "self-hosted", action: input.action }; }, async renderAudio() { return { bytes: new Uint8Array([1, 2, 3]), contentType: "audio/wav" }; }, async gc() {}, }; describe("CAPTCHA HTTP handlers", () => { test("creates and verifies same-origin challenges", async () => { const handlers = createCaptchaHttpHandlers(engine); const created = await handlers.handle( new Request("https://example.test/api/captcha/challenge", { method: "POST", headers: { origin: "https://example.test", "content-type": "application/json" }, body: JSON.stringify({ action: "signup" }), }), ); expect(created?.status).toBe(201); const verified = await handlers.handle( new Request("https://example.test/api/captcha/verify", { method: "POST", headers: { origin: "https://example.test", "content-type": "application/json" }, body: JSON.stringify({ action: "signup", answer: "1" }), }), ); expect(verified?.status).toBe(200); }); test("rejects cross-origin requests", async () => { const handlers = createCaptchaHttpHandlers(engine); const response = await handlers.create( new Request("https://example.test/api/captcha/challenge", { method: "POST", headers: { origin: "https://evil.test", "content-type": "application/json" }, body: "{}", }), ); expect(response.status).toBe(403); }); });