docs(16): add validation strategy

This commit is contained in:
2026-08-05 16:49:26 +02:00
parent 9a494b1afd
commit c5ce07afab
@@ -0,0 +1,89 @@
---
phase: 16
slug: ad-gruppen-synchronisation
# status lifecycle: draft (seeded by plan-phase) → validated (set by validate-phase §6)
# audit-milestone §5.5 distinguishes NOT-VALIDATED (draft) from PARTIAL (validated + nyquist_compliant: false) (#2117)
status: draft
nyquist_compliant: false
wave_0_complete: false
created: 2026-08-05
---
# Phase 16 — Validation Strategy
> Per-phase validation contract for feedback sampling during execution.
> Seeded from `16-RESEARCH.md` § Validation Architecture.
---
## Test Infrastructure
| Property | Value |
|----------|-------|
| **Framework** | Vitest 3.x |
| **Config file** | `apps/api/vitest.config.ts` |
| **Quick run command** | `cd apps/api && npx vitest run src/ldap/ldap.service.spec.ts src/groups/groups.service.spec.ts` |
| **Full suite command** | `cd apps/api && pnpm test` (bzw. `turbo test` vom Repo-Root) |
| **Estimated runtime** | ~30 seconds (quick) |
---
## Sampling Rate
- **After every task commit:** Run `cd apps/api && npx vitest run src/ldap/ldap.service.spec.ts src/groups/groups.service.spec.ts`
- **After every plan wave:** Run `pnpm test` (API + Web)
- **Before `/gsd-verify-work`:** Full suite must be green **plus** manueller Human-Verify-Checkpoint gegen echtes AD (ViCoTest) für A1/A2 (`objectGUID`-Stabilität, Binär-Filter-Syntax), bevor die Löschsemantik (D-05) als produktionsreif gilt
- **Max feedback latency:** 30 seconds
---
## Per-Task Verification Map
*Task-IDs werden vom gsd-planner vergeben; die Zeilen unten sind die aus RESEARCH.md abgeleiteten Verifikationsziele und werden beim Planen auf konkrete Task-IDs gemappt.*
| Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status |
|---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------|
| TBD | TBD | TBD | SC-1 | T-16-V5 (LDAP-Filter-Injection) | Import erzeugt `Group` mit `ldapDn` + `ldapObjectGuid`; DN/GUID escaped | unit | `vitest run src/ldap/ldap.service.spec.ts -t "importGroupsByDn"` | ❌ W0 | ⬜ pending |
| TBD | TBD | TBD | SC-2 | — | Nicht ausgewählte AD-Gruppen erzeugen keine `Group`-Zeile | unit | `vitest run src/ldap/ldap.service.spec.ts -t "importGroupsByDn"` (negativer Fall) | ❌ W0 | ⬜ pending |
| TBD | TBD | TBD | SC-3 | — | Rename im AD aktualisiert `name`/`ldapDn`, Mitgliedschaften bleiben erhalten | unit | `vitest run src/ldap/ldap.service.spec.ts -t "syncBoundGroupsForTenant — rename"` | ❌ W0 | ⬜ pending |
| TBD | TBD | TBD | SC-4 | T-16-D05 (Datenverlust ohne Warndialog, bewusst akzeptiert) | Verschwinden im AD löscht `Group` inkl. `GroupMembership`/`ModuleGrant` (Cascade) | unit + migration-sql | `vitest run src/ldap/ldap.service.spec.ts -t "syncBoundGroupsForTenant — delete"` | ❌ W0 (Cascade-SQL ✅ via 15-01) | ⬜ pending |
| TBD | TBD | TBD | SC-5 | — | Gruppen mit `ldapDn: null` bleiben vom Gruppen-Sync unberührt | unit | `vitest run src/ldap/ldap.service.spec.ts -t "ignores a Tessera group with no ldapDn"` | ❌ W0 (Muster aus D-21 vorhanden) | ⬜ pending |
| TBD | TBD | TBD | D-04 | — | `internalName` überlebt Sync, gewinnt in Liste / Matrix-Spaltenkopf / User-Chips | unit + component | `vitest run src/groups/groups.service.spec.ts` + Web-Component-Test | ❌ W0 | ⬜ pending |
| TBD | TBD | TBD | D-06 | T-16-RACE (Default-Handoff Race, P2002) | Kein Mandant ohne Standardgruppe nach Sync-Löschung | unit | `vitest run src/ldap/ldap.service.spec.ts -t "default marker handoff"` | ❌ W0 | ⬜ pending |
| TBD | TBD | TBD | D-07 | — | `GroupFormModal` löst keine `GET /ldap/groups`-Discovery mehr aus | component | Angepasster `groups-page.test.tsx` | ❌ W0 (bestehende Tests anpassen) | ⬜ pending |
*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*
---
## Wave 0 Requirements
- [ ] `apps/api/src/ldap/ldap.service.spec.ts` — neue `describe`-Blöcke für `importGroupsByDn` und `syncBoundGroupsForTenant` (Rename / Delete / Default-Handoff / Namenskollision)
- [ ] `apps/api/src/groups/groups.service.spec.ts` — `listForTenant`/`getImpact`-Selects auf `internalName ?? name` prüfen, sofern die Logik im Service liegt
- [ ] `apps/web/src/app/(portal)/admin/groups/groups-page.test.tsx` — Assertion, dass `GroupFormModal` nach D-07 keine AD-Discovery mehr auslöst
- [ ] Migration-SQL-Textest im Stil von `migration-sql.spec.ts` für die neuen Spalten (`internalName`, `ldapObjectGuid`) und den Unique-Index
- Kein neues Test-Framework nötig — Vitest ist bereits eingerichtet.
---
## Manual-Only Verifications
| Behavior | Requirement | Why Manual | Test Instructions |
|----------|-------------|------------|-------------------|
| `objectGUID` bleibt über eine AD-Umbenennung hinweg identisch (Annahme A1) | SC-3 / SC-4 | Kein AD im Test-Harness; entscheidet, ob Rename von Delete unterscheidbar ist | Gegen ViCoTest (`balios.ctl.local`) read-only: Gruppe suchen, `objectGUID` notieren, im AD umbenennen, erneut suchen, GUID vergleichen |
| Binär-Filter-Syntax für `objectGUID` liefert Treffer (Annahme A2) | SC-3 / SC-4 | RFC-4515-Escaping für Binärwerte nicht gegen echtes AD verifiziert | Gegen ViCoTest read-only: Suche mit escaped `objectGUID`-Filter absetzen, Treffer prüfen |
| AD-Löschung entzieht Modulzugriff ohne D-17-Warndialog (bewusst akzeptiert) | SC-4 / D-05 | Sichtbare Nutzerwirkung, kein Unit-Test-Ziel | Importierte Gruppe im AD löschen, Sync laufen lassen, Modulzugriff des betroffenen Benutzers prüfen |
---
## Validation Sign-Off
- [ ] All tasks have `<automated>` verify or Wave 0 dependencies
- [ ] Sampling continuity: no 3 consecutive tasks without automated verify
- [ ] Wave 0 covers all MISSING references
- [ ] No watch-mode flags
- [ ] Feedback latency < 30s
- [ ] Live-AD-Checkpoint für A1/A2 durchgeführt
- [ ] `nyquist_compliant: true` set in frontmatter
**Approval:** pending