Two backlog trees are complete: `docs/project/backlog/` (75 files, every WP done) and `docs/project/refactor-backlog-setup/` (the arc before it). Move both under `docs/project/archive/` with `git mv`, so history stays intact through `git log --follow`. `SHOWCASE-ROADMAP.md` moves with them, because it points at the now-archived backlog README. Add `docs/project/archive/README.md`. It states that these trees are historical and names the two directories that are still live. Repoint every inbound reference named in RD-30's Files table: CLAUDE.md, the root README, both backend READMEs, `LetterHtml.cs`, `a11y.mdx`, the `document-feature` and `new-ssp` skills, and the readable-codebase PLAN, README, and RD-19 ticket. Fix two upward-relative links inside the moved WP files (WP-68, WP-69) that gained a directory level and would otherwise break. Repoint `.prettierignore`'s two agent-prompt exclusions to their new path, so prettier keeps leaving those files' exact wording alone. Mark RD-30 done and check off its acceptance criteria; flip its README row to done. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
87 lines
4.3 KiB
Markdown
87 lines
4.3 KiB
Markdown
# WP-74 — E2E isolation without a new backend endpoint
|
|
|
|
Status: done (42f7bd6)
|
|
Phase: 12 — DDD hardening
|
|
|
|
## Why
|
|
|
|
The three Playwright specs shared one mutable backend and admitted it in their own comments
|
|
("Restart the backend between CI runs — a second run would see a leftover Concept"). A crashed
|
|
mid-wizard run poisoned every subsequent run via `CreateConcept`'s 409, and both mutating specs
|
|
acted as the same identity (`DocumentStore.DemoOwner`), so any new state-touching spec would
|
|
collide immediately.
|
|
|
|
## Decisions (pre-made)
|
|
|
|
**WP-70 recorded the fix as a dev-only seed endpoint. That premise was wrong**, and exploration
|
|
established why:
|
|
|
|
- The DB path already routes through `IConfiguration` (`Program.cs`,
|
|
`Db.ConnectionString = GetConnectionString("AppDb") ?? …`), so `ConnectionStrings__AppDb` as an
|
|
env var gives a throwaway DB with **zero backend change** — the same trick
|
|
`TestWebApplicationFactory` already uses per-test.
|
|
- `StubIdentityProvider` **already honours** an `X-Subject` header; the only gap was that no FE
|
|
interceptor sent one.
|
|
- The backend has **no `IsDevelopment()` gate anywhere** (grep: zero hits), so a seed endpoint
|
|
would have had to invent the codebase's first environment gate — a new security posture for no
|
|
gain.
|
|
|
|
So: throwaway DB + a dev-only `X-Subject` interceptor. No new endpoint, no environment gate.
|
|
|
|
## Acceptance criteria
|
|
|
|
- [x] **`npm run e2e` passes twice back-to-back with no backend restart** — the actual acceptance
|
|
test, and the thing that failed before this WP.
|
|
- [x] **`X-Subject` observed on a real request** reaching the backend (`X-Subject: 111222333` on
|
|
`GET /api/v1/uploads/categories`), not merely wired.
|
|
- [x] Each new BSN elfproef-verified by script against the weights `[9,8,7,6,5,4,3,2,-1]`.
|
|
- [x] No backend change, no new endpoint, no `IsDevelopment()` gate.
|
|
- [x] Committed port config still defaults to 4200 (verification used an override).
|
|
|
|
## Notes on the two caveats
|
|
|
|
- **`reuseExistingServer` stays on.** Flipping it to `false` would hard-fail `npm run e2e` for
|
|
anyone already running the docker stack on 4200/5000 — a real local-workflow regression. The
|
|
consequence (the throwaway DB only applies when Playwright itself spawns the backend; always
|
|
true in CI) is documented in a comment on the `webServer` entry.
|
|
- **Unique DB filename per invocation**, with `global-setup.ts` sweeping only _prior_ runs'
|
|
leftovers. A fixed name unlinked mid-run is only safe if SQLite's pool never reopens by path
|
|
afterwards; under `fullyParallel` that risks silently recreating an empty, unmigrated DB.
|
|
|
|
## Deviation: interceptors alone were not enough
|
|
|
|
Two hand-written call sites bypass Angular's interceptor chain (as `CLAUDE.md` documents) and
|
|
needed `X-Subject` stamped explicitly:
|
|
|
|
- `libs/shared/src/upload/upload.adapter.ts`'s raw XHR upload — without this every uploaded
|
|
document landed under `DemoOwner`, breaking submit for any other identity.
|
|
- `apps/ssp/.../letter-preview.adapter.ts`'s preview fetch (plus `cache: 'no-store'`, correct
|
|
regardless since the endpoint sends no `Cache-Control`).
|
|
|
|
## Known gap (a real backend bug, not caused by this WP)
|
|
|
|
Under any BSN other than `DemoOwner`, `GET /brief/preview` returns a **sent** letter still
|
|
carrying the draft watermark — while `curl` against the same backend at the same instant returns
|
|
the correct frozen archive. Client caching was ruled out (`no-store`, then cache-busting query
|
|
strings), the dev proxy was ruled out, and it reproduced across two BSNs and never for
|
|
`DemoOwner`. This points at a staleness/race in `BriefStore`'s SQLite read path.
|
|
|
|
`brief-v2.spec.ts` therefore keeps the shared `zorgverlener` identity — it still gains
|
|
throwaway-DB repeatability, just not per-spec identity isolation. `actors.ts` reserves a
|
|
`briefOpsteller` actor for whoever fixes the backend. **Tracked as a follow-up below.**
|
|
|
|
## Verification
|
|
|
|
```bash
|
|
npm run e2e # twice consecutively, no backend restart
|
|
npm run lint && npm run typecheck && npm test && npm run build
|
|
```
|
|
|
|
Note: port 4200 was held by an unrelated container on the dev machine, so verification ran with
|
|
`E2E_BASE_URL` pointed at an alternate port. The committed default is unchanged.
|
|
|
|
## Follow-ups
|
|
|
|
- **`/brief/preview` staleness for non-`DemoOwner` identities** (above) — the blocker for giving
|
|
`brief-v2.spec.ts` its own identity.
|