From 54d23992fa7e28919028bf8b68986179c8c70ec8 Mon Sep 17 00:00:00 2001 From: Andriy Yevtushyn <129842532+Avionic23@users.noreply.github.com> Date: Tue, 19 May 2026 11:45:15 +0300 Subject: [PATCH 1/2] add architectural fitness function tests --- fitness/dependency_test.go | 39 ++++++++++++++++++++ fitness/interface_size_test.go | 66 ++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 fitness/dependency_test.go create mode 100644 fitness/interface_size_test.go diff --git a/fitness/dependency_test.go b/fitness/dependency_test.go new file mode 100644 index 0000000..b787f93 --- /dev/null +++ b/fitness/dependency_test.go @@ -0,0 +1,39 @@ +package fitness + +import ( + "encoding/json" + "os/exec" + "strings" + "testing" +) + +func TestDependencyDirection(t *testing.T) { + hierarchy := []string{ + "listener", + "proxy", + "router", + "backend", + } + + for i, pkg := range hierarchy { + imports := getImports("load-balancer/" + pkg) + for _, imp := range imports { + for j := 0; j < i; j++ { + if strings.Contains(imp, "load-balancer/"+hierarchy[j]) { + t.Errorf( + "%s imports %s — violates downward dependency rule", + pkg, hierarchy[j], + ) + } + } + } + } +} + +func getImports(pkg string) []string { + cmd := exec.Command("go", "list", "-json", pkg) + out, _ := cmd.Output() + var info struct{ Imports []string } + json.Unmarshal(out, &info) + return info.Imports +} diff --git a/fitness/interface_size_test.go b/fitness/interface_size_test.go new file mode 100644 index 0000000..1a19e5b --- /dev/null +++ b/fitness/interface_size_test.go @@ -0,0 +1,66 @@ +package fitness + +import ( + "go/ast" + "go/parser" + "go/token" + "os" + "path/filepath" + "strings" + "testing" +) + +func TestInterfaceSizes(t *testing.T) { + maxMethods := 5 + projectRoot := ".." + + filepath.Walk(projectRoot, func(path string, info os.FileInfo, err error) error { + if err != nil { + return nil + } + if info.IsDir() { + name := info.Name() + if name == "vendor" || name == "fitness" || name == ".git" { + return filepath.SkipDir + } + return nil + } + if !strings.HasSuffix(path, ".go") { + return nil + } + if strings.HasSuffix(path, "_test.go") { + return nil + } + + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, path, nil, 0) + if err != nil { + return nil + } + + for _, decl := range file.Decls { + gd, ok := decl.(*ast.GenDecl) + if !ok || gd.Tok != token.TYPE { + continue + } + for _, spec := range gd.Specs { + ts := spec.(*ast.TypeSpec) + iface, ok := ts.Type.(*ast.InterfaceType) + if !ok { + continue + } + + methodCount := len(iface.Methods.List) + name := file.Name.Name + "." + ts.Name.Name + + if methodCount > maxMethods { + t.Errorf( + "%s (%s) has %d methods (max %d)", + name, path, methodCount, maxMethods, + ) + } + } + } + return nil + }) +} From f5841bdac753c33d8d69cb7162716f8ffec966b7 Mon Sep 17 00:00:00 2001 From: Andriy Yevtushyn <129842532+Avionic23@users.noreply.github.com> Date: Tue, 19 May 2026 11:45:56 +0300 Subject: [PATCH 2/2] ci: run fitness tests on pull request --- .github/workflows/fitness.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/fitness.yml diff --git a/.github/workflows/fitness.yml b/.github/workflows/fitness.yml new file mode 100644 index 0000000..f9225e1 --- /dev/null +++ b/.github/workflows/fitness.yml @@ -0,0 +1,17 @@ +name: Fitness Functions + +on: + pull_request: + +jobs: + fitness: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Run fitness tests + run: go test ./fitness/...