Files
felhom.eu/hub/internal/osupdates/testapproval_test.go
T

174 lines
6.6 KiB
Go

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)
}
}