85 lines · 3.7 KB
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";

const env: Record<string, string | undefined> = {};
mock.module("cloudflare:workers", () => ({ env, waitUntil: () => undefined }));
mock.module("./limits", () => ({ rawRequestIp: () => "203.0.113.9" }));

const { requireTurnstile, turnstileSiteKey } = await import("./turnstile");

const realFetch = globalThis.fetch;
let calls: URLSearchParams[] = [];
let reply: { status: number; body: unknown } = { status: 200, body: {} };

beforeEach(() => {
  Object.assign(env, {
    PUBLIC_URL: "https://gitfor.dev",
    TURNSTILE_SITEKEY: "0x4AAAAAAFUg9PmDIBH5nisz",
    TURNSTILE_SECRET: "secret",
    TURNSTILE_HOSTNAMES: "",
  });
  calls = [];
  reply = { status: 200, body: { success: true, action: "signup", hostname: "gitfor.dev" } };
  globalThis.fetch = (async (_url: string, init: RequestInit) => {
    calls.push(new URLSearchParams(String(init.body)));
    return new Response(JSON.stringify(reply.body), { status: reply.status });
  }) as unknown as typeof fetch;
});

afterEach(() => {
  globalThis.fetch = realFetch;
});

describe("requireTurnstile", () => {
  test("accepts a token Siteverify vouches for, sending secret, token and client IP", async () => {
    await requireTurnstile("tok", "signup");
    expect(calls).toHaveLength(1);
    expect(calls[0].get("secret")).toBe("secret");
    expect(calls[0].get("response")).toBe("tok");
    expect(calls[0].get("remoteip")).toBe("203.0.113.9");
  });