Testing & CI/CD
Run automated tests and CI/CD pipelines against the A3 API without burning sandbox quota. The @a3api/mock-server package provides deterministic, offline testing.
The Problem
The Playground is great for manual testing, but CI/CD pipelines and automated test suites need something else:
- No browser — the Playground is interactive;
jestandpytestare not. - Quota burn — every real API call costs one of your 100 Sandbox checks/month.
- Network dependency — flaky tests when the network is down or slow.
@a3api/mock-server
A zero-dependency mock server that replicates both API endpoints with deterministic behavior. Same request validation as the real API, predictable verdicts, no API key required, no network needed.
POST /v1/assurance/assess-age— deterministic scoring with fixed thresholdsPOST /v1/assurance/verify-token— decodes and returns the token payload (mock tokens use.mock-signature)
npm install --save-dev @a3api/mock-server
Programmatic (test suites)
Start the server in your test setup and point your HTTP client at it:
import { createMockServer } from '@a3api/mock-server';
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
let server;
let baseUrl;
beforeAll(async () => {
server = createMockServer({ port: 0 }); // random available port
const { url } = await server.start();
baseUrl = url;
});
afterAll(async () => {
await server.stop();
});
it('returns CONSISTENT for adult signals', async () => {
const res = await fetch(`${baseUrl}/v1/assurance/assess-age`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
os_signal: '18-plus',
user_country_code: 'US',
behavioral_metrics: {
avg_touch_precision: 0.88,
scroll_velocity: 600,
form_completion_time_ms: 12000,
},
}),
});
expect(res.status).toBe(201);
const body = await res.json();
expect(body.verdict).toBe('CONSISTENT');
expect(body.assessed_age_bracket).toBe('18-plus');
});CLI (CI pipelines)
Start the mock as a background process in your pipeline:
npx a3-mock-server --port 9967 &
# Run your test suite against http://127.0.0.1:9967
npm test
score() — skip the server entirely
For pure unit tests, import the scoring function directly:
import { score } from '@a3api/mock-server';
const result = score({
os_signal: '18-plus',
user_country_code: 'US',
behavioral_metrics: {
avg_touch_precision: 0.25,
scroll_velocity: 4000,
form_completion_time_ms: 1500,
},
});
console.log(result.verdict); // 'OVERRIDE'
console.log(result.signal_overridden); // true
Deterministic Scoring
The mock uses a simplified scoring engine with fixed thresholds. The same input always produces the same verdict:
| Minor Evidence Score | Verdict |
|---|---|
| ≥ 0.65 | OVERRIDE — clear & convincing minor evidence |
| ≥ 0.40 | REVIEW — partial contradiction |
| < 0.40 | CONSISTENT — evidence agrees with OS signal |
os_signal: "not-available" | PROVISIONAL — regardless of score |
The scoring matches the Playground presets: sending the
CONSISTENT preset produces CONSISTENT, the OVERRIDE preset produces
OVERRIDE, and so on.
Identifying Mock Responses
Every response includes two markers so your code can distinguish mock from production:
X-A3-Mock: trueheader — present on every HTTP responseverification_tokenends with.mock-signature— the token payload includes"mock": truein its decoded JSON
What the Mock Validates
The mock enforces the same request shape as the real API:
- Required fields (
os_signal,user_country_code) - Enum values (os_signal, consent_status, ip_type, referrer_category, etc.)
- Numeric ranges (0–1 for precision/confidence, 0–150 for age bounds)
- Conditional requirements (
consent_sourcerequired whenparental_consent_statusis present) - Nested object structure (behavioral_metrics, device_context, etc.)
If your request passes the mock's validation, it will pass the real API's validation. This catches integration bugs before they reach production.
What the Mock Does Not Do
- No API key validation — any key (or no key) is accepted
- No rate limiting — send as many requests as you want
- No
evidence_tags/confidence_scoreplan gating — all fields are always returned, regardless of what plan you would be on - Simplified scoring — the mock's scoring is deterministic and predictable, not an exact replica of the production fusion engine