package stacks import ( "fmt" "io" "log" "testing" "time" ) // R-1347, the consequence: an app whose last update ended held is not watched for a crash loop; once its hold is lifted by // a restore and the flag forgotten (backup.clearUpdateHoldAfterRestore → ForgetUpdateHeld) the same restarts trip the // watcher again. func TestR1347_TheWatcherWatchesAgainOnceTheFlagIsForgotten(t *testing.T) { m := &Manager{logger: log.New(io.Discard, "", 0), stacks: map[string]*Stack{ "nextcloud": {Name: "nextcloud", Deployed: true, Containers: []ContainerInfo{{Name: "nextcloud-web-1"}}}, }} m.markUpdateHeld("nextcloud") count := int64(0) m.execFn = func(string, ...string) (string, error) { return fmt.Sprintf("/nextcloud-web-1|%d\n", count), nil } t0 := time.Date(2026, 11, 2, 4, 20, 0, 0, time.UTC) sample := func(from int) []UnhealthyVerdict { var got []UnhealthyVerdict for i := from; i < from+12 && len(got) == 0; i++ { count += 3 got = m.ObserveUnhealthy(t0.Add(time.Duration(i)*time.Minute), nil) } return got } if got := sample(0); len(got) != 0 { t.Fatalf("control: an app whose update ended held must not be judged: %+v", got) } if !m.ForgetUpdateHeld("nextcloud") { t.Fatal("the flag was set, so it must be forgotten") } got := sample(20) if len(got) != 1 || got[0].Stack != "nextcloud" || got[0].Kind != UnhealthyCrashLoop { t.Fatalf("after the restore lifted the hold the watcher must watch the app again; verdicts %+v", got) } }