v0.279.0: after_install (decision 45), known default logins on the page, Part D empty-backup alarm, night chain (R-705), R-706
gates / gates (push) Successful in 27s
gates / gates (push) Successful in 27s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
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)")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user