The grants part keeps and recovers every binding of state asked in requires, and the validator reads both forms (issue 1966) #190
@@ -9,6 +9,9 @@ import (
|
||||
"fmt"
|
||||
"git.novox.be/novox/mesh-contracts/go/seats/meshbroker"
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/fs"
|
||||
"maps"
|
||||
"os"
|
||||
@@ -2870,6 +2873,68 @@ func TestGrants(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
// Issue 1966 (ADR 0595 §1, §11): the state a manifest keeps and a membership issues is read through mesh-sdk's two rules,
|
||||
// catalogue.KeptStates and Membership.StateIssued or StateNames, which answer the old `state` section and a key-value
|
||||
// entry asked in `requires` alike. A reader of a manifest's or a membership's `State` beside them misses the new form,
|
||||
// the kind of this issue's faults; only the rules that judge or compose the old section itself read it. Read by the
|
||||
// syntax tree across the repository, since the field's name alone is shared by other types.
|
||||
t.Run("the state a manifest keeps or a membership issues is read through mesh-sdk's two rules", func(t *testing.T) {
|
||||
judgesTheOldSection := map[string]bool{
|
||||
"modules/mesh-delivery-indexer/judge/busstate.go BusStateProblems": true,
|
||||
"modules/mesh-delivery-indexer/judge/oldsections.go SectionsUsed": true,
|
||||
"modules/mesh-delivery-indexer/judge/rules.go readOnlyStateProblems": true,
|
||||
"modules/mesh-delivery-indexer/judge/rules.go parseManifest": true,
|
||||
"modules/mesh-delivery-indexer/judge/rules.go StateProblems": true,
|
||||
"modules/mesh-delivery-indexer/judge/rules.go ObjectsProblems": true,
|
||||
"modules/mesh-grants/internal/grants/records.go bucketsOf": true,
|
||||
}
|
||||
planted := "package p\n\nimport \"git.novox.be/novox/mesh-sdk/go/catalogue\"\n\n" +
|
||||
"func planted(m catalogue.Manifest) (n int) {\n\tfor range m.State {\n\t\tn++\n\t}\n\treturn n\n}\n"
|
||||
if got, err := oldStateReaders("p.go", []byte(planted)); err != nil || !slices.Equal(got, []string{"p.go planted"}) {
|
||||
t.Fatalf("a planted range m.State is read as %v (%v), want it found", got, err)
|
||||
}
|
||||
files, seen := 0, map[string]bool{}
|
||||
err := filepath.WalkDir(filepath.Join("..", ".."), func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() && path != filepath.Join("..", "..") && (d.Name() == "testdata" || d.Name() == "node_modules" || (len(d.Name()) > 1 && d.Name()[0] == '.')) {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if d.IsDir() || !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
|
||||
return nil
|
||||
}
|
||||
src, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
files++
|
||||
rel, _ := filepath.Rel(filepath.Join("..", ".."), path)
|
||||
readers, err := oldStateReaders(filepath.ToSlash(rel), src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, r := range readers {
|
||||
seen[r] = true
|
||||
if !judgesTheOldSection[r] {
|
||||
t.Errorf("%s reads a manifest's or a membership's State beside catalogue.KeptStates and Membership.StateIssued", r)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if files < 100 {
|
||||
t.Fatalf("read %d source files, want the repository's", files)
|
||||
}
|
||||
for r := range judgesTheOldSection {
|
||||
if !seen[r] {
|
||||
t.Errorf("%s no longer reads the old section: take it off the list", r)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 774/S29/AC1 (ADR 0549 §5, ADR 0571 §10): the memberships composed from the assignments alone, with no read of the
|
||||
// controller, are the ones the union of assignments and assignments gave on the same input: the fixture's (two nodes,
|
||||
// the bus and mod-x on a, mod-y on b, as placed on the live mesh's own shape of nats beside its modules), whose
|
||||
@@ -4476,3 +4541,64 @@ func channelTakes(t *testing.T, say func(oc.Condition) error) func(oc.Condition)
|
||||
return say(c)
|
||||
}
|
||||
}
|
||||
|
||||
// oldStateReaders names, as "<path> <function>", every function of the source that reads the field State of a
|
||||
// catalogue.Manifest or a membership.Membership: a receiver, parameter or declared variable of either type, or a field
|
||||
// named Manifest or Membership (issue 1966's source guard).
|
||||
func oldStateReaders(path string, src []byte) ([]string, error) {
|
||||
f, err := parser.ParseFile(token.NewFileSet(), path, src, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typed := func(e ast.Expr) bool {
|
||||
if s, ok := e.(*ast.StarExpr); ok {
|
||||
e = s.X
|
||||
}
|
||||
sel, ok := e.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
x, ok := sel.X.(*ast.Ident)
|
||||
return ok && ((x.Name == "catalogue" && sel.Sel.Name == "Manifest") || (x.Name == "membership" && sel.Sel.Name == "Membership"))
|
||||
}
|
||||
var out []string
|
||||
for _, decl := range f.Decls {
|
||||
fn, ok := decl.(*ast.FuncDecl)
|
||||
if !ok || fn.Body == nil {
|
||||
continue
|
||||
}
|
||||
names := map[string]bool{}
|
||||
var fields []*ast.Field
|
||||
if fn.Recv != nil {
|
||||
fields = append(fields, fn.Recv.List...)
|
||||
}
|
||||
for _, fl := range append(fields, fn.Type.Params.List...) {
|
||||
if typed(fl.Type) {
|
||||
for _, n := range fl.Names {
|
||||
names[n.Name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
reads := false
|
||||
ast.Inspect(fn.Body, func(n ast.Node) bool {
|
||||
if vs, ok := n.(*ast.ValueSpec); ok && vs.Type != nil && typed(vs.Type) {
|
||||
for _, n := range vs.Names {
|
||||
names[n.Name] = true
|
||||
}
|
||||
}
|
||||
if s, ok := n.(*ast.SelectorExpr); ok && s.Sel.Name == "State" {
|
||||
switch x := s.X.(type) {
|
||||
case *ast.Ident:
|
||||
reads = reads || names[x.Name]
|
||||
case *ast.SelectorExpr:
|
||||
reads = reads || x.Sel.Name == "Manifest" || x.Sel.Name == "Membership"
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if reads {
|
||||
out = append(out, path+" "+fn.Name.Name)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user