Files
felhom-controller/controller/internal/web/backups_remote_statsknown_test.go
T
admin fd28d70bff v0.308.0: controller batch 2026-10-12 — clear-address warning (R-922), secret sweep over every page (R-255), read_failed (R-266), no NVMe heat band (R-333), share globe (R-577), Hungarian dates and numbers (R-562), lost-drive option pinned (R-521)
Operator rulings 2026-10-12, 09 §3 decisions 196, 204-207, 223, 227. MinAgent 0.131.0 unchanged.
R-255's sweep found one leak, fixed here: an optional-config secret_input was rendered into its
settings page; it is now revealed on demand. Hungarian parity fixtures re-captured once for R-562.

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

145 lines
6.5 KiB
Go

package web
import (
"strings"
"testing"
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
)
// ── SCENARIO F (R-225) — AN UNREAD STORE SAYS "UNKNOWN", NEVER "ZERO" ───────────────────────────
//
// Measured live on 2026-08-05 (CAMPAIGN-11): after a rebuild this page rendered
//
// Tároló méret · 0 pillanatkép Tárhelykeret: 0 / 50 GB (0%)
//
// directly above a card stating the store contains backups made under another key. An SFTP listing
// of the remote — read-only, no decryption — found snapshot `f3d9cd67` and **12 535 KB** really
// there. `snapshot_count` and `repo_size_bytes` were simply ABSENT from settings.json, and the zero
// value spoke for them.
//
// This is R-217's defect class one card over: a field whose zero is indistinguishable from a real
// measurement, defaulted past on an unknown path. `StatsKnown` is the named state, for the same
// reason `OffsiteInventory.Empty` is named rather than inferred from `len(Apps)==0`.
//
// Render tests per branch of the gate, because a template gate without one is the v0.70.1 lesson.
func remoteStatsData(known bool, snaps int, human string) map[string]interface{} {
d := splitTestData()
d["Offbox"] = &settings.OffboxTarget{
Enabled: true, Host: "nas.local", User: "felhom", RepoPath: "/srv/repo",
LastStatus: "error", EscrowState: "escrowed", QuotaGB: 50,
SnapshotCount: snaps, RepoSizeHuman: human, StatsKnown: known,
}
d["OffboxQuotaPct"] = 0
return d
}
// The branch the finding was measured on: nothing has ever been read.
func TestBackupsRemote_UnknownStoreStatsSayUnknownNotZero(t *testing.T) {
html := renderBackupPage(t, "backups_remote", remoteStatsData(false, 0, ""))
// RED-PROOF: set StatsKnown true in this fixture (or drop the template guard) → the page renders
// "0 pillanatkép" and "0 / 50 GB (0%)" again → this FAILS. Demonstrated failing before it was kept.
if strings.Contains(html, "0 pillanatkép") {
t.Fatalf("R-225 RETURNED: an unread store reports a snapshot COUNT of zero")
}
if !strings.Contains(html, "a pillanatképek száma még ismeretlen") {
t.Fatalf("an unread store must say the count is not known")
}
if strings.Contains(html, "0 / 50 GB") {
t.Fatalf("R-225 RETURNED: an unread store reports a used figure of zero")
}
if !strings.Contains(html, "még nem tudjuk, mennyi van a tárolóban") {
t.Fatalf("an unread store must say the used figure is not known")
}
// A 0%-wide fill bar is a PICTURE of emptiness, and a picture is a claim. Scoped to the quota
// bar's own container — the page has other zero-width elements and a bare "width:0%" search
// would pass or fail for unrelated reasons.
if bar := between(html, `id="offbox-quota-bar"`, "</div>\n </div>"); strings.Contains(bar, "background:var(--border") {
t.Fatalf("the fill bar rendered over an unread store — a 0%% bar asserts emptiness")
}
}
// The other branch: a store that WAS read and is genuinely empty must still say zero. Without this,
// the fix could be "never show a number", which loses real information.
func TestBackupsRemote_KnownEmptyStoreStillSaysZero(t *testing.T) {
html := renderBackupPage(t, "backups_remote", remoteStatsData(true, 0, ""))
if !strings.Contains(html, "0 pillanatkép") {
t.Fatalf("a store that was READ and holds nothing must say zero — that is knowledge")
}
if strings.Contains(html, "még ismeretlen") {
t.Fatalf("a measured zero must not be dressed up as unknown")
}
}
// And a store with real content renders it unchanged.
func TestBackupsRemote_KnownNonEmptyStoreRendersTheNumbers(t *testing.T) {
html := renderBackupPage(t, "backups_remote", remoteStatsData(true, 2, "12.0 MB"))
if !strings.Contains(html, "2 pillanatkép") {
t.Fatalf("a measured count must render")
}
if !strings.Contains(html, "12,0 MB / 50 GB") { // R-562: decimal comma on a Hungarian page
t.Fatalf("a measured size must render against the quota")
}
if strings.Contains(html, "még ismeretlen") || strings.Contains(html, "még nem tudjuk") {
t.Fatalf("measured stats must not read as unknown")
}
}
// between returns the slice of s after the first `from` and before the next `to` (empty when either
// marker is missing) — so an assertion can be scoped to one card instead of the whole page.
func between(s, from, to string) string {
i := strings.Index(s, from)
if i < 0 {
return ""
}
rest := s[i+len(from):]
if j := strings.Index(rest, to); j >= 0 {
return rest[:j]
}
return rest
}
// ── SCENARIO H (R-228) — THE SET-ASIDE HISTORY IS VISIBLE AND HONESTLY DESCRIBED ────────────────
//
// Measured 2026-08-05 (CAMPAIGN-11 F7): a customer chose "I do not want the old data", was told the
// backups would be KEPT and not deleted, and the move-aside did exactly that — 12 535 KB, byte-exact,
// at `/home/felhom-repo.orphaned-20260805`. The box recorded the path in `orphaned_renamed_to` and a
// census found **zero** references to it in any template or handler. The promise was kept and shown
// to nobody.
func TestBackupsRemote_SetAsideHistoryIsSurfaced(t *testing.T) {
d := splitTestData()
d["Offbox"] = &settings.OffboxTarget{
Enabled: true, Host: "nas.local", User: "felhom", RepoPath: "/srv/repo",
LastStatus: "ok", EscrowState: "escrowed", StatsKnown: true,
OrphanedRenamedTo: "/home/felhom-repo.orphaned-20260805",
}
html := renderBackupPage(t, "backups_remote", d)
// RED-PROOF: delete the {{if .Offbox.OrphanedRenamedTo}} block → this FAILS, and the set-aside
// history is invisible again. Demonstrated failing before this test was kept.
if !strings.Contains(html, "félre vannak téve") {
t.Fatalf("R-228 RETURNED: the set-aside history is not mentioned at all")
}
if !strings.Contains(html, "nem töröltük") {
t.Fatalf("the customer must be told it was NOT deleted — that is the promise being kept")
}
// §7.6 — it must NOT promise the history can be reopened. It cannot be, by anyone, today.
for _, forbidden := range []string{"visszaállítható lehet", "vissza tudod állítani", "megnyithatod", "kóddal később"} {
if strings.Contains(html, forbidden) {
t.Errorf("the set-aside notice promises the history can be reopened (%q) — the read path does not exist", forbidden)
}
}
}
// And a box that never set anything aside must not claim it did.
func TestBackupsRemote_NoSetAsideNoClaim(t *testing.T) {
html := renderBackupPage(t, "backups_remote", remoteStatsData(true, 1, "12.0 MB"))
if strings.Contains(html, "félre vannak téve") {
t.Fatal("a box with no set-aside history must not claim one exists")
}
}