docs: foundational specs (PRD, architecture, design system, database, security, code style, testing) + AGENTS operational rulebook

This commit is contained in:
AHMED
2026-10-11 06:25:27 +03:00
parent 74b57bfee4
commit 7be9ffd3ed
8 changed files with 605 additions and 0 deletions
+60
View File
@@ -0,0 +1,60 @@
# 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).