package osupdates import ( "testing" "time" "gitea.dooplex.hu/admin/felhom-hub/internal/store" ) func bundleReport(t *testing.T, f *fix, host, version, sha string) { t.Helper() body := `{"system":{"pve_version":"pve-manager/9.2.2/x","config_bundle":{"version":"` + version + `","bundle_sha256":"` + sha + `"}}}` h, err := f.s.Store.GetHost(host) if err != nil || h == nil { t.Fatalf("no host %s", host) } if err := f.s.Store.SaveHostReport(host, h.CustomerID, []byte(body), store.HostReportDenorm{AgentVersion: "0.143.0"}); err != nil { t.Fatal(err) } } func vouch(t *testing.T, f *fix, sha string) { t.Helper() if err := f.s.Store.SetArtifactManifest(store.ArtifactManifest{AgentVersion: "0.143.0", AgentSHA256: "x", BundleSHA256: sha}); err != nil { t.Fatal(err) } } func count(sent []string, typ string) int { n := 0 for _, s := range sent { if s == typ { n++ } } return n } // R-840: a box behind the vouched bundle is told to the operator after 7 days — not before, once, and the clock // restarts when the box catches up. "none" (no bundle ever) counts; "unknown" never does. func TestBundleAlarm_AfterSevenDaysBehind(t *testing.T) { f := newFix(t) vouch(t, f, "new") bundleReport(t, f, "cust1", "none", "") sent, _ := f.s.Alarms() if count(sent, EventBundleBehind) != 0 { t.Fatal("alarm on the first sight") } f.now = f.now.Add(6 * 24 * time.Hour) if sent, _ = f.s.Alarms(); count(sent, EventBundleBehind) != 0 { t.Fatal("alarm before 7 days") } f.now = f.now.Add(25 * time.Hour) if sent, _ = f.s.Alarms(); count(sent, EventBundleBehind) != 1 { t.Fatalf("no alarm after 7 days: %v", sent) } if sent, _ = f.s.Alarms(); count(sent, EventBundleBehind) != 0 { t.Fatal("the alarm must not repeat at once") } bundleReport(t, f, "cust1", "0.143.0", "new") f.s.Alarms() if !f.s.Store.BundleBehindSince("cust1").IsZero() { t.Fatal("caught up: the clock must clear") } } func TestBundleAlarm_UnknownAndNoVouchedBundleSayNothing(t *testing.T) { f := newFix(t) vouch(t, f, "new") bundleReport(t, f, "cust1", "unknown", "") f.s.Alarms() f.now = f.now.Add(30 * 24 * time.Hour) if sent, _ := f.s.Alarms(); count(sent, EventBundleBehind) != 0 || !f.s.Store.BundleBehindSince("cust1").IsZero() { t.Fatalf("unknown is not a fact: %v", sent) } // control: the same box saying "none" IS behind (proves the report above was read at all) bundleReport(t, f, "cust1", "none", "") f.s.Alarms() if f.s.Store.BundleBehindSince("cust1").IsZero() { t.Fatal("control: a box saying none must start the clock") } g := newFix(t) vouch(t, g, "") bundleReport(t, g, "cust1", "0.143.0", "some-bundle") g.s.Alarms() g.now = g.now.Add(30 * 24 * time.Hour) if sent, _ := g.s.Alarms(); count(sent, EventBundleBehind) != 0 { t.Fatalf("nothing vouched, nothing behind: %v", sent) } }