91 lines
2.5 KiB
Go
91 lines
2.5 KiB
Go
package test
|
|
|
|
import (
|
|
"RenewCMS/internal/domain/role"
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"io/fs"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func nonTestGoFiles(t *testing.T, roots ...string) []string {
|
|
t.Helper()
|
|
files := []string{}
|
|
for _, root := range roots {
|
|
err := filepath.WalkDir(root, func(path string, entry fs.DirEntry, walkErr error) error {
|
|
if walkErr != nil {
|
|
return walkErr
|
|
}
|
|
if !entry.IsDir() && strings.HasSuffix(path, ".go") && !strings.HasSuffix(path, "_test.go") {
|
|
files = append(files, path)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if len(files) == 0 {
|
|
t.Fatalf("no Go files under %v", roots)
|
|
}
|
|
return files
|
|
}
|
|
|
|
func parsedGoFile(t *testing.T, path string) *ast.File {
|
|
t.Helper()
|
|
file, err := parser.ParseFile(token.NewFileSet(), path, nil, 0)
|
|
if err != nil {
|
|
t.Fatalf("cannot parse %s: %v", path, err)
|
|
}
|
|
return file
|
|
}
|
|
|
|
func isCreatedBySelector(expression ast.Expr) bool {
|
|
selector, ok := expression.(*ast.SelectorExpr)
|
|
return ok && selector.Sel.Name == "CreatedBy"
|
|
}
|
|
|
|
func TestOwnAndAllHasOneImplementation(t *testing.T) {
|
|
t.Run("No use case, handler or middleware compares CreatedBy for an authorization decision", func(t *testing.T) {
|
|
for _, path := range nonTestGoFiles(t, "../api", "../internal/infrastructure/useCases") {
|
|
ast.Inspect(parsedGoFile(t, path), func(node ast.Node) bool {
|
|
comparison, ok := node.(*ast.BinaryExpr)
|
|
if ok && (comparison.Op == token.EQL || comparison.Op == token.NEQ) && (isCreatedBySelector(comparison.X) || isCreatedBySelector(comparison.Y)) {
|
|
t.Errorf("%s compares CreatedBy directly", path)
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("A GrantSet is opaque outside the role domain", func(t *testing.T) {
|
|
if reflect.TypeOf(role.GrantSet{}).Kind() != reflect.Struct {
|
|
t.Errorf("GrantSet must be a struct so no caller can index it")
|
|
}
|
|
})
|
|
|
|
t.Run("The router and the middleware hold no catalogue key literal", func(t *testing.T) {
|
|
keys := map[string]bool{}
|
|
for _, permission := range role.Catalogue() {
|
|
keys[permission.Key] = true
|
|
}
|
|
for _, path := range nonTestGoFiles(t, "../api/middleware", "../api/router.go") {
|
|
ast.Inspect(parsedGoFile(t, path), func(node ast.Node) bool {
|
|
literal, ok := node.(*ast.BasicLit)
|
|
if !ok || literal.Kind != token.STRING {
|
|
return true
|
|
}
|
|
if value, err := strconv.Unquote(literal.Value); err == nil && keys[value] {
|
|
t.Errorf("%s holds the literal %q, use the role.Key constant", path, value)
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
})
|
|
}
|