1package analyzer
2
3import (
4 "path/filepath"
5 "strings"
6 "testing"
7
8 "github.com/bnema/hexcheck/config"
9
10 "golang.org/x/tools/go/analysis/analysistest"
11)
12
13const businessLogicRule = "suspicious-business-logic-in-adapter"
14
15func TestBusinessLogicGoldenCases(t *testing.T) {
16 result := analysistest.Run(t, analysistest.TestData(), New(Options{Config: testConfig(), ModulePath: "example.com/project"}), "example.com/project/internal/infrastructure/http")
17
18 // rulesByFile maps each file to the diagnostic categories reported in it.
19 rulesByFile := map[string][]string{}
20 for _, diagnostic := range result[0].Diagnostics {
21 file := filepath.ToSlash(result[0].Pass.Fset.Position(diagnostic.Pos).Filename)
22 rulesByFile[file] = append(rulesByFile[file], diagnostic.Category)
23 }
24
25 tests := []struct {
26 file string
27 want bool
28 }{
29 {file: "typed_policy.go", want: true},
30 {file: "mixed_detection.go", want: true},
31 {file: "probing.go", want: false},
32 }
33 for _, tt := range tests {
34 t.Run(tt.file, func(t *testing.T) {
35 if got := hasRuleInFile(rulesByFile, tt.file, businessLogicRule); got != tt.want {
36 t.Fatalf("business-logic diagnostic in %s = %v, want %v", tt.file, got, tt.want)
37 }
38 })
39 }
40}
41
42func TestBusinessLogicCapGolden(t *testing.T) {
43 cfg := testConfig()
44 cfg.Components["capped"] = config.Component{Role: config.RoleAdapter, Paths: []string{"internal/infrastructure/capped/**"}}
45 cfg.Heuristics.BusinessLogicMaxDiagnosticsPerPackage = testIntPtr(1)
46 analysistest.Run(t, analysistest.TestData(), New(Options{Config: cfg, ModulePath: "example.com/project"}), "example.com/project/internal/infrastructure/capped")
47}
48
49func TestUsedIgnoreInTestPackageVariants(t *testing.T) {
50 cfg := testConfig()
51 cfg.Components["variants"] = config.Component{Role: config.RoleAdapter, Paths: []string{"internal/infrastructure/variants/**"}}
52 results := analysistest.Run(t, analysistest.TestData(), New(Options{Config: cfg, ModulePath: "example.com/project"}), "example.com/project/internal/infrastructure/variants")
53 plain, withTests := false, false
54 for _, result := range results {
55 if len(result.Diagnostics) != 0 {
56 t.Fatalf("%s: unexpected diagnostics: %v", result.Pass.Pkg.Path(), result.Diagnostics)
57 }
58 hasTestFile := false
59 for _, file := range result.Pass.Files {
60 if strings.HasSuffix(result.Pass.Fset.Position(file.Pos()).Filename, "_test.go") {
61 hasTestFile = true
62 }
63 }
64 if hasTestFile {
65 withTests = true
66 } else {
67 plain = true
68 }
69 }
70 if !plain || !withTests {
71 t.Fatalf("expected both ordinary and test package variants; plain=%v withTests=%v", plain, withTests)
72 }
73}
74
75func hasRuleInFile(rulesByFile map[string][]string, fileSuffix, rule string) bool {
76 for file, rules := range rulesByFile {
77 if !strings.HasSuffix(file, fileSuffix) {
78 continue
79 }
80 for _, got := range rules {
81 if got == rule {
82 return true
83 }
84 }
85 }
86 return false
87}