package osupdates import ( "database/sql" "log" "os" "path/filepath" "testing" "time" "gitea.dooplex.hu/admin/felhom-hub/internal/store" _ "modernc.org/sqlite" ) // `11` §5.3.1 (hub v0.133.0): test approvals end with the test. // approveUnderTest makes ring 0 run a set and approves it at once under a TEST override (OS_APPROVE_AFTER=0 shape). func approveUnderTest(t *testing.T, f *fix, override string, set ...Package) string { t.Helper() f.s.TestOverride = override f.s.ApproveAfter, f.s.NightsRequired = 0, 0 f.report(t, "hp", "debug", true, set...) f.report(t, "n100", "debug", true, set...) if _, err := f.s.Evaluate(); err != nil { t.Fatal(err) } rel, _ := f.s.Store.LatestOSRelease(LayerGuest) if rel == nil { t.Fatal("not approved") } return rel.ID } // An approval made under the override carries the mark; one made without it does not. func TestTestApproval_IsMarked(t *testing.T) { f := newFix(t) approveUnderTest(t, f, "OS_APPROVE_AFTER=0s", pk("libc6", "u4")) rel, _ := f.s.Store.LatestOSRelease(LayerGuest) if !rel.Test { t.Fatalf("an approval under a TEST override must be marked: %+v", rel) } if info := f.s.Releases(); len(info) != 1 || !info[0].Test { t.Fatalf("the System page must see the mark: %+v", info) } f.s.TestOverride = "" f.now = f.now.Add(time.Hour) f.report(t, "hp", "debug", true, pk("libc6", "u5")) f.report(t, "n100", "debug", true, pk("libc6", "u5")) f.s.Evaluate() rel, _ = f.s.Store.LatestOSRelease(LayerGuest) if rel.Test { t.Fatalf("an approval without the override must not be marked: %+v", rel) } } // The consequence: after a restart without the override, a ring-1 box gets NO plan from the test approval; the // cancellation is an operator event and ring-1 boxes are bumped. func TestTestApproval_CancelledAtAStartWithoutTheOverride(t *testing.T) { f := newFix(t) id := approveUnderTest(t, f, "OS_APPROVE_AFTER=0s", pk("libc6", "u4")) if b := f.s.DesiredBlock("cust1"); b.Release == nil || b.Release.ID != id { t.Fatalf("before the restart the ring-1 box is served the release: %+v", b) } f.events, f.bumps = nil, nil f.s.TestOverride = "" // the hub restarts without the override ids, err := f.s.CancelTestReleases() if err != nil || len(ids) != 1 || ids[0] != id { t.Fatalf("cancelled %v, %v", ids, err) } if b := f.s.DesiredBlock("cust1"); b.Release != nil { t.Fatalf("a ring-1 box must get no plan from a cancelled test approval: %+v", b.Release) } if len(f.events) != 1 || f.events[0] != EventCancelled { t.Fatalf("events = %v", f.events) } if len(f.bumps) != 1 || f.bumps[0] != "cust1" { t.Fatalf("ring-1 boxes must be bumped: %v", f.bumps) } if c := f.s.CancelledReleases(); len(c) != 1 || c[0].ID != id { t.Fatalf("the page must list the cancellation: %+v", c) } // once is enough: a second start cancels nothing more if again, _ := f.s.CancelTestReleases(); len(again) != 0 { t.Fatalf("second start cancelled %v", again) } } func TestTestApproval_StaysWhileTheOverrideIsStillOn(t *testing.T) { f := newFix(t) approveUnderTest(t, f, "OS_APPROVE_AFTER=0s", pk("libc6", "u4")) if ids, _ := f.s.CancelTestReleases(); len(ids) != 0 { t.Fatalf("the test is still running; nothing to cancel: %v", ids) } if b := f.s.DesiredBlock("cust1"); b.Release == nil { t.Fatal("still served while the override is on") } } // A test approval that a REAL approval has superseded is history, not cancelled; the real one stays served. func TestTestApproval_SupersededIsLeftAlone(t *testing.T) { f := newFix(t) old := approveUnderTest(t, f, "OS_APPROVE_AFTER=0s", pk("libc6", "u4")) f.s.TestOverride = "" f.now = f.now.Add(time.Hour) f.report(t, "hp", "debug", true, pk("libc6", "u5")) f.report(t, "n100", "debug", true, pk("libc6", "u5")) f.s.Evaluate() real, _ := f.s.Store.LatestOSRelease(LayerGuest) if real.ID == old || real.Test { t.Fatalf("setup: %+v", real) } if ids, _ := f.s.CancelTestReleases(); len(ids) != 0 { t.Fatalf("a superseded test approval must not be cancelled: %v", ids) } if b := f.s.DesiredBlock("cust1"); b.Release == nil || b.Release.ID != real.ID { t.Fatalf("the real release stays served: %+v", b.Release) } } // After a cancellation the SAME set is approved again by the ruled wait (a real release) — the cancel is not a ban. func TestTestApproval_TheSetIsApprovedAgainByTheRuledWait(t *testing.T) { f := newFix(t) set := []Package{pk("libc6", "u4")} approveUnderTest(t, f, "OS_APPROVE_AFTER=0s", set...) f.s.TestOverride = "" f.s.ApproveAfter, f.s.NightsRequired = 24*time.Hour, 1 f.s.CancelTestReleases() f.now = f.now.Add(25 * time.Hour) f.report(t, "hp", "night", true, set...) f.report(t, "n100", "night", true, set...) f.s.Evaluate() rel, _ := f.s.Store.LatestOSRelease(LayerGuest) if rel == nil || rel.Test { t.Fatalf("the ruled wait must approve the set again, unmarked: %+v", rel) } } // The one-time backfill: on a database from before the mark, an approval earlier than 24 h after its set was first // seen (the 2026-10-04 shape: 1.5 h) is marked test; one after the ruled wait is not. func TestTestApproval_BackfillMarksTheEarlyApprovals(t *testing.T) { path := filepath.Join(t.TempDir(), "hub.db") db, err := sql.Open("sqlite", path) if err != nil { t.Fatal(err) } for _, q := range []string{ `CREATE TABLE os_candidates (fingerprint TEXT PRIMARY KEY, first_seen DATETIME NOT NULL, packages_json TEXT NOT NULL)`, `CREATE TABLE os_releases (id TEXT PRIMARY KEY, fingerprint TEXT NOT NULL, approved_at DATETIME NOT NULL, approved_by TEXT NOT NULL, packages_json TEXT NOT NULL, layer TEXT NOT NULL DEFAULT 'guest')`, `INSERT INTO os_candidates VALUES ('fpA', '2026-10-04 11:07:00', '[]'), ('fpB', '2026-10-02 08:00:00', '[]')`, `INSERT INTO os_candidates VALUES ('fpD', '2026-10-04 14:28:00', '[]')`, `INSERT INTO os_releases VALUES ('os-guest-early', 'fpA', '2026-10-04 12:39:33', 'auto', '[]', 'guest'), ('os-guest-ruled', 'fpB', '2026-10-03 09:00:00', 'auto', '[]', 'guest'), ('os-docker-button', 'fpD', '2026-10-04 14:28:42', 'operator', '[]', 'docker')`, } { if _, err := db.Exec(q); err != nil { t.Fatal(err) } } db.Close() st, err := store.New(path, log.New(os.Stderr, "", 0)) if err != nil { t.Fatal(err) } defer st.Close() rels, _ := st.UnsupersededTestReleases(LayerGuest) if len(rels) != 1 || rels[0].ID != "os-guest-early" { t.Fatalf("backfill: unsuperseded test releases = %+v", rels) } // the operator's own button approval is not backfilled (a person approved it) if d, _ := st.UnsupersededTestReleases(LayerDocker); len(d) != 0 { t.Fatalf("backfill marked the operator's Docker approval: %+v", d) } }