package fillwatch import "testing" // R-329 — `Band.Severity()` returns "" for BandOK, and "" is NOT in the hub's vocabulary // {info, warning, error, critical}. The hub would coerce it to "info" and mail nobody. // // **This is safe today, and this test is what keeps it safe**, because the safety is not in // `Severity()` at all — it is in `Check()`: the notify seam fires ONLY on an escalation // (`if next <= prev { continue }`), and BandOK is the lowest band, so a notification can never carry // it. That is an invariant held in one function about the behaviour of another, which is precisely // the shape this project has shipped wrong nine times. // // THE LAYER. The first test pins the mapping (a unit fact). The second pins the CONSEQUENCE — that // no notification can carry a band whose severity is empty — because the mapping being right is not // what makes the product correct, and asserting only the mapping is case #9's mistake. // // RED-PROOF (observed, see REPORT.md): REMOVE the de-escalation `continue` block from Check() — // every band change then notifies, and TestR329_FillwatchNeverEmitsTheEmptySeverity fails with // `notified with band ok → severity "" (event type "")`. // // **A weaker mutation is INERT here, and it is worth knowing which:** changing `if next <= prev` to // `if next < prev` does nothing, because an earlier `if next == prev { continue }` already removed // the equal case. That mutation was tried first, the test passed, and the test was right to pass — // the code had not changed behaviour. **A red-proof that passes is not automatically a weak test; // check the mutation actually applied before believing either verdict.** func TestR329_BandSeverityMapping(t *testing.T) { for _, tc := range []struct { band Band want string }{ {BandWarning, "warning"}, {BandCritical, "critical"}, } { if got := tc.band.Severity(); got != tc.want { t.Errorf("Band(%v).Severity() = %q, want %q", tc.band, got, tc.want) } } // Documented and deliberate: BandOK has no severity because it has no event. The next test is // what proves that cannot leak. if got := BandOK.Severity(); got != "" { t.Errorf("BandOK.Severity() = %q — if this ever becomes a real severity, an all-clear starts "+ "emailing customers; change it deliberately, not by accident", got) } } // The consequence: drive a real Watcher up and back down and assert that every notification carries // a severity the hub will actually route. func TestR329_FillwatchNeverEmitsTheEmptySeverity(t *testing.T) { // Reuses the package's existing harness (REUSE.md §4) rather than inventing a second fixture. h := newHarness(t, Target{Path: "/mnt/data", Label: "Adatok"}) // up to warning, up to critical, back down, back to ok — the full round trip, so a // de-escalation notification would be caught if one ever started firing. for _, pct := range []float64{50, 91, 97, 91, 50} { h.set("/mnt/data", pct, 100-pct) h.check(t) } if len(h.events) == 0 { // Positive control: a test that observes nothing proves nothing. If the fixture stopped // crossing bands this would pass forever while checking air. t.Fatal("no notifications at all — the fixture never crossed a band, so this test is checking " + "nothing; fix the fixture before trusting a green") } for _, e := range h.events { if e.Band.Severity() == "" || e.Band.EventType() == "" { t.Errorf("notified with band %v → severity %q (event type %q): the hub coerces an unknown "+ "severity to \"info\" and then drops it, so this alert would reach NOBODY", e.Band, e.Band.Severity(), e.Band.EventType()) } if !map[string]bool{"info": true, "warning": true, "error": true, "critical": true}[e.Band.Severity()] { t.Errorf("notified with severity %q, outside the hub vocabulary", e.Band.Severity()) } } t.Logf("%d crossings notified, every one with a routable severity", len(h.events)) }