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"`, "\n "); 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") { 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") } }