From b482860c03352d2b1b68e38689455a925d96ff2f Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Mon, 13 Jul 2026 08:27:52 +0200 Subject: [PATCH] =?UTF-8?q?controller:=20zero-toggle=20honesty=20=E2=80=94?= =?UTF-8?q?=20a=20configured+escrowed=20offbox=20with=20no=20app=20selecte?= =?UTF-8?q?d=20shows=20'Nincs=20t=C3=A1voli=20ment=C3=A9sre=20jel=C3=B6lt?= =?UTF-8?q?=20alkalmaz=C3=A1s=E2=80=A6'=20on=20the=20toggle=20list,=20and?= =?UTF-8?q?=20a=20zero-toggle=20run=20reports=20'Sikeres=20=E2=80=94=20nin?= =?UTF-8?q?cs=20ment=C3=A9sre=20jel=C3=B6lt=20alkalmaz=C3=A1s'=20instead?= =?UTF-8?q?=20of=20bare=20success?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude-Session: https://claude.ai/code/session_01GzammAMzsJTgpQHqxwM2bC --- controller/internal/backup/offbox.go | 6 +++ controller/internal/backup/offbox_test.go | 37 +++++++++++++++++++ controller/internal/web/handlers.go | 12 +++++- .../internal/web/templates/backups.html | 3 ++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/controller/internal/backup/offbox.go b/controller/internal/backup/offbox.go index 0f97969..989fc5c 100644 --- a/controller/internal/backup/offbox.go +++ b/controller/internal/backup/offbox.go @@ -441,6 +441,12 @@ func (m *Manager) RunOffboxBackup(ctx context.Context) error { o.LastError = "" o.SnapshotCount = snapshots var warns []string + // Zero-toggle honesty (take-two obs.): a configured target with NOTHING selected reports + // its emptiness instead of a bare success — the customer thinks offsite runs, but nothing + // is covered until at least one app is toggled. + if len(apps) == 0 { + warns = append(warns, "Sikeres — nincs mentésre jelölt alkalmazás") + } if len(missing) > 0 { warns = append(warns, fmt.Sprintf("Figyelmeztetés: %d alkalmazásnak nincs elérhető mentése, ezek kimaradtak: %s", len(missing), strings.Join(missing, ", "))) diff --git a/controller/internal/backup/offbox_test.go b/controller/internal/backup/offbox_test.go index 1a38576..3debafa 100644 --- a/controller/internal/backup/offbox_test.go +++ b/controller/internal/backup/offbox_test.go @@ -194,6 +194,43 @@ func TestOffbox_RunFailsFastAndAlerts(t *testing.T) { } } +// Zero-toggle honesty (take-two obs., v0.123.0): a run with a configured+escrowed target but ZERO +// toggled apps must stay status=ok yet report "Sikeres — nincs mentésre jelölt alkalmazás" instead +// of a bare success. Red-proof: drop the len(apps)==0 warns branch → the LastWarning assertion fails. +func TestOffbox_ZeroToggledRunReportsEmptiness(t *testing.T) { + m, sett := newOffboxManager(t) + m.SetOffboxRunner(func(_ context.Context, _ []string, args ...string) ([]byte, error) { + if contains(args, "cat") && contains(args, "config") { + return []byte(`{"version":2}`), nil // repo exists + } + return []byte(`[]`), nil + }) + // No SetAppOffbox call — zero apps toggled. + if err := m.RunOffboxBackup(context.Background()); err != nil { + t.Fatalf("zero-toggled run must not error: %v", err) + } + st := sett.GetOffboxTarget() + if st.LastStatus != "ok" { + t.Fatalf("zero-toggled run status = %q, want ok (emptiness is honesty, not failure)", st.LastStatus) + } + if !strings.Contains(st.LastWarning, "Sikeres — nincs mentésre jelölt alkalmazás") { + t.Fatalf("zero-toggled run must report its emptiness, LastWarning = %q", st.LastWarning) + } + + // Companion negative: with one toggled app captured, the emptiness copy must NOT appear. + nsRoot := m.AppNamespaceRoot("rallly") + if err := os.MkdirAll(filepath.Join(RecoveryUnitPath(nsRoot, "rallly"), "db-dumps"), 0o755); err != nil { + t.Fatal(err) + } + _ = sett.SetAppOffbox("rallly", true) + if err := m.RunOffboxBackup(context.Background()); err != nil { + t.Fatalf("toggled run must not error: %v", err) + } + if st := sett.GetOffboxTarget(); strings.Contains(st.LastWarning, "nincs mentésre jelölt") { + t.Fatalf("a covered run must not carry the emptiness copy, LastWarning = %q", st.LastWarning) + } +} + // TestOffbox_RepoIdempotent: when the repo exists (cat config succeeds), ensure must NOT init. func TestOffbox_RepoIdempotent(t *testing.T) { m, sett := newOffboxManager(t) diff --git a/controller/internal/web/handlers.go b/controller/internal/web/handlers.go index 5eb57db..f6604e2 100644 --- a/controller/internal/web/handlers.go +++ b/controller/internal/web/handlers.go @@ -671,7 +671,17 @@ func (s *Server) backupsHandler(w http.ResponseWriter, r *http.Request) { offboxTgt := s.settings.GetOffboxTarget() data["Offbox"] = offboxTgt data["OffboxConfigured"] = s.backupMgr.OffboxConfigured() - data["OffboxApps"] = s.buildOffboxApps() + offboxApps := s.buildOffboxApps() + data["OffboxApps"] = offboxApps + // Zero-toggle hint (take-two obs.): configured + escrowed but no app selected — nothing is + // actually covered by the offsite leg until the customer toggles at least one. + offboxToggled := 0 + for _, a := range offboxApps { + if a.Enabled { + offboxToggled++ + } + } + data["OffboxToggledCount"] = offboxToggled // SLICE 4 soft-quota usage bar (rendered only when a quota is set — shared model). data["OffboxQuotaPct"] = backup.OffboxQuotaPercent(offboxTgt) } else { diff --git a/controller/internal/web/templates/backups.html b/controller/internal/web/templates/backups.html index d606bca..71a2225 100644 --- a/controller/internal/web/templates/backups.html +++ b/controller/internal/web/templates/backups.html @@ -169,6 +169,9 @@

Mely alkalmazások mentődnek a távoli tárolóra?

+ {{if and .OffboxApps (eq .Offbox.EscrowState "escrowed") (eq .OffboxToggledCount 0)}} +

Nincs távoli mentésre jelölt alkalmazás — jelölj ki legalább egyet.

+ {{end}} {{if .OffboxApps}}
{{range .OffboxApps}}