package main import ( "strings" "testing" "gitea.dooplex.hu/admin/felhom-controller/internal/settings" ) // R-1286: demo-hp's Kimai was held by the crash-loop guard (reason unhealthy_stop, 2026-10-14) and the boot sweep, the // start gate and `--restore-holds` all said „held after a failed restore whose rollback also failed" for 11 days. // Each reason now says what it is and what lifts it. // RED-PROOF: holdWhy back to the old two-way branch → the unhealthy_stop, restore_mixed and putback_pending cases fail. func TestR1286_EveryHoldReasonSaysWhatItIs(t *testing.T) { sett := holdTestSettings(t) g := driveStartGate{sett: sett} for _, tc := range []struct { hold settings.RestoreHold want string }{ {settings.RestoreHold{Stack: "kimai", At: "2026-10-14T09:06:31Z", Reason: settings.HoldReasonUnhealthyStop, UnhealthyKind: "crash_loop"}, "stopped by the box for a crash loop"}, {settings.RestoreHold{Stack: "a", At: "t", Reason: settings.HoldReasonUpdateFailed}, "failed update"}, {settings.RestoreHold{Stack: "b", At: "t", Reason: settings.HoldReasonRestoreMixed}, "off-site restore failed its database replay"}, {settings.RestoreHold{Stack: "c", At: "t", Reason: settings.HoldReasonPutBackPending}, "pre-restore copy is poured back"}, {settings.RestoreHold{Stack: "d", At: "t"}, "failed restore whose rollback also failed"}, } { if err := sett.SetRestoreHold(tc.hold); err != nil { t.Fatal(err) } ok, why := g.MayStart(tc.hold.Stack) if ok || !strings.Contains(why, tc.want) { t.Fatalf("%s (reason %q): the gate says %q, want it to contain %q", tc.hold.Stack, tc.hold.Reason, why, tc.want) } if tc.hold.Reason != "" && strings.Contains(why, "failed restore whose rollback also failed") { t.Fatalf("%s: a %q hold must not read as a failed restore: %q", tc.hold.Stack, tc.hold.Reason, why) } } }