635c33d381
gates / gates (push) Successful in 31s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
124 lines
4.1 KiB
Go
124 lines
4.1 KiB
Go
package nightchain
|
||
|
||
import (
|
||
"testing"
|
||
"time"
|
||
)
|
||
|
||
// R-871 / decision 110 — the banner's rules. Red-proofs: catchup-2026-10-05/partB/red-proofs.txt.
|
||
|
||
func laptop() *HourDays { // on 18:00–23:59 every day of the week (Tester 2's shape)
|
||
var h HourDays
|
||
for k := 18; k <= 23; k++ {
|
||
h[k] = 7
|
||
}
|
||
return &h
|
||
}
|
||
|
||
func offAt(on bool) func(time.Time) (bool, bool) {
|
||
return func(time.Time) (bool, bool) { return on, true }
|
||
}
|
||
|
||
func base(now time.Time) BannerInput {
|
||
return BannerInput{Now: now, Window: "02:30", Loc: bud, SeededAt: at(1, 12, 0), DBDumpOK: at(3, 2, 31),
|
||
OnAtW: offAt(false), Hours: laptop()}
|
||
}
|
||
|
||
// Shown: the last dump is 2+ days old, the box was off at 02:30, and the record suggests 21:00.
|
||
// RED-PROOF: return Banner{} when stale (the pre-v0.295.0 silence) → "not shown".
|
||
func TestBanner_ShownWithReasonAndSuggestion(t *testing.T) {
|
||
b := ComputeBanner(base(at(5, 19, 0)))
|
||
if !b.Show {
|
||
t.Fatal("not shown although the last backup is 2 days old")
|
||
}
|
||
if b.OffAt != "02:30" || b.Suggest != "21:00" || b.DaysAgo != 2 {
|
||
t.Fatalf("banner = %+v, want off at 02:30, suggest 21:00, 2 days ago", b)
|
||
}
|
||
}
|
||
|
||
// Not shown: a backup within the last 26 h.
|
||
func TestBanner_NotShownWhenFresh(t *testing.T) {
|
||
in := base(at(5, 19, 0))
|
||
in.DBDumpOK = at(5, 2, 31)
|
||
if b := ComputeBanner(in); b.Show {
|
||
t.Fatalf("shown although the last dump is 16 h old: %+v", b)
|
||
}
|
||
}
|
||
|
||
// Not shown right after an upgrade (the ledger was seeded less than 26 h ago and knows no dump).
|
||
func TestBanner_NotShownBeforeTheRecordKnows(t *testing.T) {
|
||
in := base(at(5, 19, 0))
|
||
in.SeededAt, in.DBDumpOK = at(5, 9, 0), time.Time{}
|
||
if b := ComputeBanner(in); b.Show {
|
||
t.Fatalf("shown on the day of the upgrade: %+v", b)
|
||
}
|
||
}
|
||
|
||
// Dismissed: closed until the NEXT missed backup time, then back.
|
||
// RED-PROOF: drop the Dismissed check → "a closed banner came back without a new miss".
|
||
func TestBanner_DismissedThenBackAfterANewMiss(t *testing.T) {
|
||
in := base(at(5, 19, 0))
|
||
b := ComputeBanner(in)
|
||
in.Dismissed = b.MissedAt // the household closed it (02:30 on the 5th)
|
||
if ComputeBanner(in).Show {
|
||
t.Fatal("a closed banner came back without a new miss")
|
||
}
|
||
in.Now = at(5, 23, 0) // same evening — still the same miss
|
||
if ComputeBanner(in).Show {
|
||
t.Fatal("a closed banner came back the same evening")
|
||
}
|
||
in.Now = at(6, 19, 0) // off again at 02:30 on the 6th
|
||
if !ComputeBanner(in).Show {
|
||
t.Fatal("the banner did not come back after the next missed backup time")
|
||
}
|
||
}
|
||
|
||
// Gone by itself after a successful night (e.g. the catch-up ran).
|
||
func TestBanner_GoneAfterASuccess(t *testing.T) {
|
||
in := base(at(5, 19, 0))
|
||
in.DBDumpOK = at(5, 18, 20) // the catch-up's dump
|
||
if ComputeBanner(in).Show {
|
||
t.Fatal("still shown after a successful backup")
|
||
}
|
||
}
|
||
|
||
// The off-site tier counts when configured: a fresh dump with a 3-day-old off-site copy still shows.
|
||
func TestBanner_OffsiteTierCounts(t *testing.T) {
|
||
in := base(at(5, 19, 0))
|
||
in.DBDumpOK = at(5, 18, 20)
|
||
in.OffsiteConfigured, in.OffsiteOK = true, at(2, 4, 20)
|
||
b := ComputeBanner(in)
|
||
if !b.Show || b.DaysAgo != 3 {
|
||
t.Fatalf("banner = %+v, want shown with the off-site copy's 3 days", b)
|
||
}
|
||
}
|
||
|
||
// No clear pattern → no suggestion ("choose a time when the box is usually on"); box ON at W → no "off at".
|
||
// RED-PROOF: let Suggest return the first start without checking the days → a time is suggested from noise.
|
||
func TestBanner_NoPatternNoSuggestion(t *testing.T) {
|
||
in := base(at(5, 19, 0))
|
||
var noisy HourDays
|
||
for k := 18; k <= 23; k++ {
|
||
noisy[k] = 2 // on some evenings — 2 of 7 days is not "usually"
|
||
}
|
||
in.Hours, in.OnAtW = &noisy, offAt(true)
|
||
b := ComputeBanner(in)
|
||
if !b.Show || b.Suggest != "" || b.OffAt != "" {
|
||
t.Fatalf("banner = %+v, want shown, no suggestion, no 'off at'", b)
|
||
}
|
||
}
|
||
|
||
// A box usually ON at its window gets no new time (the miss has another cause).
|
||
func TestBanner_UsuallyOnAtWNoSuggestion(t *testing.T) {
|
||
var always HourDays
|
||
for k := range always {
|
||
always[k] = 7
|
||
}
|
||
if s := Suggest(always, 2); s != "" {
|
||
t.Fatalf("suggested %q for a box that is on at its window", s)
|
||
}
|
||
if s := Suggest(*laptop(), 2); s != "21:00" {
|
||
t.Fatalf("laptop suggestion %q, want 21:00", s)
|
||
}
|
||
}
|