repair_test.go raw
1 package emit
2
3 import (
4 "strings"
5 "testing"
6 )
7
8 func TestParseCompileErrors(t *testing.T) {
9 output := `./main.go:10:2: undefined: Spore
10 ./main.go:15:5: undefined: cfg
11 ./main.go:20:2: "fmt" imported and not used
12 ./main.go:25:2: Node redeclared in this block
13 `
14 fixes := parseCompileErrors(output)
15
16 want := map[string]string{
17 "Spore": "undefined_type",
18 "cfg": "undefined_var",
19 "fmt": "unused_import",
20 "Node": "redeclared",
21 }
22
23 if len(fixes) != len(want) {
24 t.Fatalf("got %d fixes, want %d", len(fixes), len(want))
25 }
26
27 for _, f := range fixes {
28 expectedKind, ok := want[f.name]
29 if !ok {
30 t.Errorf("unexpected fix: %s %s", f.kind, f.name)
31 continue
32 }
33 if f.kind != expectedKind {
34 t.Errorf("fix %s: got kind %q, want %q", f.name, f.kind, expectedKind)
35 }
36 }
37 }
38
39 func TestParseCompileErrorsDedup(t *testing.T) {
40 output := `./main.go:10:2: undefined: Foo
41 ./main.go:15:2: undefined: Foo
42 ./main.go:20:2: undefined: Foo
43 `
44 fixes := parseCompileErrors(output)
45 if len(fixes) != 1 {
46 t.Fatalf("got %d fixes, want 1 (deduplication failed)", len(fixes))
47 }
48 }
49
50 func TestAddTypeStub(t *testing.T) {
51 source := `package main
52
53 import "fmt"
54
55 func main() {
56 var s Spore
57 fmt.Println(s)
58 }
59 `
60 result := addTypeStub(source, "Spore")
61 if !strings.Contains(result, "type Spore struct{}") {
62 t.Error("type stub not added")
63 }
64 // Verify it's after the import block.
65 importIdx := strings.Index(result, `import "fmt"`)
66 stubIdx := strings.Index(result, "type Spore struct{}")
67 if stubIdx < importIdx {
68 t.Error("stub added before import block")
69 }
70 }
71
72 func TestAddTypeStubNoDuplicate(t *testing.T) {
73 source := `package main
74
75 type Spore struct{}
76
77 func main() {}
78 `
79 result := addTypeStub(source, "Spore")
80 if strings.Count(result, "Spore") != strings.Count(source, "Spore") {
81 t.Error("duplicate type stub added when type already exists")
82 }
83 }
84
85 func TestAddVarStub(t *testing.T) {
86 source := `package main
87
88 func main() {
89 _ = cfg
90 }
91 `
92 result := addVarStub(source, "cfg")
93 if !strings.Contains(result, "var cfg interface{}") {
94 t.Error("var stub not added")
95 }
96 }
97
98 func TestRemoveImport(t *testing.T) {
99 source := `package main
100
101 import (
102 "fmt"
103 "os"
104 )
105
106 func main() {
107 os.Exit(0)
108 }
109 `
110 result := removeImport(source, "fmt")
111 if strings.Contains(result, `"fmt"`) {
112 t.Error("unused import not removed")
113 }
114 if !strings.Contains(result, `"os"`) {
115 t.Error("used import was incorrectly removed")
116 }
117 }
118
119 func TestRemoveRedeclaration(t *testing.T) {
120 source := `package main
121
122 type Foo struct{}
123
124 type Foo struct{ X int }
125
126 func main() {}
127 `
128 result := removeRedeclaration(source, "Foo")
129 count := strings.Count(result, "type Foo ")
130 if count != 1 {
131 t.Errorf("expected 1 declaration of Foo, got %d", count)
132 }
133 }
134
135 func TestApplyFixes(t *testing.T) {
136 source := `package main
137
138 import (
139 "fmt"
140 "os"
141 )
142
143 func main() {
144 var s Spore
145 _ = cfg
146 _ = s
147 os.Exit(0)
148 }
149 `
150 fixes := []fix{
151 {kind: "undefined_type", name: "Spore"},
152 {kind: "undefined_var", name: "cfg"},
153 {kind: "unused_import", name: "fmt"},
154 }
155
156 result := applyFixes(source, fixes)
157
158 if !strings.Contains(result, "type Spore struct{}") {
159 t.Error("type stub not added")
160 }
161 if !strings.Contains(result, "var cfg interface{}") {
162 t.Error("var stub not added")
163 }
164 if strings.Contains(result, `"fmt"`) {
165 t.Error("unused import not removed")
166 }
167 }
168
169 func TestStubNames(t *testing.T) {
170 source := `package main
171
172 type Foo struct{}
173 var bar interface{}
174 type Baz struct{ X int }
175 func main() {}
176 `
177 names := StubNames(source)
178 if len(names) != 2 {
179 t.Fatalf("got %d stubs, want 2: %v", len(names), names)
180 }
181 if names[0] != "Foo" || names[1] != "bar" {
182 t.Errorf("got stubs %v, want [Foo bar]", names)
183 }
184 }
185
186 func TestIsTypeName(t *testing.T) {
187 tests := []struct {
188 name string
189 want bool
190 }{
191 {"Spore", true},
192 {"Node", true},
193 {"cfg", false},
194 {"maxSize", false},
195 {"", false},
196 }
197 for _, tt := range tests {
198 got := isTypeName(tt.name)
199 if got != tt.want {
200 t.Errorf("isTypeName(%q) = %v, want %v", tt.name, got, tt.want)
201 }
202 }
203 }
204
205 func TestFindReceiverTypes(t *testing.T) {
206 source := `package main
207
208 func (x marketSignal) confidence() float64 { return 0 }
209 func (x *Block) Marshal() ([]byte, error) { return nil, nil }
210 func standalone() {}
211 func (x Node) ID() string { return "" }
212 `
213 types := findReceiverTypes(source)
214 want := map[string]bool{"marketSignal": true, "Block": true, "Node": true}
215 for name := range want {
216 if !types[name] {
217 t.Errorf("expected receiver type %q not found", name)
218 }
219 }
220 if types["standalone"] {
221 t.Error("standalone function incorrectly detected as receiver type")
222 }
223 }
224
225 func TestEnsureMain(t *testing.T) {
226 // Source without main — should add it.
227 source := "package main\n\nfunc init() {}\n"
228 result := ensureMain(source)
229 if !strings.Contains(result, "func main()") {
230 t.Error("ensureMain did not add main()")
231 }
232
233 // Source with main — should not duplicate.
234 source2 := "package main\n\nfunc main() {\n\tfmt.Println(\"hello\")\n}\n"
235 result2 := ensureMain(source2)
236 if strings.Count(result2, "func main()") != 1 {
237 t.Error("ensureMain duplicated main()")
238 }
239 }
240
241 func TestPromoteReceiverFixes(t *testing.T) {
242 fixes := []fix{
243 {kind: "undefined_var", name: "marketSignal"},
244 {kind: "undefined_var", name: "cfg"},
245 {kind: "undefined_type", name: "Block"},
246 }
247 receivers := map[string]bool{"marketSignal": true}
248 promoteReceiverFixes(fixes, receivers)
249 if fixes[0].kind != "undefined_type" {
250 t.Errorf("marketSignal not promoted: got %s", fixes[0].kind)
251 }
252 if fixes[1].kind != "undefined_var" {
253 t.Errorf("cfg incorrectly promoted: got %s", fixes[1].kind)
254 }
255 if fixes[2].kind != "undefined_type" {
256 t.Errorf("Block changed: got %s", fixes[2].kind)
257 }
258 }
259
260 func TestInsertAfterImports(t *testing.T) {
261 source := `package main
262
263 import (
264 "fmt"
265 )
266
267 func main() {
268 fmt.Println("hello")
269 }
270 `
271 result := insertAfterImports(source, "// INSERTED\n")
272 lines := strings.Split(result, "\n")
273 foundInsert := false
274 for i, line := range lines {
275 if strings.TrimSpace(line) == "// INSERTED" {
276 // Should be after the closing ")" of import.
277 if i > 0 && strings.TrimSpace(lines[i-1]) == ")" {
278 foundInsert = true
279 }
280 break
281 }
282 }
283 if !foundInsert {
284 t.Error("text not inserted after import block")
285 }
286 }
287