Testing Strategy
Testing matrix and coverage requirements for the V0.1 release across all three applications.
API (PHPUnit — ≥ 80% critical domain coverage)
All API security tests are CI-blocking. A failing test prevents merge.
Critical Test Categories
| # | Category | What to test |
|---|
| 1 | Collection security | No line-level voters on collections. Doctrine Filter global ON. Cross-tenant queries must fail. |
| 2 | Doctrine Filter | Filter ON everywhere in HTTP. QueryFilterEnforcer returns 500 if filter OFF. CLI whitelist allows exempted commands. |
| 3 | Slug / i18n | NFKC/fold normalization. FR and AR slug collision tests are blocking. ICU version pinned. |
| 4 | State Processor | tenantId injection from JWT. Malicious tenantId in body silently ignored. |
| 5 | Voters + expressions | Item-level voters only. WRITE cross-tenant returns 403. |
| 6 | ETag / If-Match | Behavior per CONCURRENCY_STRICT flag. 428 (missing header) / 412 (mismatch) when strict. Canonical JSON non-regression. |
| 7 | Presigned uploads | AWS mocks for presigned PUT. HEAD post-upload with backoff. Size/type validation. |
| 8 | RateLimiter | Auth/public key isolation. Pepper rotation. Retry-After header. Quota per role. |
| 9 | JWKS | Rollover (double-KID). Clock skew tolerance. aud as string and array. id_token refused. Cross-env/tenant tokens refused. Circuit breaker behavior. |
| 10 | Public projections | GET anonymous is cacheable (correct Vary/Cache-Control/ETag). Authorization present returns 403 + no-store. Edge strip tested. |
| 11 | Preview | X-Preview-Token header. HMAC signature validation. Redis one-time (SETNX+TTL). Expired/replay returns 401. |
| 12 | RFC 7807 errors | All 4xx/5xx responses return application/problem+json conformant body. |
Front-Office (Vitest + Playwright)
| Category | Tool | What to test |
|---|
| Components | Testing Library | Rendering, props, events, slots |
| Accessibility | Testing Library | Skip link present, focus management, hreflang correct |
| CSP | Playwright | Zero unhashed inline scripts/styles. XSS injection attempt blocked. |
| Performance budgets | Lighthouse CI | Hard-fail if JS > 180KB (+20%), CSS > 60KB (+20%), LCP > 2.0s/2.5s |
| Matomo DNT | Playwright | DNT=1 header produces zero Matomo network requests |
| Silent auth | Playwright E2E | AT expiration + ITP-like scenario results in clean relog. prompt=none failure triggers modal. |
Back-Office (Vitest + Playwright)
| Category | Tool | What to test |
|---|
| Components | Testing Library (Vitest) | Rendering, props, events, slots |
| Accessibility | Testing Library | Accessible forms, keyboard navigation, focus management |
| CSP | Playwright | Hash-only enforcement, zero unhashed inline |
| Trusted Types | Playwright | Report-only mode active (V0.1) |
| XSS | Playwright | Injection attempt produces 0 API calls |
| OpenAPI hash | Build step | Build fails if local hash does not match /contract-hash |
| ETag UX | Playwright | 412/428 responses handled gracefully in UI |
Integration Tests
| Category | What to test |
|---|
| CORS | Allowed origins, exposed headers, preflight responses |
| Vary | Origin, Accept-Language, Accept-Encoding present on all responses |
| Authorization caching | GET with Authorization header returns Cache-Control: no-store |
| Edge strip | /v1/public/* requests have Authorization stripped by edge nginx |
E2E Smoke Tests (Playwright)
Front-Office Smoke
- All locales render correctly (
fr, en, nl, ar)
hreflang tags present and correct
- Lazy images load on scroll
- RTL layout renders correctly for
ar
Back-Office Smoke
- Real OIDC login (Keycloak, no mock) without Refresh Token
- Minimal CRUD: create, read, update, delete an article
- Preview one-time token: header-based, non-cacheable, single use
SLO Validation (k6)
| Metric | Target |
|---|
| API p95 latency | < 300ms |
| API p99 latency | < 600ms |
| Error rate | < 1% |
| Test seed | 3 tenants x 4 locales x 10,000 articles |
| Warm-up | 2 minutes before measurement |