CUT-06: Shutdown() prueft offene Diskrepanzen erneut vor Abschaltung
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (pull_request) Canceled after 0s
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (push) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (push) Canceled after 0s
CI / Connect Security-Test-Gate (QA-01 AC4, Merge-Gate) (push) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (pull_request) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (push) Canceled after 0s
CI / Connect Security-Test-Gate (QA-01 AC4, Merge-Gate) (pull_request) Canceled after 0s
Supply-Chain-Scan (Go) / govulncheck (push) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (push) Canceled after 0s
CI / Web (dms-browser) (push) Canceled after 0s
CI / Web (pdf-viewer) (push) Canceled after 0s
CI / Web (shl) (push) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (pull_request) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (pull_request) Canceled after 0s
CI / Web (dms-browser) (pull_request) Canceled after 0s
CI / Web (pdf-viewer) (pull_request) Canceled after 0s
CI / Web (shl) (pull_request) Canceled after 0s
Core-Schnittstellen-Vertragstests / contract-tests (pull_request) Canceled after 0s
Supply-Chain-Scan (Go) / govulncheck (pull_request) Canceled after 0s
Supply-Chain-Scan (npm) / npm-audit (pull_request) Canceled after 0s
Core-Pflichttest-Gate / pflichttest-gate (pull_request) Canceled after 0s
RBAC-Rollenhierarchie-Pflichtgate / rbac-policy-invariant (pull_request) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (push) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (pull_request) Canceled after 0s

Behebt eine von unabhaengiger Verifikation gefundene Luecke: eine nach
ApproveShutdown neu gemeldete Diskrepanz konnte die Abschaltung bisher
nicht mehr aufhalten, obwohl Shutdown() behauptete, Frist und Freigabe
unabhaengig von ApproveShutdowns eigener Reihenfolge durchzusetzen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
patrickandClaude Sonnet 5 committed 2026-10-15 21:23:00 +02:00
1 parent 663b9874b9
commit 11abc5047a
3 files changed
+62

No files matched your search

+29
View File
@@ -143,3 +143,32 @@ auch tatsächlich jeden Zugriff sperrt. Der reale 90-Tage-Parallelbetrieb
gegen die echten Altsysteme sowie die echte zweite Freigabe bleiben, wie
oben beschrieben, ein separater operativer Schritt für Menschen mit echtem
Produktionszugriff.
## Nachtrag — Korrektur nach unabhängiger Verifikation
Eine unabhängige Verifikation hat einen echten Lücke gefunden: `Shutdown()`
prüfte zwar eigenständig Fristende und Existenz der zweiten Freigabe, aber
NICHT erneut auf offene Diskrepanzen. Szenario: `ApproveShutdown` erfolgt
korrekt ohne offene Diskrepanzen, danach wird eine neue Diskrepanz gemeldet
(`ReportDiscrepancy`, Status `open`) — `Shutdown()` hätte das ignoriert und
trotzdem abgeschaltet, obwohl Akzeptanzkriterium 3 genau das verhindern soll.
Behoben: `Shutdown()` prüft jetzt zusätzlich `OpenDiscrepancyCount` und
verweigert mit `ErrOpenDiscrepancies`, falls nach der Freigabe noch eine
offene Diskrepanz gemeldet wurde. Neuer Test
`TestShutdown_RefusedWhenDiscrepancyReportedAfterApproval` bildet genau
dieses Szenario nach und bestätigt die Ablehnung.
Erneuter Testlauf nach dem Fix (echte Postgres-DB, Host 204):
```
--- PASS: TestStartPeriod_LegacySystemReadOnlyNotWritable (0.72s)
--- PASS: TestShutdown_RefusedBeforeDeadline (0.69s)
--- PASS: TestApproveShutdown_RefusedWithOpenDiscrepancies (0.77s)
--- PASS: TestShutdown_LocksSystemCompletelyAfterApproval (0.74s)
--- PASS: TestShutdown_RefusedWhenDiscrepancyReportedAfterApproval (0.56s)
ok gitea.perlbach24.de/scripte/nexarch/internal/parallelrun 3.479s
ok gitea.perlbach24.de/scripte/nexarch/internal/rollbackrun 2.873s
```
`go build ./...` und `gofmt -l` auf den geänderten Dateien weiterhin sauber.
+7
View File
@@ -213,6 +213,13 @@ func Shutdown(ctx context.Context, store *Store, cluster *cutoverorchestrator.Cl
if _, err := store.LatestShutdownApproval(ctx, periodID); err != nil {
return err
}
open, err := store.OpenDiscrepancyCount(ctx, periodID)
if err != nil {
return err
}
if open > 0 {
return ErrOpenDiscrepancies
}
conn, err := pgx.Connect(ctx, cluster.AdminDSN())
if err != nil {
+26
View File
@@ -252,3 +252,29 @@ func TestShutdown_LocksSystemCompletelyAfterApproval(t *testing.T) {
t.Fatal("verbindung nach abschaltung haette fehlschlagen muessen")
}
}
func TestShutdown_RefusedWhenDiscrepancyReportedAfterApproval(t *testing.T) {
cluster := testCluster(t)
store := testStore(t)
ctx := context.Background()
dbName := setupLegacyDB(t, cluster)
cutoverAt := time.Now().Add(-100 * 24 * time.Hour)
periodID, err := store.StartPeriod(ctx, cluster, testTenantID, dbName, cutoverAt, 90*24*time.Hour)
if err != nil {
t.Fatalf("StartPeriod: %v", err)
}
after := time.Now()
if _, err := store.ApproveShutdown(ctx, periodID, "qa-leitung", "abschlussfreigabe", after); err != nil {
t.Fatalf("ApproveShutdown: %v", err)
}
if _, err := store.ReportDiscrepancy(ctx, periodID, "nach freigabe gemeldet"); err != nil {
t.Fatalf("ReportDiscrepancy: %v", err)
}
if err := Shutdown(ctx, store, cluster, periodID, after); !errors.Is(err, ErrOpenDiscrepancies) {
t.Fatalf("Shutdown nach einer erst nach der Freigabe gemeldeten Diskrepanz haette ErrOpenDiscrepancies liefern muessen, habe %v", err)
}
}