kept second-drive copies (D25: R-1175, R-1181, R-1179); .fab import keeps the family gate (R-1191); stale restore temp (R-1188 c); the overcommit sentence made true (R-1192); escrow before the floor (R-1176); unreleased
gates / gates (push) Successful in 1m20s
gates / gates (push) Successful in 1m20s
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/backup"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/config"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
|
||||
)
|
||||
|
||||
// D25 (operator ruling 2026-10-21): the restore page lists kept second-drive copies with „Eltávolítás" — and shows no
|
||||
// section at all when there are none. Render test per branch of the {{if .KeptCopies}} gate (the seam-wiring rule).
|
||||
func TestD25_RestorePageListsKeptCopiesWithRemove(t *testing.T) {
|
||||
base := func() map[string]interface{} {
|
||||
d := i18nLayoutData("backups-restore", "Visszaállítás a mentésből")
|
||||
d["Backup"] = i18nBackupStatus(false, false)
|
||||
d["OffboxConfigured"] = true
|
||||
d["OffsiteStoreState"] = "known"
|
||||
return d
|
||||
}
|
||||
with := base()
|
||||
with["KeptCopies"] = []keptCopyRow{{ID: "0123456789abcdef", Source: "csaladi_fotok", Line: "a(z) hdd_3 meghajtóról megőrzött másolat · a meghajtó nincs csatlakoztatva", SizeHuman: "1.2 GB", Since: "2026-10-21T12:00:00Z"}}
|
||||
html := renderBackupPage(t, "backups_restore", with)
|
||||
for _, want := range []string{"Megőrzött másolatok", "csaladi_fotok", "a(z) hdd_3 meghajtóról megőrzött másolat", `action="/backup/kept/remove"`,
|
||||
`name="id" value="0123456789abcdef"`, `name="confirm" value="1"`, "Eltávolítás", "confirmRemoveKeptCopy(this)"} {
|
||||
if !strings.Contains(html, want) {
|
||||
t.Errorf("restore page with a kept copy lacks %q", want)
|
||||
}
|
||||
}
|
||||
if html := renderBackupPage(t, "backups_restore", base()); strings.Contains(html, "Megőrzött másolatok") || strings.Contains(html, "/backup/kept/remove") {
|
||||
t.Error("restore page with NO kept copy shows the kept-copies section")
|
||||
}
|
||||
}
|
||||
|
||||
// „Eltávolítás" through the handler: no confirm → refused, nothing removed; an unknown id → its own refusal; the
|
||||
// recorded copy → removed, and ONLY it. Real settings + manager + disk (the record is the only selector).
|
||||
func TestD25_RemoveHandlerRemovesOnlyTheRecordedCopy(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
lg := log.New(io.Discard, "", 0)
|
||||
sett, err := settings.Load(filepath.Join(tmp, "settings.json"), lg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
destBase := filepath.Join(tmp, "hdd_1", "backups", "secondary", "_shares")
|
||||
kept := filepath.Join(destBase, "hdd_3", "csaladi_fotok")
|
||||
other := filepath.Join(destBase, "hdd_2", "b_share")
|
||||
for _, d := range []string{kept, other} {
|
||||
if err := os.MkdirAll(d, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(d, "f.txt"), []byte("x"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
st, _ := os.Stat(kept)
|
||||
sys := st.Sys().(*syscall.Stat_t)
|
||||
if err := sett.UpsertKeptCopy(settings.KeptCopy{Kind: "share", Source: "csaladi_fotok", Drive: "hdd_3", Reason: "drive_away",
|
||||
DestBase: destBase, Path: kept, Dev: uint64(sys.Dev), Ino: sys.Ino}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
id := settings.KeptCopyID(kept)
|
||||
cfg := &config.Config{}
|
||||
cfg.Paths.DataDir = tmp
|
||||
s := &Server{cfg: cfg, backupMgr: backup.NewManager(cfg, sett, lg), logger: lg}
|
||||
post := func(form string) string {
|
||||
rr := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("POST", "/backup/kept/remove", strings.NewReader(form))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
s.keptCopyRemoveHandler(rr, req)
|
||||
return rr.Header().Get("Location")
|
||||
}
|
||||
if loc := post("id=" + id); !strings.Contains(loc, "flash.kept.needs_confirmation") {
|
||||
t.Fatalf("no confirm → %q", loc)
|
||||
}
|
||||
if _, err := os.Stat(kept); err != nil {
|
||||
t.Fatal("removed without a confirm")
|
||||
}
|
||||
if loc := post("id=ffffffffffffffff&confirm=1"); !strings.Contains(loc, "flash.kept.not_found") {
|
||||
t.Fatalf("unknown id → %q", loc)
|
||||
}
|
||||
if loc := post("id=" + id + "&confirm=1"); !strings.Contains(loc, "flash.kept.removed") {
|
||||
t.Fatalf("recorded copy → %q", loc)
|
||||
}
|
||||
if _, err := os.Stat(kept); !os.IsNotExist(err) {
|
||||
t.Fatalf("the kept copy is still there: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(other, "f.txt")); err != nil {
|
||||
t.Fatalf("another copy was touched: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user