1
+ package goconst
2
+
3
+ import (
4
+ "go/ast"
5
+ "go/parser"
6
+ "go/token"
7
+ "testing"
8
+ )
9
+
10
+ func TestMatchConstant (t * testing.T ) {
11
+ tests := []struct {
12
+ name string
13
+ code string
14
+ wantIssues int
15
+ wantMatches map [string ]string // string -> matching const name
16
+ }{
17
+ {
18
+ name : "basic constant match" ,
19
+ code : `package example
20
+ const MyConst = "test"
21
+ func example() {
22
+ str := "test"
23
+ }` ,
24
+ wantIssues : 1 ,
25
+ wantMatches : map [string ]string {
26
+ "test" : "MyConst" ,
27
+ },
28
+ },
29
+ {
30
+ name : "multiple constants same value" ,
31
+ code : `package example
32
+ const (
33
+ FirstConst = "test"
34
+ SecondConst = "test"
35
+ )
36
+ func example() {
37
+ str := "test"
38
+ }` ,
39
+ wantIssues : 1 ,
40
+ wantMatches : map [string ]string {
41
+ "test" : "FirstConst" , // Should match the first one defined
42
+ },
43
+ },
44
+ {
45
+ name : "constant after usage" ,
46
+ code : `package example
47
+ func example() {
48
+ str := "test"
49
+ }
50
+ const MyConst = "test"` ,
51
+ wantIssues : 1 ,
52
+ wantMatches : map [string ]string {
53
+ "test" : "MyConst" ,
54
+ },
55
+ },
56
+ {
57
+ name : "constant in init" ,
58
+ code : `package example
59
+ func init() {
60
+ const InitConst = "test"
61
+ }
62
+ func example() {
63
+ str := "test"
64
+ }` ,
65
+ wantIssues : 1 ,
66
+ wantMatches : map [string ]string {
67
+ "test" : "InitConst" ,
68
+ },
69
+ },
70
+ {
71
+ name : "constant in different scope" ,
72
+ code : `package example
73
+ const GlobalConst = "global"
74
+ func example() {
75
+ const localConst = "local"
76
+ str1 := "global"
77
+ str2 := "local"
78
+ }` ,
79
+ wantIssues : 2 ,
80
+ wantMatches : map [string ]string {
81
+ "global" : "GlobalConst" ,
82
+ "local" : "localConst" ,
83
+ },
84
+ },
85
+ {
86
+ name : "exported vs unexported constants" ,
87
+ code : `package example
88
+ const (
89
+ ExportedConst = "exported"
90
+ unexportedConst = "unexported"
91
+ )
92
+ func example() {
93
+ str1 := "exported"
94
+ str2 := "unexported"
95
+ }` ,
96
+ wantIssues : 2 ,
97
+ wantMatches : map [string ]string {
98
+ "exported" : "ExportedConst" ,
99
+ "unexported" : "unexportedConst" ,
100
+ },
101
+ },
102
+ {
103
+ name : "string matches multiple constants" ,
104
+ code : `package example
105
+ const (
106
+ Const1 = "duplicate"
107
+ Const2 = "duplicate"
108
+ Const3 = "duplicate"
109
+ )
110
+ func example() {
111
+ str := "duplicate"
112
+ }` ,
113
+ wantIssues : 1 ,
114
+ wantMatches : map [string ]string {
115
+ "duplicate" : "Const1" , // Should match the first one defined
116
+ },
117
+ },
118
+ {
119
+ name : "constant with special characters" ,
120
+ code : `package example
121
+ const SpecialConst = "test\nwith\tspecial\rchars"
122
+ func example() {
123
+ str := "test\nwith\tspecial\rchars"
124
+ }` ,
125
+ wantIssues : 1 ,
126
+ wantMatches : map [string ]string {
127
+ "test\n with\t special\r chars" : "SpecialConst" ,
128
+ },
129
+ },
130
+ }
131
+
132
+ for _ , tt := range tests {
133
+ t .Run (tt .name , func (t * testing.T ) {
134
+ fset := token .NewFileSet ()
135
+ f , err := parser .ParseFile (fset , "example.go" , tt .code , 0 )
136
+ if err != nil {
137
+ t .Fatalf ("Failed to parse test code: %v" , err )
138
+ }
139
+
140
+ config := & Config {
141
+ MinStringLength : 1 , // Set to 1 to catch all strings
142
+ MinOccurrences : 1 , // Set to 1 to catch all occurrences
143
+ MatchWithConstants : true ,
144
+ }
145
+
146
+ issues , err := Run ([]* ast.File {f }, fset , config )
147
+ if err != nil {
148
+ t .Fatalf ("Run() error = %v" , err )
149
+ }
150
+
151
+ if len (issues ) != tt .wantIssues {
152
+ t .Errorf ("Got %d issues, want %d" , len (issues ), tt .wantIssues )
153
+ for _ , issue := range issues {
154
+ t .Logf ("Found issue: %q matches constant %q" , issue .Str , issue .MatchingConst )
155
+ }
156
+ }
157
+
158
+ // Verify constant matches
159
+ for _ , issue := range issues {
160
+ if wantConst , ok := tt .wantMatches [issue .Str ]; ok {
161
+ if issue .MatchingConst != wantConst {
162
+ t .Errorf ("String %q matched with constant %q, want %q" ,
163
+ issue .Str , issue .MatchingConst , wantConst )
164
+ }
165
+ } else {
166
+ t .Errorf ("Unexpected string found: %q" , issue .Str )
167
+ }
168
+ }
169
+ })
170
+ }
171
+ }
172
+
173
+ func TestMatchConstantMultiFile (t * testing.T ) {
174
+ // Test constant matching across multiple files
175
+ files := map [string ]string {
176
+ "const.go" : `package example
177
+ const (
178
+ SharedConst = "shared"
179
+ PackageConst = "package"
180
+ )` ,
181
+ "main.go" : `package example
182
+ func main() {
183
+ str1 := "shared"
184
+ str2 := "package"
185
+ }` ,
186
+ }
187
+
188
+ fset := token .NewFileSet ()
189
+ astFiles := make ([]* ast.File , 0 , len (files ))
190
+
191
+ for name , content := range files {
192
+ f , err := parser .ParseFile (fset , name , content , 0 )
193
+ if err != nil {
194
+ t .Fatalf ("Failed to parse %s: %v" , name , err )
195
+ }
196
+ astFiles = append (astFiles , f )
197
+ }
198
+
199
+ config := & Config {
200
+ MinStringLength : 1 ,
201
+ MinOccurrences : 1 ,
202
+ MatchWithConstants : true ,
203
+ }
204
+
205
+ issues , err := Run (astFiles , fset , config )
206
+ if err != nil {
207
+ t .Fatalf ("Run() error = %v" , err )
208
+ }
209
+
210
+ wantMatches := map [string ]string {
211
+ "shared" : "SharedConst" ,
212
+ "package" : "PackageConst" ,
213
+ }
214
+
215
+ if len (issues ) != len (wantMatches ) {
216
+ t .Errorf ("Got %d issues, want %d" , len (issues ), len (wantMatches ))
217
+ }
218
+
219
+ for _ , issue := range issues {
220
+ if wantConst , ok := wantMatches [issue .Str ]; ok {
221
+ if issue .MatchingConst != wantConst {
222
+ t .Errorf ("String %q matched with constant %q, want %q" ,
223
+ issue .Str , issue .MatchingConst , wantConst )
224
+ }
225
+ } else {
226
+ t .Errorf ("Unexpected string found: %q" , issue .Str )
227
+ }
228
+ }
229
+ }
0 commit comments