Boots the REAL Fastify app over a fresh in-memory DB (buildServer({ db }), driven by
app.inject — no listen) to exercise the security seam end to end:
- routes.test.ts (7): /health open; login rejects bad creds and sets token+csrf
cookies on good ones; an unauthenticated GET /api/occupancy is 401; a site:read-only
role GETs occupancy but is 403 on PUT /api/site-config (the permission gate, with a
valid CSRF so the 403 is the perm check); an admin passes the same PUT; and a mutation
with the auth cookie but NO csrf header is 403 (double-submit enforced).
Adds seedUser()/login() helpers (real bcrypt + the real /api/auth/login route) and
LOG_LEVEL=silent in the vitest env so asserted 401/403 responses don't flood output.
server 75/75 green (8 suites).
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
Foundation for testing every service. Adds @parking/db/testing — createTestDb()
spins a fresh in-memory SQLite and applies the real Drizzle migrations, so server
tests run against the production schema with zero live-DB risk.
Wires Vitest into apps/server (test script + config; test signing keys via env)
and adds the first Phase-1 suites against the anti-fraud core:
- signer.test.ts (10): sign/verify round-trip, tamper + forgery rejection,
malformed-signature guard, determinism, keyId rotation (buildVerifier).
- event-log.test.ts (12): monotonic index, prevHash linkage, payload-in-signature,
append serialization, and verifyChain() catching every tamper class — edited
payload, deleted row (index gap), broken prevHash, unknown keyId — plus
canonicalize byte-stability.
Also stops *.test.ts leaking into shipped dist/ (tsconfig exclude in server +
shared; shared had been emitting compiled tests all along).
server 22/22, shared 87/87 green.
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V