Approach selection
Scope: pick the cheapest verification that actually proves your claim. This page is the decision table; the scripts live in procedures, the things you test against in infrastructure.
| What you're testing | Approach | Clerk network call? |
|---|---|---|
| Unit test — guard logic, pipes, decorators | Mock verifyToken() + synthetic request.auth | None |
| Unit test — webhook sync service | Mock PrismaService + ClerkBackendService | None |
| Backend e2e — portal endpoints | Guard-override pattern (override the guards in test/*.e2e-spec.ts) | None |
| Backend e2e — webhook endpoint | Pre-signed svix payload with a known secret | None |
Frontend unit — components using useAuth | Mock the @clerk/react module | None |
| Service method against real DB, no HTTP | Context probe (Recipe D) | None |
| Webhook handler mirrors an event | Signed-payload replay (Recipe C) | None |
| Real Clerk event → webhook → DB (live) | Relay round-trip (Recipe A) | Yes |
| Portal endpoint works for a real user | Real org-active token (Recipe B) | None (if DB pre-mirrored) |
| Full signup → gate → HTTP response | Recipe E (A+B combined) | Yes |
Selection rules:
- Default to the cheapest row that proves your claim. A bare
401only proves the route exists. - Mocked unit tests don't prove the server boots or the DB accepts the query — pair them with the runtime-verification mandate before calling a task done.
- For anything depending on auth, org resolution, or quotas, use a real token (Recipe B) or the combined flow (Recipe E), and assert the real response body plus the DB side effects — not just the status code.
Source-checked against optolink-backend @ 29f8589 and optolink-portal @ 7d31d45, 2026-10-06 (mock patterns:
clerk-auth.guard.spec.ts,clerk-sync.service.spec.ts,test/portal-*.e2e-spec.ts,src/**/*.test.tsx). Recipe scripts themselves are verified on their own procedure pages.