package backupwindow import "testing" // Group A — LegTimes derives the three nightly legs at fixed offsets, wrap-safe across midnight. // Red-proof: drop the modulo in FmtHHMM → the 23:30 case yields "24:30"/"25:15" and fails. func TestLegTimes(t *testing.T) { cases := []struct{ start, db, tier2, offbox string }{ {"02:30", "02:30", "03:30", "04:15"}, // the default window {"23:30", "23:30", "00:30", "01:15"}, // wraps past midnight {"22:00", "22:00", "23:00", "23:45"}, {"00:00", "00:00", "01:00", "01:45"}, {"2:30", "02:30", "03:30", "04:15"}, // normalizes a missing leading zero } for _, c := range cases { db, tier2, offbox := LegTimes(c.start) if db != c.db || tier2 != c.tier2 || offbox != c.offbox { t.Errorf("LegTimes(%q) = (%q,%q,%q), want (%q,%q,%q)", c.start, db, tier2, offbox, c.db, c.tier2, c.offbox) } } } // Group A — invalid input is rejected (LegTimes → empty; Valid → error). Callers pass a value // already resolved through EffectiveWindow, so an empty result is never rendered. func TestLegTimes_InvalidRejected(t *testing.T) { for _, bad := range []string{"25:77", "2200", "", "ab:cd", "24:00", "12:60", "-1:00"} { db, tier2, offbox := LegTimes(bad) if db != "" || tier2 != "" || offbox != "" { t.Errorf("LegTimes(%q) = (%q,%q,%q), want all empty (rejected)", bad, db, tier2, offbox) } if Valid(bad) == nil { t.Errorf("Valid(%q) = nil, want an error", bad) } } if Valid("02:30") != nil { t.Error("Valid(02:30) returned an error for a well-formed time") } } // Group A — the whole-guest gate span is [W+2h, W+6h), wrap-safe. func TestGateWindow(t *testing.T) { if from, to := GateWindow("02:30"); from != "04:30" || to != "08:30" { t.Errorf("GateWindow(02:30) = (%q,%q), want (04:30,08:30)", from, to) } if from, to := GateWindow("23:00"); from != "01:00" || to != "05:00" { t.Errorf("GateWindow(23:00) = (%q,%q), want (01:00,05:00) — must wrap", from, to) } if from, to := GateWindow("bad"); from != "" || to != "" { t.Errorf("GateWindow(bad) = (%q,%q), want empties", from, to) } } // Group B — precedence: a valid settings value wins over a valid yaml value, which wins over the // "02:30" default; an empty/corrupt settings value falls through the chain. func TestEffectiveWindow(t *testing.T) { cases := []struct{ settingsVal, yamlVal, want string }{ {"22:00", "02:30", "22:00"}, // settings wins over yaml {"", "03:00", "03:00"}, // yaml when settings empty {"", "", "02:30"}, // default when both empty {"garbage", "03:00", "03:00"}, // corrupt settings → fall through to yaml {"garbage", "nope", "02:30"}, // both invalid → default {"22:00", "", "22:00"}, // settings valid, yaml empty } for _, c := range cases { if got := EffectiveWindow(c.settingsVal, c.yamlVal); got != c.want { t.Errorf("EffectiveWindow(%q,%q) = %q, want %q", c.settingsVal, c.yamlVal, got, c.want) } } }