Files
anwar-pi-website/TESTING.md
T

61 lines
4.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)
```bash
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 `vitest` as a dev dependency; tests live beside the code or under `web/src/lib/__tests__/` — pick one location and stay consistent (recommend `__tests__/` next to `src/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's `LAUNCH_MODE` leak 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 string `content`; 422 with pattern names on secret-bearing Lexical; `sourceId` dedup returns existing post (no 409, no duplicate row); `pickFields` strips `access`/`readingMinutes`; PUT cannot set `status`.
- `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 → 200 `alreadySubscribed: true`; no double-send on dup.
- Comments route: unauthenticated → reject; client-sent `author` ignored (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.com` rows 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)
1. `tsc --noEmit` + `npm run build` exit 0.
2. New/changed lib behavior → matching Vitest suite updated or added.
3. Changed route contract → §3 regression updated (status + body shape).
4. Changed access logic → ghost-read check (logged-out VIP post) recorded in the PR.
5. No new `any`, no new `'use client'` without justification, no new dependency without a line of justification (CODE_STYLE §8).