docs: close TECH-AXIOS-STORE-COUPLING and add TECH-AXIOS-INTERCEPTOR-TESTS

Removes the closed TECH-AXIOS-STORE-COUPLING entry from BACKLOG.md
(the structural decoupling landed in 53f6a7b + 26a92b3). The
git-history search `git log --grep=TECH-AXIOS-STORE-COUPLING`
remains the durable closure record, per the backlog hygiene
convention.

Adds a follow-up entry TECH-AXIOS-INTERCEPTOR-TESTS that captures
all four acceptance scenarios (X-Organisation-Id header
injection, 401 auth-fail flow, 403+impersonation_ended revocation
flow, 4xx/5xx error toast). Phase A audit found that none of
these is tested today; the refactor is gedragsneutraal so no
regression was introduced, but the gap is real and should not
silently outlive the refactor that made it visible. Priority
medium per Bert's Phase B sign-off.

Appends the debt-closed sentence to the Sessie 1c entry in
ARCH-CONSOLIDATION-2026-04.md, citing commit 53f6a7b.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-05-04 22:24:05 +02:00
parent 26a92b3078
commit 4197df2b2f
2 changed files with 61 additions and 35 deletions

View File

@@ -574,7 +574,11 @@ Zie §4 voor scope en stappen.
enforcement na PR-B), `TECH-WS3-BOUNDARIES-ROUTER-ZONE` (matrix-update
wanneer `plugins/1.router/` naar `router/` verhuist). WS-3 lint cleanup
+ boundaries enforcement effectief afgerond; volgende WS-3 stap is PR-B
(portal merge) zodra WS-6 sessie 2 in main is geland.
(portal merge) zodra WS-6 sessie 2 in main is geland. Debt closed in
commit `53f6a7b`: `lib/axios.ts` decoupled from stores via
`registerInterceptors(client, deps)` callback seam,
`plugins/3.axios-bindings.ts` provides the runtime wiring; the four
per-line disables are removed.
**Klaar-criteria:**
- `apps/portal/` is verwijderd

View File

