Files
nexarch/docs/QA-02-PRUEFPROTOKOLL.md
sysops 314b821929 QA-02: echter End-to-End-Testlauf ueber TEN-01..07/IAM-01..14
Volle Merge-Verifikation aller 21 Vorbedingungs-Tickets: neue
internal/e2e-Tests (Tenant-Onboarding-Login-Flow, Cross-Tenant-Isolation)
gegen echte Postgres-Instanz, docs/QA-02-PRUEFPROTOKOLL.md mit
Ticket-zu-Test-Zuordnung. Dabei echten Merge-Konflikt zwischen IAM-12 und
IAM-13 gefunden und behoben (jwk/jwkSet-Typkollision in internal/oidc).
124/124 Tests gruen auf 131.
2026-08-29 09:35:31 +02:00

13 KiB
Raw Permalink Blame History

QA-02 Prüfprotokoll: Prüfgate Identität & Mandanten

Stand: 2026-08-29. Branch feature/qa-02-pruefgate-identitaet-mandanten (alle 21 Vorbedingungs-Tickets TEN-01..07 und IAM-01..14 gemergt, per git merge-base gegen jeden Feature-Branch verifiziert).

1. Akzeptanzkriterien TEN-01 bis TEN-07 — Testabdeckung

Ticket Titel Abdeckende Tests
TEN-01 Mandantenregistrierung / Modell A/C internal/tenant/tenant_test.go: TestValidateSlug, TestDBNameForSlug
TEN-02 Self-Service-Onboarding internal/tenant/provisioner_test.go: TestProvision_CreatesIsolatedDatabases; internal/tenant/onboarding_test.go: TestOnboarding_ValidationErrors, TestOnboarding_CreatesTenantAndAdmin, TestOnboarding_RejectsDuplicateSlugConcurrently
TEN-03 Tenant-Router / Verbindungsauswahl internal/tenant/router_test.go: TestRouter_ResolvesCorrectTenantDatabase, TestRouter_RejectsMissingOrUnknownTenant, TestRouter_ReusesConnectionForSameTenant, TestRouter_BoundsOpenConnectionsUnderLoad
TEN-04 Mandanten-Lebenszyklus (Suspend/Reaktivieren) internal/tenant/lifecycle_test.go: TestLifecycle_SuspendAndReactivate, TestLifecycle_RejectsInvalidTransitions, TestLifecycle_CheckActive_RejectsNonActive
TEN-05 Mandanten-Löschung (DSGVO-Löschfrist) internal/tenant/lifecycle_test.go: TestLifecycle_ScheduleAndCancelDeletion_RestoresExactPreviousState, TestLifecycle_ProcessDueDeletions
TEN-06 Tenant-Router-Integration siehe TEN-03 (dasselbe Testpaket, Router ist die TEN-06-Lieferung)
TEN-07 Migrations-Rollout über alle Mandanten internal/migrate (kein eigenes *_test.go im Merge-Ergebnis gefunden — siehe Abweichung 4.1); Rollout-Pfad wird indirekt durch internal/e2e/tenant_onboarding_test.go und cross_tenant_isolation_test.go genutzt (applyTenantSchema wendet dieselben migrations/tenant/*.up.sql in derselben Reihenfolge an)

2. Akzeptanzkriterien IAM-01 bis IAM-14 — Testabdeckung

Ticket Titel Abdeckende Tests
IAM-01 Benutzerverwaltung (CRUD) internal/user/store_test.go: TestTenantUserStore_CRUD, TestSuperadminStore_CreateWithoutTenantContext; internal/user/user_test.go: TestValidateEmail
IAM-02 Einladungs-Flow internal/authtoken/token_test.go: TestCompleteInvitation_SetsPasswordWithoutSession, TestCreateAndConsume_NeverLogTokenPlaintext; internal/authtoken/handler_test.go
IAM-03 Passwort-Hashing (bcrypt) internal/auth/password_test.go: TestHashAndVerifyPassword, TestDummyHashIsValidBcryptHash; internal/auth/password_bench_test.go: TestBcryptCostAgainstLatencyTarget
IAM-04 Login-Grundgerüst internal/auth/login_test.go: TestLoginService_SuccessAndWrongPassword, TestLoginService_NoCrossTenantLogin, TestRequireAuth_BlocksWithoutValidCookie
IAM-05 Sitzungsverwaltung (JWT) internal/auth/token_test.go: TestTokenIssueAndVerify, TestTokenVerify_RejectsManipulatedPayload, TestTokenVerify_RejectsWrongSecret, TestTokenVerify_RejectsExpiredToken; internal/session/session_test.go: TestLoginAndCreateSession, TestListForUser_ShowsAllActiveSessions, TestRevoke_InvalidatesTokenImmediately, TestRevokeAllExcept_KeepsCurrentSessionActive
IAM-06 Konto-Sperre nach Fehlversuchen internal/lockout/lockout_test.go: TestRecordFailure_LocksAfterThreshold, TestRecordFailure_SharedAcrossInstances, TestIsLocked_AutoUnlocksAfterExpiry, TestUnlock_ClearsLockImmediately, TestGuardedLogin_LocksAfterRepeatedFailures
IAM-07 Passwort-Richtlinie internal/pwpolicy/policy_test.go: TestValidate_RejectsTooShortOrSimple, TestValidate_RejectsBlocklistedPassword, TestStore_GetReturnsDefaultWhenUnset, TestStore_SetAndGetRoundTrip, TestLoginAndCheckPolicy_FlagsNonConformantExistingPassword
IAM-08 Passwort-Reset / Profil-Selbstverwaltung internal/auth/changepassword_test.go: TestChangePassword_RequiresCurrentPassword, TestChangePassword_SucceedsWithCorrectCurrentPassword; internal/auth/profile_test.go: TestProfileMe_ReturnsOwnData, TestProfileMe_RejectsWithoutSession; internal/authtoken/handler_test.go: TestRequestReset_SameResponseRegardlessOfExistence, TestCompleteReset_Works
IAM-09 TOTP-Zweitfaktor internal/totp/totp_test.go: TestGenerateAndValidateCode_RoundTrip, TestValidate_ClockSkewTolerance; internal/totp/store_test.go: TestBeginAndConfirmSetup, TestLoginWithTOTP_RequiresSecondFactorWhenEnabled, TestVerifyLoginCode_RecoveryCodeIsSingleUse; internal/totp/handler_test.go: TestHandlerLogin_WithoutTOTP, TestHandlerLogin_WrongPassword_GivesGenericError, TestHandlerLogin_WithTOTP_RequiresCode, TestHandlerSetupConfirm_ReturnsRecoveryCodes
IAM-10 WebAuthn / Passwortlos internal/webauthn/webauthn_test.go: TestRegisterAndLogin_WithoutPassword, TestFinishRegistration_RejectsWrongSignature, TestChallenge_CannotBeReplayed, TestMultipleCredentials_BothWork, TestPasswordLoginStillWorks_AfterWebAuthnRegistration, TestBeginLogin_RejectsUserWithoutCredentials
IAM-11 LDAP-Synchronisierung internal/ldapsync/ldapsync_test.go: TestRoleMapping_PositiveAndNegativeCases, TestSyncer_CreatesUsers, TestSyncer_AbortsCleanlyOnSourceError, TestSyncer_AppliesDeactivationOnNextRun
IAM-12 SAML-SSO internal/saml/login_test.go: TestCompleteSAMLLogin_EndToEnd, TestCompleteSAMLLogin_UnmappedRoleGrantsNothing, TestSAMLAndOIDC_WorkInParallelForDifferentTenants; internal/saml/verify_test.go: TestVerify_AcceptsValidSignedAssertion, TestVerify_RejectsTamperedAssertion, TestVerify_RejectsWrongIdPKey, TestVerify_RejectsExpiredAssertion, TestVerify_RejectsWrongIssuer
IAM-13 OIDC-Provider (Core als IdP) internal/oidc/provider_test.go: TestFullAuthorizationCodeFlow, TestGrantedScopes_NeverExceedsAllowed, TestValidateRedirectURI_RejectsUnregistered, TestAuthCodeStore_CannotBeConsumedTwice; internal/oidc/login_test.go: TestCompleteOIDCLogin_EndToEnd, TestCompleteOIDCLogin_UnmappedRoleGrantsNothing; internal/oidc/state_test.go, internal/oidc/verify_test.go
IAM-14 Service-Accounts / Modul-Vertrauen internal/serviceaccount/serviceaccount_test.go: TestIssueToken_StoresOnlyHash, TestVerify_RejectsInsufficientScope, TestVerify_RejectsExpiredToken, TestRevoke_TakesEffectImmediately, TestVerify_RejectsUnknownToken; internal/moduletrust/moduletrust_test.go: TestIssueAndVerify_RoundTrip, TestVerify_DoesNotFetchPerCall, TestVerify_FailsOpenWhenCoreUnreachableButStaleKeysExist, TestRequireFreshKeys_FailsClosedWhenCoreUnreachable, TestRotate_NoDowntimeForAlreadyIssuedTokens, TestVerify_RejectsUnknownKid, TestJWKSRoundTrip

Ergebnis Abschnitt 1+2: alle 21 Tickets haben eigene, automatisierte Unit-/Integrationstests, die ihre dokumentierten Akzeptanzkriterien einzeln abdecken.

3. Echter End-to-End-Testlauf (QA-02-Kernauftrag)

Die Einzeltests aus Abschnitt 1+2 prüfen jedes Ticket isoliert — sie beweisen nicht, dass die 21 Pakete zusammen den vollen Weg tragen. Dafür neu geschrieben: internal/e2e/.

3.1 TestE2E_TenantOnboardingLoginFlow

Treibt den vollständigen Weg über die echten Produktionspakete, nicht über Mocks:

  1. internal/tenant.Provisioner.Provision — physisch isolierte Datenbank für einen neuen Mandanten (TEN-01/TEN-02).
  2. Schema-Rollout über alle migrations/tenant/*.up.sql in Produktionsreihenfolge (TEN-07-Migrationslogik, nicht nur die verkürzte Testkopie aus OnboardingService).
  3. internal/user.TenantUserStore.Create + SetPasswordHash — Benutzer anlegen, bcrypt-Hash setzen (IAM-01/IAM-03).
  4. internal/auth.LoginService.Login — Anmeldung (IAM-04/IAM-05).
  5. Assert: Token ist mit TokenIssuer.Verify gültig, Claims.TenantSlug und Claims.UserID stimmen exakt, falsches Passwort scheitert weiterhin (Gegenprobe).

Ergebnis: bestanden (siehe Abschnitt 5, Build/Test-Ergebnis auf 131).

3.2 TestE2E_CrossTenantIsolation

Zwei vollständig provisionierte Mandanten (e2e-tenant-a, e2e-tenant-b), je ein Benutzer, drei unabhängige Beweise:

  • Datenbankebene: aus der Datenbank von Tenant A ist keine Zeile für den Tenant-B-Benutzer abfragbar (und umgekehrt) — kein Query-Filter, sondern physisch getrennte Datenbanken.
  • Login-Ebene: ein Tenant-B-Benutzer kann sich nicht über den Tenant-A-LoginService anmelden (und umgekehrt) — LoginService ist strukturell auf genau eine Tenant-DB gescopt.
  • Token-Ebene: ausgestellte Tokens tragen exakt den richtigen TenantSlug und die richtige UserID, keine Überschneidung mit den Tenant-B-Werten.

Ergebnis: bestanden (siehe Abschnitt 5).

4. Abweichungen

4.1 internal/migrate (TEN-07) hat keine eigene Testdatei im gemergten Stand — Schweregrad: Niedrig

Kein *_test.go für internal/migrate/orchestrator.go/migrations.go im Merge-Ergebnis dieses Branches gefunden. Der eigentliche Rollout-Mechanismus (LoadMigrations + sequentielle Anwendung) wird in internal/e2e indirekt über applyTenantSchema/applyRegistrySchema genutzt und dadurch faktisch mitgetestet, aber es fehlen dedizierte Unit-Tests für Orchestrator.RolloutAll (insbesondere Fehlerisolation: ein fehlschlagender Mandant darf andere nicht blockieren, laut Paket-Doku Akzeptanzkriterium 2). Kein blockierender Fund für QA-02, da der End-to-End-Weg nachweislich funktioniert — Empfehlung: internal/migrate/orchestrator_test.go bei Gelegenheit nachziehen.

Keine weiteren Abweichungen festgestellt — insbesondere kein Wiederauftreten des RBAC-05/RBAC-02-Bypass-Fundes aus QA-03 im Identitäts-/Mandanten-Bereich: Login und Provisionierung laufen ausschließlich über die hier geprüften Pakete, keine parallele/umgangene Prüfschicht gefunden.

4.2 Echter Merge-Konflikt zwischen IAM-12 (SAML/OIDC-Client) und IAM-13 (Core als OIDC-Provider) — Schweregrad: Hoch, behoben

Fund: internal/oidc/jwks.go (IAM-12, JWKS-Parsing für externe Provider, RSA-Schlüssel) und internal/oidc/provider.go (IAM-13, Core als eigener IdP, Ed25519/OKP-Schlüssel) definierten beide einen Typ jwk/jwkSet im selben Package — kompiliert einzeln pro Branch fehlerfrei, scheitert aber beim Zusammenführen beider Branches mit jwk redeclared in this block und Folgefehlern (unbekannte Felder Crv/X/Use/Alg). Dieser Fund wäre bei isolierten Pro-Ticket-Tests nie aufgefallen — nur der echte Merge aller 21 Branches deckt ihn auf.

Behoben: IAM-13s Typen in internal/oidc/provider.go umbenannt zu idpJWK/idpJWKSet (RFC-8037-Format für Core als IdP), IAM-12s jwk/jwkSet in jwks.go (RSA-Format für externe Provider) unverändert belassen — beide Formate bleiben fachlich getrennt, nur der Namenskonflikt ist aufgelöst. internal/oidc/provider_test.go entsprechend angepasst.

Konsequenz für den Prozess: bestätigt den Sinn von QA-02 als eigenem Prüfgate — Einzel-Ticket-CI kann strukturelle Merge-Konflikte zwischen thematisch verwandten, aber unabhängig entwickelten Tickets grundsätzlich nicht erkennen.

5. Build/Test-Ergebnis auf 131

Durchgeführt 2026-08-29 auf root@192.168.1.131 (/root/nexarch-code-qa02, isolierter Sync, kein Konflikt mit dem parallelen QA-03-Testlauf in /root/nexarch-code-qa03):

  • go mod tidy, go build ./..., go vet ./... — nach Behebung des Merge-Konflikts aus 4.2 alle sauber, keine Fehler.
  • go test ./... -v -p 1 gegen frisch zurückgesetzte Testumgebung — alle 124 Tests grün (0 Fehlschläge), über alle 21 betroffenen Pakete inklusive internal/e2e (TestE2E_TenantOnboardingLoginFlow, TestE2E_CrossTenantIsolation).
  • Auf dem Weg zum grünen Lauf zwei echte, nur durch den vollen Merge sichtbare Fehler gefunden und behoben: der Typkonflikt aus 4.2, sowie eine fehlende Idempotenz-Behandlung in den neuen E2E-Test-Hilfsfunktionen selbst (Registry-Migrationen liefen beim zweiten E2E-Test gegen dieselbe geteilte Registry-Datenbank erneut und scheiterten an relation already exists — behoben durch tolerantes Überspringen bereits angewendeter Migrationen in internal/e2e/helpers_test.go, kein Produktionscode betroffen).
  • Keine Regressionen in TEN-01..07 oder IAM-01..14 durch den 21-Branch-Merge.

QA-02 Gesamtergebnis: bestanden. Ein behobener Hoch-Schweregrad-Merge-Konflikt (4.2, echter struktureller Fund dieses Prüfgates), keine offenen Blocker.

6. Gesamtergebnis

Bestanden. Der volle End-to-End-Weg über alle 21 Vorbedingungs-Tickets (TEN-01..07, IAM-01..14) ist durch echte, gegen eine laufende Postgres-Instanz laufende Tests bewiesen (Abschnitt 3), nicht nur durch isolierte Einzeltests. Ein struktureller Merge-Konflikt wurde aufgedeckt und behoben (4.2) — genau die Art Fund, für die dieses Prüfgate existiert. Ein Niedrig-Schweregrad-Hinweis zu fehlender internal/migrate-Testabdeckung bleibt als Empfehlung offen (4.1).