package api import ( "go/ast" "go/parser" "go/token" "io" "log" "os" "path/filepath" "strings" "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" ) // R-706 (v0.279.0) — a removal "with its backups" also deletes the app's off-site verification copy, and // ONLY that app's. COMPANION RED-PROOF: remove the removeVerificationCopy call from removeStack → the AST // check fails; make the helper skip the delete → the copy is still on disk. func TestR706_RemovalWithBackupsDeletesTheVerificationCopy(t *testing.T) { dir := t.TempDir() drive := filepath.Join(dir, "drive") cfg := &config.Config{} cfg.Paths.DataDir = filepath.Join(dir, "data") _ = os.MkdirAll(cfg.Paths.DataDir, 0o755) _ = os.MkdirAll(drive, 0o755) sett, err := settings.Load(filepath.Join(cfg.Paths.DataDir, "settings.json"), log.New(io.Discard, "", 0)) if err != nil { t.Fatal(err) } if err := sett.AddStoragePath(settings.StoragePath{Path: drive, Label: "HDD", Schedulable: true}); err != nil { t.Fatal(err) } bm := backup.NewManager(cfg, sett, log.New(io.Discard, "", 0)) mine, other := bm.OffsiteRestoreScratchPath("cloudapp"), bm.OffsiteRestoreScratchPath("otherapp") if mine == "" || other == "" { t.Fatalf("no verification-copy location resolved (%q, %q)", mine, other) } for _, p := range []string{mine, other} { _ = os.MkdirAll(p, 0o755) _ = os.WriteFile(filepath.Join(p, "restored.bin"), []byte("x"), 0o644) } r := &Router{backupMgr: bm, logger: log.New(io.Discard, "", 0)} got := r.removeVerificationCopy("cloudapp") if len(got) != 1 || !strings.HasPrefix(got[0], mine) { t.Fatalf("reported %v, want the copy at %s", got, mine) } if _, err := os.Stat(mine); !os.IsNotExist(err) { t.Fatalf("the verification copy is still on disk: %v", err) } if _, err := os.Stat(filepath.Join(other, "restored.bin")); err != nil { t.Fatalf("another app's verification copy was touched: %v", err) } fset := token.NewFileSet() f, err := parser.ParseFile(fset, "router.go", nil, 0) if err != nil { t.Fatal(err) } called := false for _, d := range f.Decls { if fn, ok := d.(*ast.FuncDecl); ok && fn.Name.Name == "removeStack" && fn.Body != nil { ast.Inspect(fn.Body, func(n ast.Node) bool { if s, ok := n.(*ast.SelectorExpr); ok && s.Sel.Name == "removeVerificationCopy" { called = true } return true }) } } if !called { t.Fatal("removeStack does not call removeVerificationCopy (R-706)") } }