Testing & Harness
Three layers of verification, tracked on one scoreboard: probe 34/34 screens · flows 14/14 · backend tests 27/27 (the "closed 2026-07-13" milestone in docs/v2-rewrite/08-v2.1-gap-analysis.md).
Layer 1 — Backend unit tests (Vitest)
packages/backend, run with pnpm -C packages/backend test.
- Vitest 4,
environment: "edge-runtime"(@edge-runtime/vm), withconvex-testinlined — tests run the real Convex functions against an in-memory backend built from the actual schema:convexTest(schema, import.meta.glob("./**/*.ts")). - One suite:
convex/authz.test.ts(~27 cases), authorization and tenant isolation:- Staff data access (QA-001) — owner lists staff incl. pay; manager/employee/customer are FORBIDDEN by default; a
rolePoliciesoverride to manager flips the outcome. - Settings access (QA-001) — manager reads all settings surfaces; employee/customer get typed FORBIDDEN.
- Platform surface (QA-002) —
amIPlatformOwnertrue only for active org owners;applicationsListrejects others. - Typed not-found (QA-003) — malformed ids return
nullacross every detail read; cross-tenant ids returnnull(no existence leak); valid in-org ids resolve. - Org resolution — unknown slug → ORG_NOT_FOUND, bad token → UNAUTHENTICATED, non-member → FORBIDDEN.
- Time-clock self-access — employees only ever receive their own entries.
- Staff data access (QA-001) — owner lists staff incl. pay; manager/employee/customer are FORBIDDEN by default; a
- Gap worth knowing: no unit coverage yet for pricing/tax math, the cron sweep, providers, or the visit state machine beyond what authz exercises — those are covered end-to-end by the flows harness instead.
Layer 2 — Screen probe (Playwright)
tools/walkthrough/v2-probe.mjs (repo root; the walkthrough dir is shared with v1's video tooling — plain Node + playwright, no test-runner config).
- Logs in through the real UI (
demo_owner@pawdentity.test→ picks Demo Boarding). - Visits a hardcoded list of 29 routes (accounts, dashboard, board, all the /new forms, calendar, catalog, notes, messages, schedule, staff, staff/me/hours, all 7 reports, settings, portal, platform) plus 5 drill-downs (row-click into client/pet/visit/order detail and a message thread) — the "34 screens".
- Fails the run on any visible error text (
Unhandled Runtime Error,Application error,404,500,Build Error) or any non-favicon console/page error.
Layer 3 — Business flows (Playwright)
tools/walkthrough/v2-flows.mjs — 14 end-to-end flows asserting observable results, printed as PASS | FAIL | SKIP per flow (nonzero exit on FAIL):
- Board check-in
planned → in_housewith live count increment + desk agreement e-sign gate - Log a care event (feeding) verified via toast
- Check-out
in_house → completedwith live count decrement - Record a payment on an unpaid order
- Create a note
- Realtime cross-session — a second login (
jackson@) sees the board update with no refresh - Medication needed → administered
- Pet issue report → resolve
deposit_due → deposit_met- Portal self-serve — client
ava.west@example.comsaves a card → books a stay → pays the deposit, zero staff touches 11–14. v2.1 tail: calendar week/day views, catalog inventory badge, brand-logo upload in settings, public/our-customersdirectory
Running everything locally
cd v2/packages/backend && pnpm test # 27 authz cases
# stack up (see Running Locally), then:
cd tools/walkthrough
npm install && npx playwright install chromium
node v2-probe.mjs
node v2-flows.mjs
Philosophy
The harness favors observable behavior over implementation: flows assert what a user sees (counts change, toasts appear, a second session updates live) rather than internal state. The probe is a cheap tripwire — every screen must render clean before any deeper claim is made. CI runs all three layers on every v2.1 PR; see CI Pipeline.