// Copyright 2026 Alibaba Group // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cli import ( "go/ast" "go/parser" "go/token" "os" "path/filepath" "strconv" "strings" "testing" ) // TestProductionCodeDoesNotCallForTestHelpers enforces the fortest.go boundary: // production (non-test) code must not call symbols whose names end in ForTest. // Definitions may live in fortest.go; *_test.go may call them freely. func TestProductionCodeDoesNotCallForTestHelpers(t *testing.T) { root := moduleRoot(t) fset := token.NewFileSet() var violations []string err := filepath.WalkDir(root, func(path string, d os.DirEntry, walkErr error) error { if walkErr != nil { return walkErr } if d.IsDir() { name := d.Name() switch name { case ".git", "vendor", "node_modules", ".worktrees", "artifacts", "dist", "target": return filepath.SkipDir } return nil } if !strings.HasSuffix(path, ".go") { return nil } base := filepath.Base(path) if strings.HasSuffix(base, "_test.go") || base == "fortest.go" { return nil } rel, relErr := filepath.Rel(root, path) if relErr != nil { rel = path } file, parseErr := parser.ParseFile(fset, path, nil, 0) if parseErr != nil { return parseErr } ast.Inspect(file, func(n ast.Node) bool { call, ok := n.(*ast.CallExpr) if !ok { return true } name := calledIdentName(call.Fun) if name == "" || !strings.HasSuffix(name, "ForTest") { return true } pos := fset.Position(call.Pos()) violations = append(violations, rel+":"+strconv.Itoa(pos.Line)+": call to "+name) return true }) return nil }) if err != nil { t.Fatalf("walk module: %v", err) } if len(violations) > 0 { t.Fatalf("production code must not call *ForTest helpers:\n %s", strings.Join(violations, "\n ")) } } func moduleRoot(t *testing.T) string { t.Helper() wd, err := os.Getwd() if err != nil { t.Fatalf("getcwd: %v", err) } dir := wd for { if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { return dir } parent := filepath.Dir(dir) if parent == dir { t.Fatal("go.mod not found above package directory") } dir = parent } } func calledIdentName(fun ast.Expr) string { switch v := fun.(type) { case *ast.Ident: return v.Name case *ast.SelectorExpr: return v.Sel.Name default: return "" } }