Test-User Factory
At a glance
A disposable-user factory that spawns realistic test identities inside a dedicated `tenant=sandbox-sys`, lets sys operators validate end-to-end behaviour without polluting production data, and routes any sandbox email to an internal catch-all inbox so no real recipient ever receives a test message.
How it works
The factory exists for one reason: an operator should never QA a feature against a real user's data. Every spawned account lives in the isolated `sandbox-sys` tenant, which is documented and enumerated in `docs/tenants/sandbox-sys.md`. Operators open `sys/(dashboard)/sandbox`, pick a preset (`new player`, `club admin with pending approvals`, `federation admin with open incidents`, `retired player`, `suspended license`), or compose a bespoke configuration: role, language, nationality, license state (none / active / expired), club affiliation, 2FA state.
The endpoint provisions the user with the same hooks and validations as a real signup — there is no Cosmos shortcut — so tests reflect production behaviour. Sandbox screens are visually unmistakable: an amber header bar saying `SANDBOX — not real data` is rendered in admin and app whenever the tenant resolves to sandbox-sys, eliminating any chance of confusing it with a real federation. Outbound mail is intercepted at the transport layer: SendGrid sees a sandbox-tenant flag and reroutes everything to an internal catch-all inbox visible at `sys/(dashboard)/sandbox/mail-inbox`, so verification emails, OTP codes, password resets, and notifications can be inspected without ever hitting a real address.
When sandbox state needs to be wiped, `Nuke sandbox` cascades a delete across users, sessions, audit rows, and uploaded artefacts. The endpoint is throttled to once per hour and audit-logged, preventing accidental loops and giving the trail a clear `actor_id` for every wipe. The sibling `test-session` flow in F21.02 reuses the sandbox tenant when an operator wants to verify a feature in a specific role without spawning a long-lived account.
Key capabilities
- Spawn sandbox users with role, language, nationality, license state, club, 2FA
- Five preset scenarios for one-click reproduction of common support cases
- Visible amber sandbox banner across admin and app to prevent data confusion
- Sandbox-bound email routed to internal catch-all inbox, never to real recipients
- Hourly-throttled, audit-logged `Nuke sandbox` for full reset between test runs
- Same hooks and validations as production — no Cosmos shortcuts
In practice
An engineer is implementing a new license-renewal flow and wants to confirm it handles an expired French license correctly. She opens the sandbox view, picks the `retired player` preset, sets nationality to FR and license state to expired, and clicks spawn. The factory hands her a credentials packet, she logs in via the test-session sibling flow, walks through the renewal, and inspects the verification email in the sandbox mail inbox.
The flow has a bug; she patches it, redeploys to dev, and clicks `Nuke sandbox` to wipe the state. The audit log records her wipe with reason and timestamp, and the next test starts from a clean tenant.
Features in this subsystem
5| ID | Status | Features |
|---|---|---|
| F21.03.01 | Shipped | Create sandbox user with chosen role, language, nationality, license state (none/active/expired), club affiliation, 2FA state. ✅ PL-T123 |
| F21.03.02 | Shipped | Preset scenarios — "new player, just registered", "club admin with pending approvals", "federation admin with open incidents", "retired player", "suspended license". One-click spawn. Implemented |
| F21.03.03 | Shipped | Nuke sandbox — delete all sandbox users + data. Throttled to once per hour, audit-logged. Implemented |
| F21.03.04 | Shipped | Sandbox tenant is visually marked in admin and app: amber header bar "SANDBOX — not real data". Implemented |
| F21.03.05 | Shipped | Sandbox emails routed to an internal catch-all inbox instead of real recipients. Implemented |