655f5f66f8
gates / gates (push) Successful in 6m0s
- 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
47 lines
2.4 KiB
Go
47 lines
2.4 KiB
Go
package web
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// R-402 (operator ruling 2026-10-12, S33, `09` §3 decision 228): the host page shows ONE line about
|
|
// the off-site integrity check — „Last full check: OK/FAILED, depth N, date" — from the fields the
|
|
// controller has sent since v0.227.0/v0.228.0. The verdict is never shown without its depth, and an
|
|
// absent timestamp means NEVER CHECKED, not FAILED (the R-331 lesson: a card that rendered a verdict
|
|
// from fields nothing wrote).
|
|
func TestOffsiteIntegrityLine(t *testing.T) {
|
|
cases := []struct{ name, report, want string }{
|
|
{"ok, full depth", `{"offsite":{"last_integrity_check":"2026-10-12T01:14:00Z","last_integrity_ok":true,"last_integrity_depth":"100%"}}`,
|
|
"OK, depth 100%, 2026-10-12 01:14 UTC"},
|
|
{"failed (bool absent), structure", `{"offsite":{"last_integrity_check":"2026-10-11T01:00:00Z","last_integrity_depth":"structure"}}`,
|
|
"FAILED, depth structure, 2026-10-11 01:00 UTC"},
|
|
{"old controller: no depth", `{"offsite":{"last_integrity_check":"2026-10-11T01:00:00Z","last_integrity_ok":true}}`,
|
|
"OK, depth unknown (controller older than v0.228.0), 2026-10-11 01:00 UTC"},
|
|
{"never checked", `{"offsite":{"repo_size_bytes":5}}`, "never checked"},
|
|
{"no off-site object", `{"version":"0.307.0"}`, "not reported (no off-site status in the box's report)"},
|
|
{"no report", ``, "not reported (no off-site status in the box's report)"},
|
|
}
|
|
for _, c := range cases {
|
|
if got := offsiteIntegrityLine(c.report); got != c.want {
|
|
t.Errorf("%s: got %q, want %q", c.name, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// The line reaches the host page (both branches of the template gate: a host with a customer renders
|
|
// it, with the verdict from the newest controller report). RED-PROOF: delete the "OffsiteIntegrity"
|
|
// key from hostDetailData → the id and the verdict are missing.
|
|
func TestHostPageShowsOffsiteIntegrityLine(t *testing.T) {
|
|
s, st, _ := newRevealServer(t)
|
|
cookie, _ := newRevealSession(t, s)
|
|
seedNetHost(t, st, "r402-host", "0.150.0", liveReportJSON, "")
|
|
if err := st.SaveReport("c-r402-host", []byte(`{"offsite":{"last_integrity_check":"2026-10-12T01:14:00Z","last_integrity_ok":true,"last_integrity_depth":"100%"}}`)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
body := getHostPage(t, s, cookie, "r402-host")
|
|
if !strings.Contains(body, `id="offsite-integrity"`) || !strings.Contains(body, "OK, depth 100%, 2026-10-12 01:14 UTC") {
|
|
t.Fatal("the host page does not show the off-site integrity line")
|
|
}
|
|
}
|