Files
felhom-controller/controller/internal/web/kept_copies_test.go
T

98 lines
4.0 KiB
Go

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)
}
}