1package analyzer
2
3import (
4 "path/filepath"
5 "testing"
6
7 "github.com/bnema/hexcheck/config"
8 "golang.org/x/tools/go/analysis/analysistest"
9)
10
11func TestAnalyzer(t *testing.T) {
12 tests := []struct {
13 name string
14 patterns []string
15 }{
16 {
17 name: "boundary imports",
18 patterns: []string{
19 "example.com/project/internal/domain",
20 "example.com/project/internal/policy",
21 "example.com/project/internal/application/usecase",
22 "example.com/project/internal/infrastructure/sql",
23 },
24 },
25 {
26 name: "heuristics",
27 patterns: []string{
28 "example.com/project/internal/infrastructure/http",
29 "example.com/project/cmd/app",
30 },
31 },
32 {
33 name: "type leaks",
34 patterns: []string{
35 "example.com/project/internal/application/port",
36 },
37 },
38 }
39
40 for _, tt := range tests {
41 t.Run(tt.name, func(t *testing.T) {
42 analysistest.Run(t, analysistest.TestData(), New(Options{Config: testConfig(), ModulePath: "example.com/project"}), tt.patterns...)
43 })
44 }
45}
46
47func testIntPtr(v int) *int { return &v }
48
49func testConfig() *config.Config {
50 cfg := &config.Config{
51 Root: filepath.Join(analysistest.TestData(), "src/example.com/project"),
52 Version: 1,
53 Components: map[string]config.Component{
54 "domain": {Role: config.RoleCore, Paths: []string{"internal/domain/**", "internal/policy/**"}},
55 "usecase": {Role: config.RoleUsecase, Paths: []string{"internal/application/usecase/**"}},
56 "ports": {Role: config.RolePorts, Paths: []string{"internal/application/port/**"}},
57 "http": {Role: config.RoleAdapter, Paths: []string{"internal/infrastructure/http/**"}},
58 "sql": {Role: config.RoleAdapter, Paths: []string{"internal/infrastructure/sql/**", "internal/sqlbad/**"}},
59 "cmd": {Role: config.RoleEntrypoint, Paths: []string{"cmd/**"}},
60 },
61 Rules: config.DefaultRuleSeverities(),
62 Heuristics: config.Heuristics{
63 BusinessLogicThreshold: testIntPtr(8),
64 BusinessLogicMinStrongSignals: testIntPtr(2),
65 BusinessLogicMinWeakSignals: testIntPtr(2),
66 BusinessLogicMaxFunctionNodes: testIntPtr(2000),
67 BusinessLogicMaxDiagnosticsPerPackage: testIntPtr(10),
68 BusinessLogicMode: "audit",
69 BusinessLogicMinConfidence: "medium",
70 BusinessKeywords: []string{"Validate", "Authorize", "Compute", "Calculate", "Apply", "Transition", "Can", "Detect", "Migrate", "Resolve", "Profile", "Score", "Ranking", "Restore", "Purge", "Update", "Performance", "Selected"},
71 },
72 ExternalTypes: config.ExternalTypes{
73 FrameworkPackages: []string{"example.com/framework"},
74 },
75 Mocking: config.Mocking{
76 GeneratedMockPaths: []string{"internal/application/port/mocks/**"},
77 },
78 Allow: []config.Allow{
79 {Rule: "no-infra-imports-in-usecase", Path: "internal/application/usecase/*_test.go", Reason: "test architecture rule has a more specific diagnostic"},
80 },
81 }
82 if err := cfg.Validate(); err != nil {
83 panic(err)
84 }
85 return cfg
86}