import { describe, expect, test } from "bun:test"; import { createContext } from "@wrnexus/core"; import { v } from "@wrnexus/validation"; import { captchaPlugin, defineCaptchaProvider, parseWithCaptcha, type CaptchaProvider, } from "../src/index.ts"; import type { PluginContext, TransformContext } from "@wrnexus/plugin"; describe("CAPTCHA validation and development audit", () => { test("combines schema and action-bound provider verification failures", async () => { const requests: unknown[] = []; const provider: CaptchaProvider = defineCaptchaProvider({ name: "fixture", client: { responseField: "captcha-response" }, async verify(input) { requests.push(input); return { success: false, provider: "fixture", action: input.action, message: "Try again" }; }, }); const ctx = createContext( new Request("https://app.test/signup"), new URL("https://app.test/signup"), ); const result = await parseWithCaptcha( v.object({ email: v.string().email() }), { email: "invalid", "captcha-response": "token" }, ctx, { action: "signup", provider, bindIp: true }, ); expect(result.ok).toBe(false); expect(result.errors.email).toBeDefined(); expect(result.errors["captcha-response"]).toBe("Try again"); expect(requests[0]).toEqual( expect.objectContaining({ action: "signup", hostname: "app.test", providerToken: "token" }), ); }); test("rejects incomplete custom providers", () => { expect(() => defineCaptchaProvider({ name: "", client: { responseField: "x" } } as never), ).toThrow("stable name"); expect(() => defineCaptchaProvider({ name: "x", client: { responseField: "" } } as never), ).toThrow("responseField"); expect(() => defineCaptchaProvider({ name: "x", client: { responseField: "token" } } as never), ).toThrow("verify()"); }); test("flags exposed secrets, missing provider keys, and inaccessible hard challenges", async () => { const metadata = new Map(); const plugin = captchaPlugin(); const context = { mode: "development", file: "signup.wrn", metadata, } as TransformContext; await plugin.transformCode?.( ``, context, ); const panels = await plugin.devToolbarPanels?.({ metadata } as PluginContext); const ids = panels?.[0]?.issues?.map((issue) => String((issue as { id: string }).id)) ?? []; expect(ids.some((id) => id.startsWith("client-secret:"))).toBe(true); expect(ids.some((id) => id.startsWith("missing-site-key:"))).toBe(true); expect(ids.some((id) => id.startsWith("hard-without-audio:"))).toBe(true); }); });