Composition roots already hold Snapshot ports; the interface and archtest now forbid putting Pool/DocGov/DocRevision/ContextManager/CAS/ProjectWorkflow/FreeformDoc/KB back on Service. Co-authored-by: Cursor <cursoragent@cursor.com>
109 lines
2.6 KiB
Go
109 lines
2.6 KiB
Go
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"
|
|
}
|