Files
felhom-controller/controller/internal/settings/r263_backup_target_writers_test.go
T
2026-10-05 17:05:24 +02:00

76 lines
2.4 KiB
Go

package settings
import (
"go/ast"
"go/parser"
"go/token"
"os"
"path/filepath"
"strings"
"testing"
)
// R-263: "SetBackupTarget is the only writer that GRANTS StoragePath.BackupTarget" — a drive never acquires the
// whole-guest backup-target role by appearing (E-2 §3). This scans EVERY non-test Go file under controller/internal and
// cmd for a write of a field named BackupTarget — an assignment `x.BackupTarget = v` or a composite-literal key
// `BackupTarget: v` — and fails on any that is not the literal `false`, unless it sits inside SetBackupTarget.
// RED-PROOF: plant `s.StoragePaths[0].BackupTarget = true` in any other function → this test fails.
func TestR263_OnlySetBackupTargetGrantsTheRole(t *testing.T) {
var roots []string
for _, r := range []string{"..", "../../cmd"} { // internal/ (this package's parent) and cmd/
if _, err := os.Stat(r); err == nil {
roots = append(roots, r)
}
}
files, writes := 0, 0
var bad []string
for _, root := range roots {
_ = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() || !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
return nil
}
fset := token.NewFileSet()
f, perr := parser.ParseFile(fset, path, nil, 0)
if perr != nil {
t.Fatalf("parse %s: %v", path, perr)
}
files++
for _, decl := range f.Decls {
fn, _ := decl.(*ast.FuncDecl)
inSetter := fn != nil && fn.Name.Name == "SetBackupTarget"
ast.Inspect(decl, func(n ast.Node) bool {
check := func(val ast.Expr, pos token.Pos) {
writes++
if id, ok := val.(*ast.Ident); ok && id.Name == "false" {
return
}
if !inSetter {
bad = append(bad, fset.Position(pos).String())
}
}
switch x := n.(type) {
case *ast.AssignStmt:
for i, lhs := range x.Lhs {
if sel, ok := lhs.(*ast.SelectorExpr); ok && sel.Sel.Name == "BackupTarget" && i < len(x.Rhs) {
check(x.Rhs[i], x.Pos())
}
}
case *ast.KeyValueExpr:
if k, ok := x.Key.(*ast.Ident); ok && k.Name == "BackupTarget" {
check(x.Value, x.Pos())
}
}
return true
})
}
return nil
})
}
if files < 100 || writes < 2 {
t.Fatalf("scan too small to mean anything: %d files, %d BackupTarget writes (want the two in settings.go)", files, writes)
}
if len(bad) > 0 {
t.Fatalf("BackupTarget may be granted outside SetBackupTarget at: %v", bad)
}
}