Zum Hauptinhalt springen

Test-Übersicht — Medidentas (lebend)

Welche automatischen Tests sichern die Anwendung ab? Sie dienen True North #3 (vollständige & rechtssichere Dokumentation): jeder Test sichert, dass das dokumentierte Verhalten reproduzierbar stimmt und nicht unbemerkt driftet. Diese Übersicht ist lebend (Stand 0.127.1). 692 Tests / 57 DateienMED-D-285 (Onboarding-Kundenstrecke als klarer 3-Schritte-Flow, Slice B): +0 Tests — reine Client-Umstrukturierung (onboarding-public.component.ts von flacher Seite zum 3-Schritte-Stepper mit abgeleitetem „wo fehlt was?"-Report; nutzt bestehende Endpunkte kontext/entwurf/einreichen) → ng build-verifiziert; Angular-Komponenten haben keine Unit-Test-Infrastruktur (wie MED-D-267 ff.). 692 unverändert. Davor MED-D-284 (Onboarding-Grunddaten-Katalog erweitert, datensparsam): +1 Testdomain/stammdaten-uebernahme.test.ts: die neuen Grunddaten (Handynummer/Steuernummer/Steuerberater) bilden über ONBOARDING_STAMM_REGELN 1:1 auf die Person ab; es gibt bewusst KEIN Steuer-ID-/Ausweisnummer-Ziel (G-6). Zusätzlich ausfuellgrad-Erwartungen in api/onboarding-formular.test.ts 75→67 (die 2 neuen öffentlichen optionalen Felder Handynummer+Steuernummer verschieben den gewichteten Nenner; die Steuerberater-Felder sind auf der öffentlichen Strecke bewusst nicht erhoben, Art.-14-Gate). 691→692 (keine neue Datei). Davor MED-D-283 (Kunden-Self-Service-Portal, Slice S2 — passwortlose Auth-Routen; inkl. CodeRabbit-#320-Härtung): +9 Tests — neue api/portal.test.ts: dormant→404 (Golden Rule, alle 4 öffentlichen Portal-Routen), Anmelden ohne Konto-Enumeration (unbekannte E-Mail → identische ok-Antwort ohne Link; ungültige E-Mail → 400; ohne testMode: bekanntes vs. unbekanntes Konto → identischer Status UND Body), voller Magic-Link-Flow (Staff legt Konto an → anmelden liefert testLink → login setzt HttpOnly/Secure/SameSite=Lax/Path-Cookie → /ich liefert eigenen Mandanten → abmelden → /ich 401), Token-Replay → 401 (Einmalgebrauch), gesperrtes Konto → laufende Session 401 (Sperre beendet Session), /ich ohne gültiges Cookie → 401, Portal-Konto für unbekannten Mandanten → 404: 682→691, 56→57 Dateien. Davor MED-D-282 (Kunden-Self-Service-Portal, Slice S1 — Fundament, DORMANT): +16 Tests — neue domain/portal-auth.test.ts: Bearer-Hashing deterministisch/64-Hex/Rohwert nicht enthalten (MED-D-280); Magic-Link-Token-Gültigkeit (TTL + Einmalgebrauch); Session-Gültigkeit mit getrennter Idle- UND Absolut-TTL + Widerruf; Record-Konstruktion hasht den Bearer-Wert; atomarer Einmal-Claim verbraucheAnmeldeToken (zweiter Aufruf → null, kein Replay) + Session-Lebenszyklus + Konto-Scope (via MemoryRepo): 666→682, 55→56 Dateien. Davor MED-D-276 (CodeRabbit-Härtung #313, in-PR vor Merge, 4 Runden): +12 Testsapi/timer.test.ts (Segment-Konsistenz: starten öffnet ein Segment, buchen/verwerfen räumen Segmente; Direkt-Buchung bei Zeit über mehrere Mandanten → verteilen_noetig; Push-Endpoint-Allowlist istErlaubterPushEndpoint (HTTPS + Provider); POST /api/timer/vordergrund weist malformte mandantId mit 400 ab / akzeptiert explizit null; POST /api/push/subscribe weist fremden Endpoint mit 400 ab; Runde 2: deleteAktiverTimer als atomarer Claim (zweiter Aufruf → false), buchenVerteilt claim-first → zweite Buchung auf denselben Timer bucht NICHT erneut / kein_timer; Runde 3: loescheTimerSegmenteMitIds räumt nur die genannten Segmente — nebenläufiger Vordergrund überlebt, OP-TIME-5; Runde 4: korrigieren gleicht die Segment-Historie an den korrigierten Gesamtwert an): 654→666 (keine neue Datei). Davor MED-D-275 (Durchlaufender Timer + Vordergrund-Segmente + Buchungs-Overlay + Web-Push): +6 Testsapi/timer.test.ts (Vordergrund-Wechsel protokolliert Zeit je Akte ohne Konflikt; vordergrund(null) → „nicht zugeordnet"; buchenVerteilt legt je Zeile eine Leistungszeit an, räumt Segmente, setzt den Timer zurück; keine gültigen Zeilen → keine_zeilen; Pause schließt/Fortsetzen öffnet Segment): 648→654 (keine neue Datei; Client — SW/Push/Overlay — ng build-verifiziert, keine Client-Unit-Test-Infra). Davor MED-D-273 (Akte „Mandate"-Reiter-IA: Mandat-Overlay + Formulare→Dokumente + Meeting-Doku→Rail): +0 Tests (reine Client-Umstrukturierung in mandant-auftraege.component.ts/mandant-detail.component.ts — Overlay statt inline-Form, [hidden]-Register-Umstellung, Komponenten-Umzug in die Rail + Dead-Code-Bereinigung → ng build verifiziert; Overlay/Reiter-Platzierung wären grundsätzlich testbar, es fehlt aber die Client-Unit-Test-Infrastruktur): 648 unverändert. Davor MED-D-271 (Neuer Mandant als Overlay) + MED-D-272 (Overflow-Fix Akte/Overlay): +0 Tests (MED-D-271: Anlege-Form aus verwaltung.component.ts in neue neuer-mandant-overlay.component.ts verschoben, gleiche POST /api/mandanten-Logik → ng build verifiziert; MED-D-272: CSS-Umbruch-/min-width:0-Fixes in styles.css + eine Rail-Zelle → rein visuell, nicht unit-testbar): 648 unverändert. Davor MED-D-270 (CodeRabbit-Nachlese #310: Statistik-Retry-Race-Guard + Endpunkt-Doc): +0 Tests (monotoner statistikSeq-Guard in cockpit.component.ts + Doku-Korrektur /api/fragebogen/statistikng build verifiziert, nicht unit-testbar; der sicherheitsrelevante Server-Gate ist bereits in api/rbac.test.ts getestet — 403/403/200 — CodeRabbits Client-Component-Tests bewusst als Backlog übersprungen, Client build-only): 648 unverändert. Davor MED-D-269 (Statistiken als eigener Cockpit-Bereich): +0 Tests (Widget-Umzug Verwaltung -> Cockpit-Modus in cockpit.component.ts/verwaltung.component.ts -> ng build verifiziert, nicht unit-testbar): 648 unverändert. Davor MED-D-268 (A11y-Fix Mandanten-Zeilen = native Buttons): +0 Tests (Entfernen zweier ARIA-Rollen in cockpit.component.ts -> ng build verifiziert, nicht unit-testbar): 648 unverändert. Davor MED-D-267 (Mandanten-Liste als Landing, Cockpit-Modus): +0 Tests (reine Client-Ansicht in cockpit.component.ts — Filter/Liste rein abgeleitet aus vorhandenen GET /api/mandanten-Daten, kein neues Backend → ng build verifiziert, nicht unit-testbar; Angular-Komponenten haben keine Unit-Test-Infrastruktur): 648 unverändert. Davor MED-D-263 (CodeRabbit-Nachlese #299: stale-Attribut-Bereinigung + Template-Paginierung): +2 Testssignatur/docusign.test.ts (listeTemplates folgt nextUri über bis zu 100 Seiten und aggregiert, Eintrag ohne id verworfen; templateFelder dedupliziert je tabLabel und nennt den Fehler-Body bei Nicht-OK): 646→648 (keine neue Datei). Davor MED-D-262 (DocuSign-Template-Auswahl + Feld-Introspektion): +2 Testsapi/fremdformular-vorlage.test.ts (Provider-Templates + -Felder gezogen inkl. tabLabel-Reihenfolge; Provider ohne Introspektion → leere Listen, kein Wurf): 644→646 (keine neue Datei). Davor MED-D-261 (B41 Feld-Editor-Token-Fix): +0 Tests (reine CSS-Token-Korrektur im component-lokalen styles:-Block → ng build verifiziert, nicht unit-testbar): 644 unverändert. Davor MED-D-260 (Selbst-Rollenwahl, interim Admin-Bootstrap): +2 Testsauth/auth.test.ts (Selbst-Rollenwahl POST /api/ich/rolle ohne Admin-Guard → berater→admin bricht den Bootstrap-Deadlock, danach /api/benutzer 200 + /api/ich spiegelt die neue Rolle, ungültige Rolle → 400; erzwungen ohne Access-Header → 401): 642→644 (keine neue Datei). Davor MED-D-258 (CodeRabbit-Nachlese #292: eIDAS-Niveau-Guard + eindeutige Hausformular-Vorlage): +2 Testsapi/signatur.test.ts +1 (Template-Niveau unter der Matrix-Anforderung → auf das Matrix-Niveau angehoben, nie unter-signieren) + api/fremdformular-vorlage.test.ts +1 (zweite aktive Vorlage auf denselben Hausformular-Dokumenttyp → abgewiesen, eindeutiges Auto-Routing): 640→642 (keine neue Datei). Davor MED-D-257 (Hausformular auf ausfüllbare DocuSign-Fassung routen, MED-OP-FORM-6 Baustein 2): +3 Testsapi/signatur.test.ts +2 (Hausformular mit registriertem ausfüllbarem Template → geroutet auf anfordernAusTemplate inkl. Prefill/Template-Niveau/Identitäts-Mapping, keine flache PDF; ohne passendes Template → flache PDF-Erzeugung bleibt) + api/fremdformular-vorlage.test.ts +1 (anlegen übernimmt gültige Hausformular-Verknüpfung, lehnt unbekannten Dokumenttyp ab, ohne Verknüpfung dokumenttyp=null): 637→640 (keine neue Datei). Davor MED-D-256 (Eigenformular beim Versenden erzeugen+vorbefüllen): +2 Testsapi/dokumentgenerierung.test.ts (vorlageFuerBezeichnung: exakter Titel + generierungBezeichnung-Form → Vorlage, unbekannt → null) + api/signatur.test.ts (Makler-Alleinauftrag ohne Datei → beim Versenden erzeugt+vorbefüllt+signiert, %PDF-Bytes an den Provider; zwei Bezeichnungen auf Nicht-Vorlage umgestellt, damit kein_inhalt/422 weiter greifen): 635→637 (keine neue Datei). Davor MED-D-255 (Härtung nach dem DocuSign-Vorfall): +2 Testsapi/signatur.test.ts (Provider-Fehler mit Empfänger-PII → E-Mail im Detail redigiert [email]; Upstream-Read-Fehler der Ablage → provider_fehler statt opaker 500): 633→635 (keine neue Datei). Davor MED-D-254 (DocuSign-Fehler im Live-System sprechend machen): +5 Testsapi/signatur.test.ts (echter Provider: Dokument ohne Datei → kein_inhalt; Datei ohne Empfänger → kein_empfaenger; Provider-Wurf → provider_fehler mit Detail, kein halb angelegter Vorgang; HTTP 422 „keine abgelegte Datei" + HTTP 502 sanitisierter Grund): 628→633 (keine neue Datei). Davor MED-D-252 (Cockpit-Fokus entschlackt + „Zu fakturieren"-Filter): +1 Testapi/app.test.ts (offeneFakturierungMinuten summiert abrechenbar & nicht abgerechnet, lässt nicht-abrechenbare weg): 627→628 (keine neue Datei). Davor MED-D-251 (CodeRabbit-Nachlese zu Weg B): +2 Testsapi/fremdformular-vorlage.test.ts (anlegen lehnt eine zweite Vorlage mit derselben Template-ID ab; Provider-Wurf beim Template-Envelope → kein verwaistes Dokument): 625→627 (keine neue Datei). Davor MED-D-250 (Weg B: Fremd-Formular-Vorlagen/DocuSign-Templates, live verifiziert): +8 Testsdomain/fremdformular.test.ts +3 (baueTabWerte tabLabel=Attribut→Wert, leere weg; identitaetsMapping → AD-009-Rücklauf greift), neue api/fremdformular-vorlage.test.ts +5 (anlegen validiert Niveau/filtert unbekannte Attribute; starten legt Dokument mit Identitäts-Mapping + Vorgang an und befüllt die Tabs aus den Stammdaten; Prefill→Rücklauf-Kreis über AD-009; kein_empfaenger ohne Kontakt; Provider ohne Template-Seam → nicht_verfuegbar): 617→625, 54→55 Dateien. Davor MED-D-247 (Formular-Rücklauf-Prüfung, AD-009): +7 Testsdomain/fremdformular.test.ts +2 (ruecklaufRegeln bildet nur rückschreibbare Attribute ab; ATTRIBUT_ZU_STAMMZIEL ohne bank./praxis.), api/signatur.test.ts +5 (Diff aktuell↔neu; bank.iban ausgeschlossen; formularRuecklauf persistiert nichts; Übernahme schreibt nur Gewähltes versioniert mit Quelle formular; nichts_gewaehlt; kein Mapping → null): 610→617 (keine neue Datei). Davor MED-D-246 (Abgelaufene Dokumente abgeleitet, AD-005): +7 Testsdomain/aktenreife.test.ts +2 (abgelaufen = fällig/wichtig/Stufe aktiv, blockiert nicht; getrennt von fehlt), domain/naechste-schritte.test.ts +2 (dokumente-erneuern-Schritt + Sortierung wichtig zwischen Blocker und Wiedervorlage-Hinweis), api/dokument.test.ts +3 (Service detail/offenePunkte leiten den Ablauf ab; keine Wiedervorlage angelegt = Backlog sauber): 603→610 (keine neue Datei). Davor MED-D-244 (AD-006-Nachlese, Dokument-Scope-Guard auf den Signatur-Routen): api/signatur.test.ts +1 Test (HTTP-Guard: fremder Mandant → POST /signatur + /signatur/vor-ort liefern 404 ohne Provider-Aufruf; eigener Mandant → 201 mit Empfänger-Override): 602→603 (keine neue Datei). Davor MED-D-243 (AD-006 editierbarer Empfänger im Unterschriften-Assistenten): api/signatur.test.ts +2 Tests (aufzeichnender Stub-Provider: anfordern ohne Override → Mandant-Kontakt, mit Override → gewählte Person; vor Ort → Override-Name greift, E-Mail bleibt optional): 600→602 (keine neue Datei). Davor MED-D-241 (Akten-Cockpit-Design-System exakt übernommen): api/app.test.ts +1 Test (direkte, berater-editierbare Stammdaten telefon/geburtsdatum/strasse/plz/ort via PATCH → persistiert

  • erzeugt genau eine neue append-only Stammdaten-Version (G-4), leerer Wert löscht das Feld — weiche Validierung): 599→600 (keine neue Datei). (MED-D-242 „Über den Link hochgeladen" war reine Client-Sicht → keine neuen Server-Tests, 600 unverändert.) Davor MED-D-238 (DocuSign end-to-end live verifiziert + Private-Key-Parser gehärtet): signatur/docusign.test.ts +1 Test (verstümmelter PEM-Delimiter — 4 Trailing-Hyphens, keine Umbrüche, literale \n — wird trotzdem geparst, JWT-Signatur läuft durch): 598→599 (keine neue Datei). Davor MED-D-237 (In-Person-Host = eingeloggter Berater): signatur/docusign.test.ts +1 Test (Adapter bevorzugt den übergebenen Berater-Host vor der Default-Konfig, bei Envelope-Anlage und Recipient-View): 597→598. Davor MED-D-236 (echte Vor-Ort-/In-Person-Signatur, OP-SIGN-1): signatur/docusign.test.ts um 5 Tests erweitert (Adapter: vorOrtVorbereiten erstellt captive In-Person-Envelope ohne E-Mail-Versand + verlangt Host + lässt signerEmail ohne Kunden-Mail weg, vorOrtSignaturUrl liefert die Recipient-View-URL über die Host-Identität, docuSignKonfigAusEnv liest Host; SignaturService: vorOrtVorbereiteneingebettet:true gegen DocuSign / false+nicht_verfuegbar gegen Fake): 592→597 (keine neue Datei). Davor MED-D-235 (Fremdformular-Feldmapping, AD-010/MED-OP-FORM-5, Weg A): drei neue Dateien — pdf/pdf-formular.test.ts (4 Tests: AcroForm-Felder lesen inkl. Art/Seite/Optionen, leeres PDF ohne Formular, Text/Auswahl/Ankreuz füllen ohne Flatten, unbekannte Feldnamen defensiv), domain/fremdformular.test.ts (6 Tests: Weltmodell-Katalog eindeutig + Leser, anschrift.komplett, bank.iban Konto-vor-Alt-IBAN, bereinigeMapping, bauePrefillFelder/-Werte), api/fremdformular.test.ts (7 Tests: Service ansicht/mapping/vorbefuellen + PII-armes Audit + Fehlerpfade, plus HTTP-Route-Test über createApp + FakeNextCloud): 574→591 Tests, 51→54 Dateien. Davor MED-D-231 (B10/MED-OP-UX-8, Auftrag-Playbook-Fehler sichtbar): api/beratungsauftrag.test.ts um 2 Tests erweitert (Anlage mit scheiterndem Playbook → playbookFehler:true + Auftrag bleibt persistiert + PII-armes auftrag.playbook_fehler-Audit; Statuswechsel mit scheiterndem Status-Playbook → playbookFehler:true + Wechsel gelingt): 572→574 (keine neue Datei). Davor MED-D-227 (Akten-Cockpit Slice B, In-Person-Signatur): api/signatur.test.ts netto +1 — der Vor-Ort-Abschluss-Test prüft jetzt Dokument→erhalten (statt unterschrieben) + Onboarding-Item unangetastet + Audit signatur.vor_ort_abgeschlossen, plus ein neuer Idempotenz-Test (571→572, keine neue Datei). Davor MED-D-223 (Akten-Cockpit Slice A, AD-008 DocumentRow-Zustandsmaschine): api/signatur.test.ts um 8 Tests erweitert (Service: erinnern best-effort+Audit / unbekannt→null; signiertHochladen Dokument→erhalten+Scan-Ablage+Audit / unbekannt→null; HTTP: POST .../erinnern 200/404, POST .../signiert-hochladen 200/404): 563→571 Tests (51 Dateien, keine neue Datei). Davor MED-D-220 (DocuSign-HTTP-Adapter): neue signatur/docusign.test.ts (10 Tests gegen gemocktes globalThis.fetch + real generierten PKCS#8-Schlüssel: reine Helfer mappeStatus/parseWebhook/verifyDocuSignHmac/docuSignKonfigAusEnv-Dormancy; Adapter — anfordern erstellt Envelope mit documentBase64+Signer+eventNotification, wirft ohne Bytes/Unterzeichner, status lädt die kombinierte signierte Fassung, kein abschliessenDemo; SignaturService-Integration — Ablage-Bytes + Mandanten-E-Mail werden an den echten Provider durchgereicht, vorOrtAbschluss → 409/nicht_verfuegbar): 553→571 Tests, 50→51 Dateien. Davor MED-D-204 (UX-Welle 10 Slice 2, B15 Playbook-Feedback): api/app.test.ts um 1 Test erweitert (erfolgreicher Lifecycle-Wechsel liefert playbookFehler:false) + bestehender Playbook-Fehler-Test um die playbookFehler:true-Antwort-Assertion ergänzt: 552→553 Tests. Davor MED-D-198 (Akten-Cockpit S5, Unterschriften-Assistent): api/signatur.test.ts um 5 Tests erweitert (Vor-Ort-/Demo-Abschluss inkl. Write-back R4→R3→R2; unbekannter Vorgang → null; Provider ohne Demo-Seam → nicht_verfuegbar (kein Fälschen echter Vorgänge); HTTP POST .../vor-ort-abschluss 200/404): 547→552 Tests. Davor MED-D-195 (Akten-Cockpit S3a, 1:n-Bankkonten): neue api/bankkonto.test.ts (12 Tests: HTTP-CRUD inkl. IBAN-Normalisierung + Archivieren + 400-Validierung + 404; Backfill-Idempotenz, Crash-Teillauf, Nebenläufigkeit, identische-IBAN-kein-Dedup, leere-Felder-kein-Konto, UNIQUE-quellSchluessel-Ablehnung): 535→547 Tests, 49→50 Dateien. Davor MED-D-179 (Stammdaten-Versionierung, Slice 1): neue domain/stammdaten-version.test.ts (5 Tests: snapshotVon/snapshotGleich, Hash-Ketten-Bau + Verify, Manipulations-Erkennung, feldHistorie) + neue api/stammdaten-versionierung.test.ts (7 Tests: v1-Seed bei createMandant, v2 bei Feldänderung mit intakter Kette + Herkunft, kein Rausch-Bump bei reiner lifecycle-/ verantwortlich-Änderung, self_service-Herkunft, idempotenter Backfill, HTTP-Route + Berater-PATCH-Version): 523→535 Tests, 47→49 Dateien. Davor MED-D-177 (CodeRabbit-Nachlese zu MED-D-176, RBAC-Sicherheitsfund): rbac.test.ts +1 Test (By-ID-Wiedervorlage-Routen für fremden Mandanten → 404, für eigenen → 200- Regression, erledigen + wieder öffnen): 522→523 Tests. Davor MED-D-174 (CodeRabbit-Nachlese zu MED-D-172, RBAC-Sicherheitsfund): timer.test.ts +3 Tests (neue RBAC-Describe-Gruppe — /api/timer/start/buchen für fremden Mandanten → 404, für eigenen → 201-Regression): 519→522 Tests. Davor MED-D-172 (Aktiver Timer, ein Timer je Benutzer statt je Browser-Tab, OP-TIME-3): neue api/timer.test.ts mit 25 Tests (Service-Ebene: Start/ Konflikt/Idempotenz/Pause/Fortsetzen/Korrigieren/Buchen mit und ohne weiterlaufen/Verwerfen; HTTP-Ebene: alle /api/timer*-Routen inkl. Validierung + 409-Konfliktfälle): 494→519 Tests, 46→47 Dateien. Davor MED-D-158 (CodeRabbit-Nachlese zu MED-D-157: leistungszeit.test.ts +1 Test für missgebildeten auftragRef-Typ, z. B. Zahl/Objekt statt Text/null): 493→494 Tests. Davor MED-D-157 (Zeiterfassungs-Mandat-Zuordnung: leistungszeit.test.ts +2 Tests für erfassen() mit auftragRef + HTTP-Validierung unbekannter/fremder Aufträge): 491→493 Tests. Davor MED-D-152 (CodeRabbit-Nachlese zu MED-D-151: app.test.ts +1 Test für Best-effort-Playbook-Fehlerbehandlung in service.ts#aktualisieren()): 490→491 Tests. Davor MED-D-151 (PROZESS_PHASE retiriert, domain/phasen.test.ts → neue domain/lifecycle.test.ts, Guard-Matrix + Playbook-Katalog; beratungsauftrag.test.ts +2 Tests für das neue Auftrags-Status-Playbook, Idempotenz + Fehler-Isolation): 488→490 Tests). Davor MED-OP-VALID-1 (kanonischer Feld-Typ + KI-Formatvorschlag, immer weich): Slice 1 (MED-D-146) +18 Tests (feld-typ.test.ts, regel-vorschlag.test.ts, erweiterte ausfuellformular.test.ts); Slice 2 (MED-D-147, Praxis-PLZ im App-internen Formular) +2 Tests in praxis.test.ts; Slice 3 (MED-D-149, PLZ↔Ort-Plausibilität via OpenPLZ) +13 Tests (openplz.test.ts, fake.test.ts, erweiterte praxis.test.ts); Slice 4 (MED-D-150, Onboarding-Formular) +5 Tests (erweiterte onboarding-formular.test.ts + stammdaten-uebernahme.test.ts). Pflege (verbindlich): je PR mitführen — neue/entfernte Testdateien und die Gesamtzahl aktualisieren (analog CHANGELOG.md / Feature-Liste.md).

0.47.0 (D-49, Live-Zeiterfassung auf der Akte): UI (Timer/Guard/Register in mandant-detail.component.ts + zeiterfassung.guard.ts) ist clientseitig → Absicherung über den Angular-Produktions-Build (ng build). Die admin-konfigurierbaren Parameter (Nachfrage-Schwelle + Buchungs-Rundung) und die Buchungs-Rundungsregel (buchungsminuten) sind server-seitig in domain/einstellungen.ts getestet: +3 Vitest in einstellungen.test.ts (Schwelle/Rundung + buchungsminuten).

Was läuft wo?

EbeneWerkzeugUmfangAusführen
Server (server/)Vitest + tsc692 Unit-/HTTP-Tests (57 Dateien)npm test · npm run typecheck
Client (client/)Angular Produktions-BuildKompiliert die gesamte UI (Typprüfung inkl.)npm run build
Doku-Konsistenzscripts/check-doc-consistency.shtote Links, Log-Hygiene, version.ts↔Dokubash scripts/check-doc-consistency.sh
Doku-SiteDocusaurus-Buildrendert docs/ (Broken-Link-Check)cd docs-site && npm run build

Der Client hat bewusst (noch) keine Unit-Tests — die Absicherung ist der strikte Produktions-Build (schlägt bei Template-/Typfehlern fehl). Kandidat für später: Component-Tests der Kern-Flows.

Server-Tests im Detail

Domäne — reine Funktionen (voll deterministisch)

DateiTestsDeckt ab
domain/progress.test.ts7Abgeleiteter Onboarding-Fortschritt + offene Pflicht-Items (G-2)
domain/onboarding-vorlagen.test.ts3MED-D-70: Onboarding-Pflicht-Nachweise je Objektart (Einwilligung·Personalausweis·Kundenfragebogen·SEPA·Dienstleistungsauftrag + IBAN privat) + getrennte private/Entitäts-IBAN
domain/iban.test.ts4MED-D-71: IBAN-Normalisierung + ISO-7064-Mod-97-Validierung (DACH-Längen) — erster Baustein MED-OP-VALID-1
adressen/openplz.test.ts6MED-D-149/MED-OP-VALID-1(b): OpenPLZ-PLZ↔Ort-Plausibilisierung (G-1, echter Adapter) — DACH-Reihenfolge DE→AT→CH, Vorschlag bei abweichendem Ort, immer weich (unbekannte PLZ/Netzwerk-/Timeout-Fehler → „nicht prüfbar", nie ein Blocker).
adressen/fake.test.ts3MED-D-149: deterministischer PLZ↔Ort-Fake (Test/Dev, kein Netz) — plausibel/unplausibel+Vorschlag/unbekannt, erfüllt den PlzOrtProvider-Vertrag.
domain/feld-typ.test.ts13MED-D-146 (G-8 Data Dictionary, MED-OP-VALID-1 Slice 1): kanonischer ValidierungsArt (text/email/tel/select/zahl/datum/iban/plz/betrag) — pruefeWert immer weich (leer/Freitext nie ein Blocker), normalisiereWert (IBAN/Zahl-Komma→Punkt), formatVorschlag (DE-Datum→ISO, IBAN-Normalform, PLZ/Tel-Ziffernbereinigung, null wenn kein sicherer Vorschlag ableitbar).
api/lead.test.ts10MED-D-72/OP-LEAD-1: öffentliches Lead-Formular → Mandant als lead + Entität + Sichtungs-Wiedervorlage; Einwilligungs-/Pflichtfeld-Prüfung; Ingress-Scrubbing (G-6); öffentliche Routen trotz AUTH_ENFORCED. MED-D-106 (Slice C2a, Lead-Auto-Anlage): Kontext liefert nur buchbare Produkte (dexman ausgenommen); je gewähltem Interesse ein Auftrag (anbahnung, praxis-gebunden) + Playbook + Audit-Zählung; unbekannte/nicht-buchbare/doppelte Interesse-Werte verworfen (G-6); ohne Auftrags-Dienst keine Aufträge (Regression); Route reicht interesse durch (HTTP e2e)
domain/lifecycle.test.ts8MED-D-151 (löst domain/phasen.test.ts ab): Lifecycle-Guard-Matrix — onboarding→aktiv blockiert bei offenem Onboarding bzw. offenen Unterschriften (einzeln + kombiniert), frei bei beidem erfüllt; lead→aktiv (Guard gilt direkt, kollabierte Zwischenstufen); lead→onboarding immer frei; →ruhend/→archiviert + Reaktivierung ruhend→aktiv immer frei (kein Re-Guard); Same-Status-No-Op; LIFECYCLE_SPINE/spineIndex; reduzierter Playbook-Katalog (nachfassen@onboarding, jahrescheck@aktiv) + Ref-Format.
domain/audit.test.ts4SHA-256-Hash-Kette + Tamper-Erkennung (G-4)
domain/zugriff.test.ts3D-50/RISK-27: Zugriffs-Whitelist — zugriffAktion() = <entitaet>.eingesehen, einheitliches Suffix (G-3), nur die festgelegten sensiblen Entitäten (keine Listen/Cockpit)
domain/ablage.test.ts10NextCloud-Ablagepfad-Logik (R3/R9) + Anfangsbuchstaben-Bucket (D-31) + Upload-Ordner uploadOrdner/dokumentIdAusUploadPfad (Webhook, MED-D-55) + konfigurierbare Sandbox-Wurzel setzeBasisOrdner/basisOrdner (MED-D-56).
domain/beratung-themen.test.ts4Vollständigkeitsprüfung der Beratungsdoku (R6)
domain/signatur-niveau.test.ts4eIDAS-Niveau-Matrix je Dokumenttyp (R4, RISK-2)
domain/mandant-name.test.ts4D-31: Anzeigename-Ableitung "Nachname Titel Vorname" aus R1-F08..F10; Fallback auf Einrichtungs-/Freitextname; Trim/leere Teile.
domain/aktenreife.test.ts10D-41: Strukturierte Aktenreife (True North #3) — reif nur ohne blockierende Punkte, Score-Heuristik (100 − 25/10/4, Klemmung 0), Stufe/Schwere je Kategorie, offene Wiedervorlage = hinweis (blockiert nicht), jeStufe-Statusableitung + Blocker-Zählung, alle 3 Lifecycle-Spine-Stufen abgedeckt. MED-D-80: jeStufe.hinweis zählt offene Wiedervorlagen je Stufe (WV-Badge, MED-OP-REG-1). MED-D-151: phasestufe/jePhasejeStufe umgestellt (löst PROZESS_PHASE ab); neuer Test "ruhend/archiviert (off-spine) fallen auf die letzte Spine-Position zurück".
domain/naechste-schritte.test.ts6D-46/OP-NEXTSTEP-1: abgeleitete "nächste Schritte" (G-2) — leer wenn vollständig, "Onboarding starten" vs. "nachfassen", ein Schritt je Kategorie (Dokument/Beratung/Unterschrift/Wiedervorlage), Sortierung blockierend vor hinweis, Wiedervorlage = reiner Hinweis.
domain/deploy-status.test.ts5D-47 (Variante B): defensiver Parser des deploy_status-Werts — null bei fehlend/ungültig/Typfehler, gültiger Erfolgs-Status, runUrl optional, unbekannter Status → failure (fail-safe), failure/cancelled erkannt.
pdf/pdf-dokument.test.ts3OP-DOCGEN-1: Blockmodell → PDF (pdf-lib) — Magic Bytes/Metadaten/deterministisches Datum, Mehrseiten-Umbruch, WinAnsi-Sanitizer
domain/dentmarking-kennzahlen.test.ts9Dentmarking T3: 18 Kennzahlen, Formel-Varianten alt/korrigiert (B-1..B-3/OP-DM-1), Benchmarks je Praxistyp, Potential/Mehrgewinn (inkl. Beispielrechnung Excel-Logik §11), Nenner-Guards

API / HTTP — Hono-App gegen In-Memory-Repo (MemoryRepo)

DateiTestsDeckt ab
api/app.test.ts46Kern-API: Health · Mandant (UC-1, natürliche Person ohne Typ D-45; optionale E-Mail R1-F12 + Format-Validierung) · Cockpit (UC-6, inkl. auftragStatusZusammenfassung fürs Board, MED-D-151) · Betreuungsmodell (R1-F11: Default/Cockpit/PATCH+Audit/Validierung) · aggregiertes Cockpit (/offene-punkte) · Aktenreife (Detail + Leitstand-Aggregat /api/aktenreife, OP-AKTE-1) · Lifecycle-Guard (PATCH /api/mandanten/:id lifecycleStatus, MED-D-151, löst /phase ab: 409 bei offenem Onboarding/Unterschriften, frei bei ruhend/archiviert, Playbook-Wiedervorlage bei Eintritt) · Playbooks (admin-editierbar, auf 2 Lifecycle-Stufen reduziert)
api/beratungsdoku.test.ts15R6 Beratungsdoku inkl. Pflichtangaben (F11–F20) + Verzicht
api/wiedervorlage.test.ts23R5 Wiedervorlagen + Delegation/Zuweisung (OP-WV-1) + Sammelaktionen (P3b, teilfehler-tolerant) + D-42: nachträglich bearbeiten (Betreff/Fälligkeit, Erinnerung reaktiviert) + append-only Kommentare (Autor/Zeitstempel, neueste zuerst, PII-armes Audit, Anzeige an der WV im Detail); MED-OP-WV-2: createWiedervorlage idempotent bei gleichem quelleRef (Race-Simulation ohne externen Check, spiegelt den D1-Unique-Index)
api/bankkonto.test.ts12MED-D-195 (Akten-Cockpit S3a, R1-F33): 1:n-Bankkonto je Mandant (HTTP) — CRUD + mandantScopeGuard, Defaults (pruefstatus=neu/sepaGueltig=false/quelle=manuell), IBAN-Normalisierung, PATCH Prüfstatus+SEPA → archivieren statt löschen (append-only, G-4), Validierung (400) + unbekannter Mandant/Konto (404).
api/praxis.test.ts15R1 Praxis/Gesellschaft (D-27/D-45): 0..n je Mandant, Objektart (praxis/mvz/gesellschaft) + Default/PATCH/Validierung, Geschäftsjahr-Beginn, Audit, HTTP-Validierung; MED-D-87: strukturierte Anschrift strasse/plz/ort (Wertobjekt, G-8) — anlegen getrimmt + PATCH (leer → null)
api/dentmarking.test.ts31Dentmarking T2–T7: Fragenkatalog v1 (111 Felder, Alt-Spalten-Mapping, Zahl-Parser), Erhebung-Split zahl/text, Import-Idempotenz (B-5), GJ-Upsert, PII-armes Audit, Kennzahlen-Endpoint, Gutachten-Beleg (Fassung + R3-Referenz, keine-Daten-Guard/409), Befunde → idempotente Wiedervorlagen, öffentlicher Fragebogen T5 (Einladungs-Token, Einwilligungs-Pflicht, Einmal-Einreichung, Ablauf), T6/DM-9: Zwischenspeichern/Rehydrieren des Entwurfs + Score + Feld-Metadaten im Kontext (Service + HTTP-PUT), Markierungen an der Erhebung (kein Jahreswert), T7/DM-9: TTL aus Tenant-Einstellung + Cap 30, Ablauf-Hook (markiert/benachrichtigt idempotent, Entwurf bleibt), Statistik/Completion-Trichter, Einstellungen-API (GET/PUT admin, geklemmt)
domain/einstellungen.test.ts6Tenant-Einstellungen — Fragebogen-TTL Default 30/Cap 30, Normalisierung/Klemmung, effektive Werte; Zeiterfassung (D-49): Schwelle Default 1/Klemmung 0..240, Rundung Default 0/Klemmung 0..60, buchungsminuten (Aufrunden auf Vielfache · kaufmännisch bei 0 · 0/negativ→0)
domain/dentmarking-score.test.ts8Dentmarking T6/DM-9: Katalog-Metadaten (Wichtigkeits-Klassen, Markierbarkeit, Wertebereich Behandler 1–500, Struktur-Typ Öffnungszeiten), Ausfüll-Score (leer=0 / voll=100, Gewichtung essenziell>nice, Markierungen zählen als "offen markiert" nicht als Wert, leere Werte)
ablage/r2.test.ts10Geschlossene Ablage V2 "R2 + App-UI" (MED-D-54/MED-D-58): R2Ablage erfüllt den NextCloudClient-Vertrag (ensureOrdner/exists/put/get, FakeR2Bucket ohne Netz), put() sichert Vorfassungen unter _versionen/… (verlustfrei), Upload-Link zeigt auf die App-Route, Token-Auflösung (unbekannt/abgelaufen → null, Pfad-Tricks abgewiesen), Upload pfad-sicher benannt, Invariante "nie löschen" (keine delete-Methode, MED-D-56); HTTP end-to-end: provisionieren → Upload-Link → Formular-GET → multipart-POST → erhalten → Datei-Abruf; hochladen/abgleich unverändert über den R2-Vertrag
api/ausfuellformular.test.ts37MED-OP-VALID-1/MED-D-146: pruefeAlleWerte ist immer weich (Freitext/leer nie ein Hinweis, nur echte Format-Ausreißer); kontext() liefert hinweise/vorschlaege je Feld (gültiges-aber-nicht-kanonisches DE-Datum bekommt nur einen Vorschlag, ungültige IBAN nur einen Hinweis ohne Vorschlag, Freitext bleibt unangetastet, der Rohwert wird nie automatisch überschrieben). Ausfüll-Formulare (MED-D-63): Domäne (Festwerte aus Stammsatz, Pflichtfeld-Prüfung, Ingress-Scrubbing G-6, deterministische Beleg-Nr.) + Service (erstellen/kontext/speichern/einreichen, Beleg-PDF-Ablage, Idempotenz zweite Einreichung, ohne Ablage rein datenseitig) + HTTP-Fluss (Vorlagen-Katalog, erstellen → PUT ergänzen → einreichen, Pflichtfeld-400, unbekanntes Token-404); MED-D-67: Wichtigkeit 3-stufig, Markierungen "weiß nicht"/"nachgeliefert" (erfüllen Pflichtfeld), Nachlieferungs-Wiedervorlage idempotent + Audit; MED-D-84: Stammdaten-Rückübernahme (Vorschlag nur für eingereichte, Diff-Bildung, uebernehmen schreibt nur ausgewählte+gültige Felder + Audit PII-arm, idempotent, mandant-scoped, ungültige IBAN nicht übernommen; HTTP Vorschlag→übernehmen→idempotent; MED-D-86: Anschrift strukturiert strasse/plz/ort → verlustfreie 1:1-Übernahme); MED-D-89: Feld-Sichtung + Korrektur-Schleife (feldSichten nur eingereicht + nur gelieferte Felder, Ablehnung ohne/mit Grund + Audit PII-arm (kein Grund), sichtungAbschliessen verlangt Vollständigkeit, alles akzeptiert → bleibt eingereicht + Bestätigungs-Feedback + WV erledigt, Ablehnung → Korrektur-Schleife (nur abgelehntes Feld offen, akzeptiertes gesperrt, Re-Einreichung setzt Entscheidung zurück + reaktiviert WV), mandant-scoped)
domain/stammdaten-version.test.ts5MED-D-179 (Stammdaten-Versionierung, G-4): snapshotVon zieht nur die versionierten Felder (kein betreuungsmodell/lifecycleStatus/verantwortlich); snapshotGleich (null≡'' , reine lifecycle-Änderung = gleich); baueNaechsteVersion (v1=GENESIS, Folgeversion kettet + zählt hoch); pruefeVersionsKette (intakt ok, manipulierter Feldwert bricht bei Position); feldHistorie (fasst unveränderte Folgeversionen zusammen, setzt gültig-bis).
domain/stammdaten-uebernahme.test.ts12MED-D-84 (Formular → Stammdaten): reine Diff-/Validierungslogik — Ausfüll-Formular-Felder → Stammdaten-Ziele, IBAN-Mod-97/Datum-Format-Gültigkeit (ungültig markiert aber vorgeschlagen), Idempotenz (kein Vorschlag bei Gleichstand), Normalisierung, leere/unbekannte Werte übersprungen, bauePatch überträgt nur Zielfelder. pruefeWert/normalisiereWert/ValidierungsArt sind seit MED-D-146 Re-Exporte aus domain/feld-typ.ts (G-8) — Importpfad hier unverändert.
domain/feld-sichtung.test.ts8MED-D-89/MED-OP-FORM-4 (Feld-Sichtung, reine Domäne): zuSichtendeFelder (nicht-leer, unmarkiert, sortiert), offene/vollständig gesichtet (leere Menge ≠ vollständig), setzeEntscheid (Ablehnung verlangt Grund, Akzeptanz verwirft Grund), abgelehnte/akzeptierte Felder, entscheidungenZuruecksetzen (Korrektur-Schleife), feedbackNachricht (Ablehnung listet Grund + Korrektur-Link / alles akzeptiert = Bestätigung ohne Link).
api/formular-vorgaenge.test.ts15Fragebogen-/Formular-Vorgänge (MED-D-67): Domäne (Katalog maxParallelOffen=1 je Typ, verallgemeinerter Ausfüllgrad Pflicht 2/optional 1 inkl. Whitespace/fremde Schlüssel/leere Felder, SLA-Zustände ok→erinnerung_faellig→ueberfaellig, quelleRefs) + Parallel-Limit idempotent (Onboarding je Mandant · Ausfüll-Formular je Mandant×Vorlage; nach Abschluss/Ablauf wieder frei) + Öffnungs-Tracking geoeffnetAm genau einmal (alle drei Strecken, Audit) + Sichtungs-Wiedervorlage (idempotent, Verantwortliche:r, Quelle formular) + SLA-Cron (weiche Erinnerung einmalig + PII-arm empfaenger:null, harte Eskalation idempotente Wiedervorlage, abgelaufener Link ohne weiche Erinnerung, abgeschlossene Vorgänge neutral)
api/dokument.test.ts13R3 Dokumente (Provisionieren/Abgleich/Status) + Upload-Link (OCS File Drop) + Webhook (MED-D-55): uploadLinkErzeugen idempotent, webhookDateiErstellt (Pfad→Dokument, angefordert→erhalten, idempotent, fremde ignoriert), HTTP Upload-Link + Webhook (401 ohne Secret, 200 + Statuswechsel mit Secret) + Invariante "nie löschen" (Vertrag ohne delete/remove/rm, MED-D-56)
api/dokumentgenerierung.test.ts9R12 Dokumenten-Automatisierung
api/individualdokument.test.ts11MED-D-81: Freitext-Individualdokument — Block-/Benennungs-Engine, Service (erstellen→freigeben→erzeugen→R3-PDF; Bearbeiten nach Freigabe gesperrt; explizites eIDAS-Niveau schlägt Titel-Ableitung; zurUnterschrift lehnt bei erzeugt steckengebliebene Dokumente ab), HTTP-Vollfluss + 503/400.
api/beratungsauftrag.test.ts20MED-D-95/99 (Slice A): Katalog (Enum↔Eintrag, laufend nur Dauer-Produkte), Service (anlegen/Status mandant-scoped + produkt-validiert; person-oder-praxis; nicht buchbar/fremde Praxis abgelehnt; Audit-Events G-4). MED-D-101 (Slice B): Reife-Regeln (Beratungsdoku ab Beratung, Unterschrift, Gutachten-Produkt); Artefakt binden/lösen aktualisiert Reife; fremdes Artefakt abgelehnt; idempotente Retro-Ableitung (je Praxis EIN Auftrag abgeschlossen, Gutachten gebunden, zweiter Lauf 0/0); Bindungs-Besitz-Guard (Lösen/Umhängen fremder Auftrag → artefakt_anders_gebunden, CodeRabbit #135). MED-D-104 (Slice C1): Produkt-Playbook — jedes buchbare Produkt hat Standard-Aufgaben (dexman leer); anlegen erzeugt die Playbook-Wiedervorlagen (quelle=playbook, an den Auftrag gebunden). MED-D-105 (Fehler-Isolation, CodeRabbit #138): ein Repo-Fehler beim Playbook lässt die bereits persistierte Auftrags-Anlage nicht scheitern (kein Retry-Doppel) und wird PII-arm als auftrag.playbook_fehler auditiert. MED-D-151 (+2, Auftrags-Status-Playbook): →beratung/→unterschrift erzeugen je genau eine idempotente Wiedervorlage (an den Auftrag gebunden, wiederholter Aufruf kein Duplikat); ein Repo-Fehler beim Status-Playbook rollt den bereits persistierten Statuswechsel nicht zurück.
api/meeting.test.ts8MED-D-82 (R11): Gesprächsnotiz/Meeting-Doku — Domain (Bezeichnung, aufgabeQuelleRef normalisiert-stabil), Service (erstellen→Vorschlag→Übernahme legt Wiedervorlagen quelle='aufgabe' an, delegiert; idempotent; ohne Frist sofort fällig; bearbeiten lehnt leeren Titel ab), HTTP-Vollfluss (400 bei ungültiger Art; Aufgabe im WV-Register sichtbar = eine Wahrheit).
ki/gespraechs-auswerter.test.ts4MED-D-82: Regel-Auswerter (Fake-Seam) — parst "Aufgabe:"/Checkbox-Zeilen, extrahiert @verantwortlich + ISO-/DE-Fälligkeit, ignoriert Prosa ohne Marker, liefert Haftungshinweis (RISK-14).
ki/regel-vorschlag.test.ts3MED-D-146 (MED-OP-VALID-1 Slice 1): RegelVorschlag (Fake-Seam analog RegelPruefer/RegelAuswerter) — schlägt bei erkennbarem Format-Fehler einen korrigierten Wert vor + trägt den Haftungshinweis (RISK-14), liefert vorschlag: null wenn kein sicherer Vorschlag ableitbar ist (z. B. E-Mail-Tippfehler), name identifiziert den Fake-Adapter.
api/leistungszeit.test.ts11R13 Leistungszeit + Übersicht (OP-TIME-1) + Mandat-Zuordnung (MED-D-157/158)
api/timer.test.ts46MED-D-172 (Aktiver Timer, OP-TIME-3): Service-Ebene — Start (frisch/idempotent bei gleichem Mandanten/Konflikt bei anderem Mandanten, keine stille Überschreibung), Pause/Fortsetzen (Segment-Übernahme in basisMs, idempotent), Korrigieren (überschreibt + pausiert), Buchen (mit weiterlaufen true → frischer Timer läuft weiter; false → Timer beendet, Verlassen-Flow), Verwerfen, Fehlercodes (kein_timer/anderer_mandant); zwei Benutzer haben unabhängige Timer. HTTP-Ebene: alle /api/timer*-Routen inkl. Validierung (400) + 409-Konfliktfälle. MED-D-174 (CodeRabbit-Nachlese, RBAC-Sicherheitsfund): eigene RBAC-Describe-Gruppe — /api/timer/start/buchen für einen fremden (nicht sichtbaren) Mandanten → 404 kein Existenz-Leak, für den eigenen → 201-Regression.
api/signatur.test.ts23R4 Signatur-Vorgänge (inkl. AD-006 Empfänger-Override + MED-D-244 Dokument-Scope-Guard)
api/audit.test.ts10R8 Audit-API + Integritätsprüfung (/verify); D-50 Zugriffs-Protokollierung: Akte öffnen → mandant.eingesehen (PII-arm details={}, Kette intakt), Beratungsdoku-Lesen → beratungsdoku.eingesehen (Gesundheitsbogen R6-F18), Datei-Abruf → dokument.eingesehen, Listen/Cockpit erzeugen kein .eingesehen (G-6)
api/onboarding-formular.test.ts16R2 Self-Service-Onboarding (öffentlicher Formular-Flow) + MED-D-68: Zwischenspeichern/Rehydrieren des Entwurfs + Fertigstellungsgrad (Service + HTTP-PUT, Ingress-Scrubbing)

Auth

DateiTestsDeckt ab
auth/auth.test.ts9Identität (Cloudflare Access), RBAC, Bootstrap-Admin, AUTH_ENFORCED (R7/OP-AUTH-1)
domain/rechte.test.ts8Rechte-Matrix darf(rolle, faehigkeit) je Rolle + Mandant-Scope darfMandantSehen (null/eigen/fremd, whitespace-normalisiert), D-48; fragebogen_statistik_sehen admin-exklusiv (OP-DM-9/MED-D-63)
api/stammdaten-versionierung.test.ts7MED-D-179 (Stammdaten-Versionierung, Slice 1): createMandant seedet v1; updateMandant mit geändertem Feld erzeugt v2 (Kette intakt, Herkunft berater/actor übernommen); reine lifecycleStatus/verantwortlich-Änderung erzeugt keine neue Version (kein Rausch-Bump); self_service-Herkunft; backfillStammdatenV1 idempotent (seedet nur fehlende); HTTP GET /api/mandanten/:id/stammdaten-versionen liefert die Kette aufsteigend; Berater-PATCH erzeugt eine berater-Version mit korrektem Actor/Snapshot.
api/rbac.test.ts14RBAC je Mandant HTTP: Flag aus/an, Scope der Aggregate (/api/mandanten, /offene-punkte, /aktenreife), Detail-Guard (Fremd→404), Zuweisung (200/400/403 + Audit), /api/benutzer/zuweisbar-Guard, Rückwärtskompatibilität, D-48; /api/fragebogen/statistik admin-gated 403/200 (OP-DM-9/MED-D-63); MED-OP-AUTH-2: By-ID-Mandant-Scope-Guard für Meetings/Beratungsdoku/Individualdokumente (fremd→404, eigen→200/201, je 1 Test — vor der Implementierung gegengeprüft: schlägt ohne den Fix fehl)

Worker-Auslieferung (Routing-Weiche des Workers)

DateiTestsDeckt ab
serving.test.ts12Regressionsschutz zum Public-Link-Vorfall (OP-ONBOARD-1): zielIstAssets leitet /oeffentlich* (Onboarding-/Fragebogen-Daten) + /api* an die App statt an die SPA-Assets (inkl. Pfad-Grenze gegen Präfix-Kollisionen wie /apiary); öffentliche Seiten (/fragebogen/:token, /onboarding/:token) + statische Dateien an die Assets. istSpaNavigation = SPA-Fallback nur für Dokument-Navigationen (kein Datei-Suffix). spaFallback = Fallback liefert index.html als 200, nie als Redirect (Cloudflares Asset-Layer beantwortet explizites /index.html per html_handling-Default mit 307 → / → Access-Loginwand); Fakes decken beide html_handling-Modi ab. echterFaviconPfad (MED-D-78): mappt /oeffentlich/favicon.svg/.png auf die echten Asset-Pfade — Favicon läuft über den bestehenden Access-Bypass, alle anderen Pfade → null. (Die Cloudflare-Access-Konfiguration selbst ist Infrastruktur und nicht per Test abdeckbar.)

CI-Pipeline (.github/workflows/ci.yml)

Ein changes-Vorabjob (dorny/paths-filter) erkennt die geänderten Bereiche; die schweren Jobs laufen nur bei betroffenem Bereich (Pfad-Filter, agents.md §6.6) und werden vom aggregierten CI Gate zusammengefasst (Merge nur bei grünem Gate; übersprungene Bereiche zählen als bestanden):

  1. Server (typecheck + test)tsc + Vitest. (nur wenn server/** geändert; auf main-Push immer)
  2. Client (build) — Angular-Prod-Build. (nur wenn client/** geändert; auf main-Push immer)
  3. Doku-Konsistenz — mechanischer Check (warnt, blockt nicht — Job selbst ist grün; läuft immer).
  4. Doku-Site (build) — Docusaurus inkl. Broken-Link-Check. (nur bei docs/**/docs-site/**/*.md; auf main-Push immer)

So überspringt ein Doku-only-PR Server/Client, ein Code-only-PR die Doku-Site — schnellere PR-Läufe. Installs laufen mit npm ci --prefer-offline --no-audit --fund=false, der Checkout ist auf PRs shallow (fetch-depth: 1, voll nur auf main-Push). CI Gate prüft, dass keiner der erforderlichen Jobs fehlgeschlagen ist (skipped ≠ failure). CodeRabbit-Reviews laufen separat und sind nicht-blockierend. Deploys (app-deploy.yml / docs-deploy.yml) laufen getrennt bei Push auf main. Runner: alle CI-Jobs pinnen auf das Label hot (persistenter self-hosted Runner — warmes ~/.npm, .angular/cache, clean: false greifen nur dort; ephemere Runner tragen ephemeral), D-38; Fork-PRs auf ubuntu-latest (Gate). Umschalten auf GitHub-Runner ohne Commit über die Repo-Variable CI_RUNNER (Details: Deploy.md §7b, RISK-24). Auto-Rerun (auto-rerun.yml, D-40): ein separater workflow_run-Workflow stößt bei einem fehlgeschlagenen CI-Lauf die fehlgeschlagenen Jobs einmalig neu an (run_attempt < 2) — heilt flakige hot-Runner-Läufe automatisch; abgebrochene Läufe und Fork-PRs sind ausgenommen (RISK-24), Deploys ebenfalls. Kein eigener Testlauf, sondern Selbstheilung der Pipeline.

Konventionen

  • Neue Tests beiden Wegen hinzufügen: lokal (npm test / npm run build) und damit automatisch in CI.
  • Domänenlogik als reine Funktionen testen; Dienste/Routen über die Hono-App gegen MemoryRepo (kein D1 im Test).
  • Zahlen in dieser Datei je PR aktualisieren (siehe Kopf).