diff --git a/docs/CUT-06-PRUEFPROTOKOLL.md b/docs/CUT-06-PRUEFPROTOKOLL.md index 50891fd3..7635e956 100644 --- a/docs/CUT-06-PRUEFPROTOKOLL.md +++ b/docs/CUT-06-PRUEFPROTOKOLL.md @@ -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. diff --git a/internal/parallelrun/parallelrun.go b/internal/parallelrun/parallelrun.go index 1f84d730..c60e9af4 100644 --- a/internal/parallelrun/parallelrun.go +++ b/internal/parallelrun/parallelrun.go @@ -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 { diff --git a/internal/parallelrun/parallelrun_test.go b/internal/parallelrun/parallelrun_test.go index 56892eeb..28e0965e 100644 --- a/internal/parallelrun/parallelrun_test.go +++ b/internal/parallelrun/parallelrun_test.go @@ -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) + } +}