# Testing

```bash
npm test            # vitest unit suite
npm run typecheck   # tsc --noEmit
npm run lint        # eslint
```

All three must pass before a task is marked complete.

## Current suite (80 tests, all passing)

| File | Covers |
| --- | --- |
| `tests/ssrf.test.ts` | **Security-critical.** Private/loopback/link-local/CGNAT/metadata IPv4+IPv6 blocking, protocol allowlist, credential-in-URL rejection, hostname blocklist, malformed URLs |
| `tests/robots.test.ts` | robots.txt parsing, longest-match Allow/Disallow, agent-group selection, wildcards/anchors, crawl-delay |
| `tests/url.test.ts` | URL normalization (ports, tracking params, slashes, relative resolution), internal/external classification |
| `tests/parse.test.ts` | HTML extraction: titles, headings, canonical, JSON-LD types, alt text, links, noindex, word count/hash |
| `tests/password.test.ts` | argon2id hash/verify, strength policy |
| `tests/tokens.test.ts` | token generation/hashing/constant-time match |
| `tests/rbac.test.ts` | Role permission matrix (read-only cannot write, reviewer approves but can't publish, only owner bills…) |
| `tests/rate-limit.test.ts` | window limits, key isolation |

## Test policy by layer

- **Pure logic** (crawler parsing, SSRF, RBAC, tokens): unit tests, no I/O.
- **API routes**: integration tests against a disposable Postgres
  (`docker compose up -d db` + `prisma migrate deploy` into a test DB) —
  being added with the member-management endpoints; the critical
  tenant-isolation cases (org A cannot read org B's website/crawl; 404 not
  403) are specified in TASKS.md as blocking acceptance criteria.
- **Worker/queue**: crawl lifecycle against a local Redis, including
  cancellation mid-crawl.
- **E2E browser**: registration → add website → crawl → issues, via
  Playwright once Phase 2 UI stabilises.

## Manually verified this phase (browser, real DB)

- Registration/login/logout, session cookie behaviour.
- Dashboard renders seeded organisation + website (real DB rows).
- Website page: verification instructions, crawl start.
- Failure visibility: crawl start with Redis down → fast, clear error and
  crawl marked FAILED (no silent hang).
