ShieldSignup
Guides

Testing

Use sandbox keys and prefix-based fixtures to test your ShieldSignup integration end-to-end.

ShieldSignup ships a sandbox mode driven by your API key prefix and the local-part of the email you send. Sandbox calls never deduct from your monthly quota and never call upstream signal sources, so you can run your test suite as often as you like.

Use a sandbox key

Create a key prefixed sk_test_ from Dashboard → API Keys. With a sandbox key, the API short-circuits to a deterministic fixture:

  • It does not call the disposable-domain blocklist.
  • It does not call IP intelligence sources.
  • It does not record the assessment against your monthly quota.
  • It still validates input (email is required; ip is optional).
  • It still returns the same response shape as a live request — fields, types, and rate-limit headers are identical.

Fixture matrix

The fixture matches on the local-part prefix of the email you send, plus a special case for the mailinator.com domain. When a usable public IP is included, challenge@* returns velocity_ip; without IP it returns velocity_domain instead.

Email patternVerdictScoreNotes
block@*block92Returns one email_disposable reason. Works with or without ip.
*@mailinator.comblock92Same fixture as block@*.
challenge@* (with usable IP)challenge45Returns velocity_ip.
challenge@* (no / ignored IP)challenge45Returns velocity_domain.
allow@*allow5Returns an empty reasons array.
anything elseallow5Returns an empty reasons array.

The domain after @ is yours to choose for the prefix patterns — block@example.com and block@your-team.test produce identical responses.

curl https://api.shieldsignup.com/v1/assess \
  -H "Authorization: Bearer sk_test_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"challenge@example.com","ip":"203.0.113.42"}'

What to test

A complete integration test covers each branch of your signup flow at least once. Recommended checklist:

  • allow response → user is created and you persist request_id.
  • challenge response → your verification flow (email or CAPTCHA) is triggered and you persist request_id on the pending record.
  • block response → user sees the generic error, no row is written.
  • 400 invalid_email → your client-side validation is engaged before hitting the API (saves a round-trip).
  • 400 invalid_ip → only for malformed strings (not for 127.0.0.1 — that returns 200 with ip_status: ignored_loopback).
  • Email-only request (no ip) → ip_provided: false, no signals.ip.
  • Usable public IP → ip_provided: true, signals.ip present.
  • 401 unauthorized → the error is caught and surfaces to alerting, not swallowed silently.
  • 429 (either quota_exceeded or rate_limit) → your retry/backoff logic kicks in.
  • Request timeout / network error → your fail-open or fail-closed policy executes correctly.

Simulating downtime

There is no built-in 503 fixture today. To test your fail-open or fail-closed path, point the API base URL at a mock server that returns the error you want:

const baseUrl =
  process.env.SHIELDSIGNUP_BASE_URL ??
  "https://api.shieldsignup.com/v1";

const res = await fetch(`${baseUrl}/assess`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.SHIELDSIGNUP_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ email, ip }),
  signal: AbortSignal.timeout(3000),
});

In tests, set SHIELDSIGNUP_BASE_URL to a local mock that responds with 500, hangs for longer than the timeout, or returns malformed JSON. Then assert your fallback path produces the right effect.

What sandbox does not mock

  • Quota and rate-limit headers are still set, but they are based on the sandbox-key's own usage, not your live key's.
  • The dashboard assessments log shows sandbox calls separately. Use the filter on API Key to focus on production traffic.

On this page