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