From 582d21b3b8254f4e6d88e69aeb4988141b8748e3 Mon Sep 17 00:00:00 2001 From: Michael McQuade Date: Fri, 21 Aug 2026 15:02:01 -0500 Subject: [PATCH] test(generator): vet every generated package, not just the corpus #120 compiled and lost data: two fields sharing a JSON tag, which encoding/json resolves by ignoring both. go vet names that, and the corpus test was the only place running it, so 45 other tests would have watched the same bug go by. Both e2e helpers now vet what they generate: the one that compiles a package and the one that compiles and runs a wire test against it. All 62 tests pass as they stand, so this adds no fixes, only the guard that would have caught the last one. The corpus test drops its own copy of the helper and uses the shared one. --- internal/generator/e2e_combinations_test.go | 26 +--------------- internal/generator/e2e_test.go | 33 +++++++++++++-------- 2 files changed, 22 insertions(+), 37 deletions(-) diff --git a/internal/generator/e2e_combinations_test.go b/internal/generator/e2e_combinations_test.go index ab42930..edbcd9a 100644 --- a/internal/generator/e2e_combinations_test.go +++ b/internal/generator/e2e_combinations_test.go @@ -1,8 +1,6 @@ package generator import ( - "os" - "os/exec" "path/filepath" "strings" "testing" @@ -91,7 +89,7 @@ func TestE2E_Combinations(t *testing.T) { t.Error("operations.go: the operation declaring security: [] should not authenticate") } - buildAndVet(t, files, "combinations") + buildGenerated(t, files, "combinations") } // containsCollapsed reports whether haystack holds needle once the runs of @@ -101,25 +99,3 @@ func TestE2E_Combinations(t *testing.T) { func containsCollapsed(haystack, needle string) bool { return strings.Contains(strings.Join(strings.Fields(haystack), " "), strings.Join(strings.Fields(needle), " ")) } - -// buildAndVet writes the generated files to a module and runs build and vet over -// them. vet catches what compiles and is still wrong: a shadowed error, a -// printf verb that does not match, a lost struct tag. -func buildAndVet(t *testing.T, files []GeneratedFile, module string) { - t.Helper() - dir := t.TempDir() - goMod := []byte("module " + module + "\n\ngo 1.25.5\n") - if err := os.WriteFile(filepath.Join(dir, "go.mod"), goMod, 0o644); err != nil { - t.Fatalf("writing go.mod: %v", err) - } - if err := WriteFiles(dir, files); err != nil { - t.Fatalf("WriteFiles: %v", err) - } - for _, args := range [][]string{{"build", "./..."}, {"vet", "./..."}} { - cmd := exec.Command("go", args...) - cmd.Dir = dir - if out, err := cmd.CombinedOutput(); err != nil { - t.Fatalf("go %s on the generated package: %v\n%s", strings.Join(args, " "), err, out) - } - } -} diff --git a/internal/generator/e2e_test.go b/internal/generator/e2e_test.go index 9ed2364..8377377 100644 --- a/internal/generator/e2e_test.go +++ b/internal/generator/e2e_test.go @@ -2021,13 +2021,17 @@ func runGeneratedWireTest(t *testing.T, files []GeneratedFile, module, testFile if err := os.WriteFile(filepath.Join(tmpDir, "wire_test.go"), []byte(testFile), 0o644); err != nil { t.Fatalf("writing wire_test.go: %v", err) } - cmd := exec.Command("go", "test", "./...") - cmd.Dir = tmpDir - if output, err := cmd.CombinedOutput(); err != nil { - for _, f := range files { - t.Logf("=== %s ===\n%s", f.Name, string(f.Content)) + // go test builds and runs; vet on top of it reports what compiles and is + // still wrong. + for _, args := range [][]string{{"vet", "./..."}, {"test", "./..."}} { + cmd := exec.Command("go", args...) + cmd.Dir = tmpDir + if output, err := cmd.CombinedOutput(); err != nil { + for _, f := range files { + t.Logf("=== %s ===\n%s", f.Name, string(f.Content)) + } + t.Fatalf("go %s on the generated package: %v\n%s", strings.Join(args, " "), err, string(output)) } - t.Fatalf("generated wire test failed: %v\n%s", err, string(output)) } } @@ -2075,12 +2079,17 @@ func buildGenerated(t *testing.T, files []GeneratedFile, module string) { if err := WriteFiles(tmpDir, files); err != nil { t.Fatalf("WriteFiles: %v", err) } - cmd := exec.Command("go", "build", "./...") - cmd.Dir = tmpDir - if output, err := cmd.CombinedOutput(); err != nil { - for _, f := range files { - t.Logf("=== %s ===\n%s", f.Name, string(f.Content)) + // vet as well as build: a generated package can compile and still be wrong in + // ways vet names, and one of them, two fields sharing a JSON tag, silently + // stops a property from decoding. + for _, args := range [][]string{{"build", "./..."}, {"vet", "./..."}} { + cmd := exec.Command("go", args...) + cmd.Dir = tmpDir + if output, err := cmd.CombinedOutput(); err != nil { + for _, f := range files { + t.Logf("=== %s ===\n%s", f.Name, string(f.Content)) + } + t.Fatalf("go %s on the generated package: %v\n%s", strings.Join(args, " "), err, string(output)) } - t.Fatalf("generated code failed to compile: %v\n%s", err, string(output)) } }