4.5 KiB
4.5 KiB
TESTING — anwarpy.ai
Owner decision 2026-10-11: tier (b) is the bar — Vitest unit tests for the core libs + regression tests for API route behavior. Tier (c) — Playwright E2E for critical journeys — activates when Phase 3 (Stripe) lands; journeys to cover then: signup→verify→login, VIP ghost-read (billing on), checkout happy path, webhook apply.
1. Standing gates (before any commit/merge)
cd web && npx tsc --noEmit # must exit 0
cd web && npm run build # must exit 0 (includes generate:types)
Both green = minimum. Gates alone are not proof of behavior — see §3.
2. Unit tests (Vitest, tier b)
- Add
vitestas a dev dependency; tests live beside the code or underweb/src/lib/__tests__/— pick one location and stay consistent (recommend__tests__/next tosrc/lib). - Required suites (the known-risk list):
| Suite | Must pin |
|---|---|
launch.test.ts |
policy truth table: billingEnabled() false when LAUNCH_MODE unset/free_early_access/paid… — including the fail-safe default |
lexical.test.ts |
lexicalToText/countLexicalWords descend into content.root (regression: the 2026-10-10 launch-blocking bug where both returned 0/'' on real serialized trees); extractH2s, lexicalToPlain |
secret-scan.test.ts |
one case per pattern (aws_access_key, pem_private_key, openai_api_key, github_token, generic_credential) returns pattern name only, never the matched text; clean payload passes; string content is scanned too |
topics.test.ts |
isTopic accepts exactly agents/models/automation/foundations; topicsFor mapping |
format.test.ts |
guidesLabel (1 دليل، 2 دليلان، 3–10 أدلة، 11+), minutesLabel, MEMBERSHIP_PRICE |
- Rule: tests assert behavior, not implementation text. No snapshot-of-source, no tautologies (
expect(fn).toBeDefined()), no mock-echo tests, no bare "does not throw". - Runtime env manipulation: use
vi.stubEnv/unstubAllEnvs— never let one test'sLAUNCH_MODEleak into another.
3. API route regression tests
Behavior tests for the routes with contracts (per PRD §3.6, SECURITY §4):
POST/PUT /api/agent/posts: 401 wrong/missing key (note:x-agent-key, not Bearer); 400 on stringcontent; 422 with pattern names on secret-bearing Lexical;sourceIddedup returns existing post (no 409, no duplicate row);pickFieldsstripsaccess/readingMinutes; PUT cannot setstatus.POST /api/subscribe: 503 + exact Arabic body while billing is off.POST /api/notify: records row then honest 503 when mail transport fails; duplicate email → 200alreadySubscribed: true; no double-send on dup.- Comments route: unauthenticated → reject; client-sent
authorignored (server-forced). - Likes: 401 anonymous; second like by same user → no duplicate row.
Implementation: pure logic moves into src/lib helpers where possible and gets unit-tested; handler-level tests run the route module with a mocked Payload local API (or hit a dev server for the smoke tier — §5). Prefer extracting decision logic over wiring up heavy mocks.
4. What must NOT become a test
- Wiring/copies/forwarding, mock echoes, source-text greps, "file exists".
- Incidental defaults that aren't contracts (an unstyled div's class list is not a spec).
- Anything already verified cheaper by the ghost-read/smoke script in §5.
- Per the gstack rule: existing tests that pin wording/implementation instead of behavior get deleted, not "fixed".
5. Smoke & verification scripts (throwaway, not committed)
- Prod verification is HTTP-level and scripted ad hoc (curl batches): ghost-read a VIP post logged out; check
isAccessibleForFree, og:image 1200×630, JSON-LD types; subscribe 503; notify row + cleanup; sitemap contents; www 308. Use@example.comrows and delete them after (ground truth: users=6, launch_notifications=0). - Keep the failing-before/passing-after pair when fixing a bug — a permanent regression test in §2/§3 if it's pure logic, the script transcript if it's deploy/config.
6. Before-merge checklist (condensed)
tsc --noEmit+npm run buildexit 0.- New/changed lib behavior → matching Vitest suite updated or added.
- Changed route contract → §3 regression updated (status + body shape).
- Changed access logic → ghost-read check (logged-out VIP post) recorded in the PR.
- No new
any, no new'use client'without justification, no new dependency without a line of justification (CODE_STYLE §8).