@@ -602,43 +602,65 @@ sprint waard, geen meelift-pad.
---
### TECH-AXIOS-STORE-COUPLING — Decouple lib/axios.ts from stores layer
### TECH-AXIOS-INTERCEPTOR-TESTS — Coverage voor de vier axios-interceptor scenarios
**Aanleiding:** WS-3 sessie 1c (eslint-plugin-boundaries enforcement)
constateerde dat `apps/app/src/lib/axios.ts` 4 imports heeft uit `stores/`
(2 statisch op regel 3-4 voor `useNotificationStore` /
`useOrganisationStore`, 2 dynamisch op regel 61, 72 voor
`useImpersonationStore` / `useAuthStore` uit 1b-iii). De `lib → stores`
edge schendt de layered-architecture matrix. Om sessie 1c on-time te
landen zijn de 4 sites gemarkeerd met `eslint-disable-next-line` +
verwijzing naar dit backlog-item; de structurele fix is bewust uitgesteld
naar een dedicated sessie omdat het architectuurwerk is, geen tooling-
cleanup.
**Aanleiding:** TECH-AXIOS-STORE-COUPLING (gesloten 2026-05-04, zie
`git log --grep=TECH-AXIOS-STORE-COUPLING`) heeft `lib/axios.ts`
ontkoppeld van de stores via een `registerInterceptors(client, deps)`
seam plus `plugins/3.axios-bindings.ts`. Tijdens de Phase A audit van
die sessie bleek dat de vier acceptatie-scenarios geen van alle een
test hebben — niet vóór en niet ná de refactor. De refactor is
gedragsneutraal (1:1 behoud), dus er is geen regressie geïntroduceerd,
maar het blijft een echte coverage-gap die we niet wilden meeniggen
in de refactor-sessie zelf: refactor-en-test-toevoeging in dezelfde
commit-set vernietigt het vermogen om vast te stellen of de tests pre-
of post-refactor gedrag specificeren.
**Wat:**
- Decouple `lib/axios.ts` van stores zodat het puur HTTP-infrastructuur
wordt. Twee paden, kies bij refactor:
- **Approach 1 (preferred starting point):** `lib/axios.ts` exporteert
de axios-instance plus een `registerInterceptors(client, deps)`
functie die callbacks accepteert (`onAuthFail`,
`onImpersonationDrop`, `getActiveOrgId`, `notify(message, level)`).
Een nieuwe `plugins/axios-bindings.ts` (mag `stores` importeren per
matrix) roept `registerInterceptors` aan bij app-init met closures
over de stores.
- **Approach 2 (fallback):** event-bus / callback registry; axios.ts
emit-eert semantische events (`auth-failed`, `needs-org-header`,
`notify-error`) en `plugins/axios-bindings.ts` subscribet.
- Verwijder alle 4 `eslint-disable-next-line` comments uit
`lib/axios.ts`.
- Tests moeten dekken: X-Organisation-Id header injection, 401/403
logout flow, impersonation revocation flow, error toast op 4xx/5xx.
- Optioneel: meteen ook de static/dynamic import-split in axios.ts
uniformeren (nu inconsistent om legacy 1b-iii-redenen).
**Wat:** Vitest-tests die de interceptors echt laden (niet via
`vi.mock('@/lib/axios')`) en assertions doen tegen een gemockte
HTTP-laag (bv. `axios-mock-adapter`). De vier scenarios:
**Prioriteit:** Middel — geen blokker voor andere workstreams, maar elke
maand dat dit blijft staan is een vlek op de boundaries-enforcement
geloofwaardigheid. Aanbevolen: meelift met de eerste WS-3 PR die `lib/`
of `plugins/` raakt, of een dedicated 2-3 uur sessie na WS-6 sluiting.
1. **`X-Organisation-Id` header-injection.** Set een actieve
organisatie via `useOrganisationStore`, registreer interceptors
met de bindings-deps, fire een outbound request, assert dat de
header de actieve ULID bevat. Test ook het null-pad: geen
actieve organisatie → header niet gezet.
2. **401 → auth-fail flow.** Mock de response op 401, registreer
interceptors waarbij `onAuthFail` een spy is, assert dat de
spy wordt aangeroepen exact wanneer `useAuthStore.isInitialized`
true is en niet gedurende de eerste `/auth/me`-probe (de
race-conditie die sessie 1b-iii repareerde — die guard moet
blijven werken).
3. **403 + `impersonation_ended` → revocation flow.** Mock de
response op `403` met body `{ impersonation_ended: true }`,
registreer interceptors waarbij `onImpersonationRevoked` een
spy is, assert dat de spy precies één keer wordt aangeroepen
en dat de generieke 403-toast NIET wordt geactiveerd (dat was
een early-return in `axios.ts`, makkelijk per ongeluk te
breken bij een toekomstige refactor).
4. **4xx/5xx error toast.** Mock 403/404/422/503/5xx/network-error
responses, assert dat `notify` de juiste boodschap + level
krijgt voor elk geval. 422 met body-`message` moet het
server-bericht doorgeven; 422 zonder `message` mag geen toast
triggeren (huidige gedrag).
**Hoe niet:** geen unit-tests die het hele `lib/axios.ts`-module
mocken — die testen de seam niet, alleen het mock-framework. De
toegevoegde waarde zit in een test-fixture die de echte
`registerInterceptors` aanroept met een echte axios-instance die
tegen `axios-mock-adapter` praat.
**Niet in scope:** integratietests die echt door Pinia heen lopen.
De seam is bewust callback-injectie zodat tests met spy-callbacks
volstaan. Wie de full-stack flow wil dekken (zie `App.vue`'s
session-init dance) doet dat met E2E in een latere sprint.
**Prioriteit:** Middel — de gap is reëel maar niet blokkerend. De
6 bestaande `vi.mock('@/lib/axios')`-tests vangen "named export
werkt nog" af, en de vier flows zijn manueel via de browser
verifieerbaar. Aanbevolen moment: eerste WS-3 PR die `axios.ts`
of de bindings-plugin opnieuw raakt, of opvolger van TECH-APP-VITEST
als bredere harness-uitbreiding.
---