package archtest import ( "go/ast" "go/parser" "go/token" "os" "path/filepath" "strings" "testing" "time" ) func TestArchitectureBoundaries(t *testing.T) { root, err := ModuleRoot() if err != nil { t.Fatal(err) } b, err := LoadBoundaries(DefaultBoundariesPath(root)) if err != nil { t.Fatal(err) } findings, err := Scan(ScanOptions{Root: root}, b) if err != nil { t.Fatal(err) } ev := Evaluate(findings, b, time.Now()) if os.Getenv("ARCHTEST_DUMP") != "" { t.Log(ev.Report()) } if ev.Failed() { t.Fatal(ev.Report()) } if os.Getenv("ARCHTEST_STRICT") == "" { return } if len(b.BlockKinds) == 0 { if len(ev.Excepted) > 0 { t.Fatalf("strict/block mode: %d baseline exceptions remain\n%s", len(ev.Excepted), ev.Report()) } return } for _, f := range ev.Excepted { for _, kind := range b.BlockKinds { if f.Kind == kind { t.Fatalf("strict/block mode: cleared kind %s still has exception %s\n%s", kind, f.Key(), ev.Report()) } } } for _, kind := range b.BlockKinds { if ev.Counts[kind] > 0 { t.Fatalf("strict/block mode: cleared kind %s count=%d\n%s", kind, ev.Counts[kind], ev.Report()) } } } var forbiddenBizdevLocators = []string{ "Pool", "DocGov", "DocRevision", "ContextManager", "CAS", "ProjectWorkflow", "FreeformDoc", "KB", } func TestBizdevNoLocatorGetters(t *testing.T) { root, err := ModuleRoot() if err != nil { t.Fatal(err) } dir := filepath.Join(root, "internal", "bizdev") fset := token.NewFileSet() pkgs, err := parser.ParseDir(fset, dir, func(info os.FileInfo) bool { name := info.Name() return strings.HasSuffix(name, ".go") && !strings.HasSuffix(name, "_test.go") }, parser.SkipObjectResolution) if err != nil { t.Fatal(err) } forbidden := make(map[string]struct{}, len(forbiddenBizdevLocators)) for _, name := range forbiddenBizdevLocators { forbidden[name] = struct{}{} } for _, pkg := range pkgs { for path, file := range pkg.Files { for _, decl := range file.Decls { fn, ok := decl.(*ast.FuncDecl) if !ok || fn.Recv == nil || fn.Name == nil { continue } if _, hit := forbidden[fn.Name.Name]; !hit { continue } if !recvIsBizdevService(fn.Recv) { continue } t.Errorf("locator getter %s must not exist on *bizdev.Service (%s)", fn.Name.Name, relFile(root, path)) } } } } func recvIsBizdevService(recv *ast.FieldList) bool { if recv == nil || len(recv.List) == 0 { return false } expr := recv.List[0].Type if star, ok := expr.(*ast.StarExpr); ok { expr = star.X } ident, ok := expr.(*ast.Ident) return ok && ident.Name == "Service" }