From 4197df2b2f41d65da7d857c1dac70220558ede03 Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Mon, 4 May 2026 22:24:05 +0200 Subject: [PATCH] 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 --- dev-docs/ARCH-CONSOLIDATION-2026-04.md | 6 +- dev-docs/BACKLOG.md | 90 ++++++++++++++++---------- 2 files changed, 61 insertions(+), 35 deletions(-) diff --git a/dev-docs/ARCH-CONSOLIDATION-2026-04.md b/dev-docs/ARCH-CONSOLIDATION-2026-04.md index af920cde..12fc6072 100644 --- a/dev-docs/ARCH-CONSOLIDATION-2026-04.md +++ b/dev-docs/ARCH-CONSOLIDATION-2026-04.md @@ -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 diff --git a/dev-docs/BACKLOG.md b/dev-docs/BACKLOG.md index 4db9073d..1b3da1c4 100644 --- a/dev-docs/BACKLOG.md +++ b/dev-docs/BACKLOG.md @@ -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. ---