Files
felhom.eu/hub/internal/monitor/golden_vouch_test.go
T
admin 655f5f66f8
gates / gates (push) Successful in 6m0s
hub v0.147.0 (code): the operator's answers S4, S6, S10, S29, S30, S32, S33
- S4 / R-242: GoldenVouchChecker - a published golden unvouched for 2 h raises golden_not_vouched (operator only)
- S6 / R-244: the delete cascade de-references app_log_issues; one-time sweep flag -sweep-app-log-residue=count|apply
- S10 / R-266: read_failed storage entry shows "read failed", not 0 %
- S29 / R-250: host-key scan IPv4 first, ~2 min ladder, "safe to press Save again" error
- S30 / R-246: stale_at retired (reader, setter, badge)
- S32 / R-521: storage_reconnected ends the storage_disconnected cooldown on both channels
- S33 / R-402: host page "Last full check (off-site)" line; two wire-gate allowlist entries removed
Each with a test seen failing first. Not built or deployed yet (operator-attended deploy).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-12 14:54:14 +02:00

62 lines
2.0 KiB
Go

package monitor
import (
"context"
"errors"
"io"
"log"
"testing"
"time"
)
// R-242 second half (S4, decision 199). The CONSEQUENCE: a published-but-unvouched golden raises one
// operator warning after the grace; a vouch clears it; Gitea down says nothing.
// RED-PROOF: make Check return before onEvent (the pre-v0.147.0 hub had no such checker) → "want 1 alarm".
func TestGoldenVouchChecker(t *testing.T) {
now := time.Date(2026, 10, 12, 12, 0, 0, 0, time.UTC)
published := []string{"0.308.0", "0.301.0"}
var listErr error
vouched := "0.301.0"
var events []string
gc := NewGoldenVouchChecker(
func(context.Context) ([]string, error) { return published, listErr },
func() string { return vouched },
func(cust, typ, sev, msg, det, src string) { events = append(events, typ+"/"+sev) },
log.New(io.Discard, "", 0))
gc.now = func() time.Time { return now }
step := func(d time.Duration) { now = now.Add(d); gc.Check() }
step(0) // first sight of the gap — no alarm yet
step(time.Hour)
if len(events) != 0 {
t.Fatalf("alarm inside the grace: %v", events)
}
step(61 * time.Minute) // past 2 h
if len(events) != 1 || events[0] != "golden_not_vouched/warning" {
t.Fatalf("want 1 alarm golden_not_vouched/warning, got %v", events)
}
step(time.Hour)
if len(events) != 1 {
t.Fatalf("the same version alarmed twice: %v", events)
}
vouched = "0.308.0" // the operator vouches
step(time.Hour)
published = []string{"0.309.0", "0.308.0"}
listErr = errors.New("gitea down")
step(5 * time.Hour)
if len(events) != 1 {
t.Fatalf("an unreachable Gitea must not alarm: %v", events)
}
// Throttle: two calls within 15 min list once.
calls := 0
gc2 := NewGoldenVouchChecker(func(context.Context) ([]string, error) { calls++; return published, nil },
func() string { return "0.309.0" }, func(_, _, _, _, _, _ string) {}, log.New(io.Discard, "", 0))
t0 := now
gc2.now = func() time.Time { return t0 }
gc2.Check()
gc2.Check()
if calls != 1 {
t.Fatalf("throttle: %d listings in one window, want 1", calls)
}
}