Files
dotfiles/dot_claude/skills/turnstile-spin/templates/worker/test/integration.test.ts
T
Prad NukalaandClaude Fable 5 20891f3910 config(git,claude,ssh): templated gitconfig; claude settings/commands/hooks/skills; ssh config
gitconfig: name/email from chezmoi data, gh credential helper by PATH
lookup (portable to darwin). claude: no credentials, history, projects,
sessions, or plugin caches. ssh config as private_ (0600).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 14:53:46 -04:00

88 lines
3.0 KiB
TypeScript

/**
* Integration tests that hit Cloudflare's real siteverify endpoint using the
* documented test sitekeys + test secrets.
*
* Test keys reference:
* https://developers.cloudflare.com/turnstile/troubleshooting/testing/
*
* Test secrets:
* 1x0000000000000000000000000000000AA — always passes
* 2x0000000000000000000000000000000AA — always fails
* 3x0000000000000000000000000000000AA — already-spent token (timeout-or-duplicate)
*
* Test sitekeys:
* 1x00000000000000000000AA — visible, always passes
* 2x00000000000000000000AB — visible, always blocks
* 1x00000000000000000000BB — invisible, always passes
* 2x00000000000000000000BB — invisible, always blocks
* 3x00000000000000000000FF — forces an interactive challenge
*
* These tests make real network calls. Run with `npm run test:integration`.
*/
import { describe, expect, it } from 'vitest';
import worker from '../src/index.js';
import type { Env } from '../src/types.js';
const PASSING_SECRET = '1x0000000000000000000000000000000AA';
const FAILING_SECRET = '2x0000000000000000000000000000000AA';
const SPENT_TOKEN_SECRET = '3x0000000000000000000000000000000AA';
// The widget produces some opaque token string. For test secrets the actual
// content doesn't matter; siteverify decides based on the secret.
const FAKE_TOKEN = 'XXXX.DUMMY.TOKEN.XXXX';
function envWith(secret: string): Env {
return {
TURNSTILE_SECRET_KEY: secret,
ALLOWED_ORIGIN: '*',
};
}
describe('integration: real siteverify with test secrets', () => {
it('passing-secret returns success=true', async () => {
const res = await worker.fetch(
new Request('https://w/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: FAKE_TOKEN }),
}),
envWith(PASSING_SECRET),
);
expect(res.status).toBe(200);
const body = (await res.json()) as { success: boolean; _worker?: { duration_ms: number } };
expect(body.success).toBe(true);
expect(body._worker?.duration_ms).toBeGreaterThan(0);
}, 15_000);
it('failing-secret returns success=false', async () => {
const res = await worker.fetch(
new Request('https://w/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: FAKE_TOKEN }),
}),
envWith(FAILING_SECRET),
);
expect(res.status).toBe(200);
const body = (await res.json()) as { success: boolean; 'error-codes': string[] };
expect(body.success).toBe(false);
expect(body['error-codes'].length).toBeGreaterThan(0);
}, 15_000);
it('spent-token secret returns timeout-or-duplicate', async () => {
const res = await worker.fetch(
new Request('https://w/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: FAKE_TOKEN }),
}),
envWith(SPENT_TOKEN_SECRET),
);
expect(res.status).toBe(200);
const body = (await res.json()) as { success: boolean; 'error-codes': string[] };
expect(body.success).toBe(false);
expect(body['error-codes']).toContain('timeout-or-duplicate');
}, 15_000);
});