package settings import ( "io" "log" "path/filepath" "testing" ) // R-704 (v0.278.0) — ClearUpdateHold clears the holds that belong to an INSTALL (update, crash-loop stop) and // never a restore hold (R-379), which stays operator-cleared. // COMPANION RED-PROOF: the pre-fix predicate (`h.Reason != HoldReasonUpdateFailed`) → the crash-loop case fails. func TestR704_ClearUpdateHoldClearsTheInstallsHoldsOnly(t *testing.T) { s, err := Load(filepath.Join(t.TempDir(), "settings.json"), log.New(io.Discard, "", 0)) if err != nil { t.Fatal(err) } for _, c := range []struct { reason string want bool }{{HoldReasonUpdateFailed, true}, {HoldReasonUnhealthyStop, true}, {HoldReasonRestoreFailed, false}} { if err := s.SetRestoreHold(RestoreHold{Stack: "app", At: "2026-09-13T19:56:32Z", Reason: c.reason}); err != nil { t.Fatal(err) } cleared, err := s.ClearUpdateHold("app") if err != nil { t.Fatal(err) } _, still := s.GetRestoreHold("app") if cleared != c.want || still == c.want { t.Fatalf("reason %q: cleared=%v still-held=%v, want cleared=%v", c.reason, cleared, still, c.want) } _, _ = s.ClearRestoreHold("app") } if cleared, _ := s.ClearUpdateHold("none"); cleared { t.Fatal("clearing an app with no hold reported a clear") } }