diff --git a/codegen/testutil/README.md b/codegen/testutil/README.md index ecf6fd99cc..e53b37e73d 100644 --- a/codegen/testutil/README.md +++ b/codegen/testutil/README.md @@ -96,22 +96,6 @@ func TestWithFluentAPI(t *testing.T) { } ``` -### Custom Options - -```go -func TestWithOptions(t *testing.T) { - opts := testutil.Options{ - BasePath: "testdata/custom", // Base directory for golden files - FormatCode: true, // Format Go code before comparison - CreateMissing: true, // Create golden files if missing - DiffContextLines: 5, // Lines of context in diffs - } - - gf := testutil.WithOptions(t, opts) - gf.StringContent(code).Path("output.golden").CompareContent() -} -``` - ### Directory Comparison Compare entire directory structures: @@ -133,12 +117,6 @@ func TestGeneratedDirectory(t *testing.T) { # Update golden files go test -update # or -u or -w -# Show detailed diffs -go test -golden.diff - -# Disable colored output -go test -golden.color=false - # Sequential updates (for debugging) go test -update -golden.parallel=false ``` @@ -179,9 +157,9 @@ mypackage/ The package automatically detects and formats content based on file extensions: -- `.go` files: Formatted with `go/format` -- `.json` files: Pretty-printed with proper indentation -- `.golden` files: Format detected from full filename (e.g., `server.go.golden` → Go) +- `.go` or `.go.golden`: Formatted with `go/format` +- `.json` or `.json.golden`: Pretty-printed with proper indentation +- Other extensions: Treated as plain text - Other extensions: Treated as plain text ## Notes diff --git a/codegen/testutil/example_test.go b/codegen/testutil/example_test.go index 5d37c3a9f2..7dc3d5219f 100644 --- a/codegen/testutil/example_test.go +++ b/codegen/testutil/example_test.go @@ -21,89 +21,19 @@ func main() { testutil.AssertString(t, "testdata/golden/hello_world.go.golden", code) } -// Example: Using the fluent API -func TestFluentAPI(t *testing.T) { - gf := testutil.NewGoldenFile(t, "testdata/golden") - - // Generate code - code := generateServiceCode() - - // Use fluent API for comparison - gf.StringContent(code). - Path("service.go.golden"). - CompareContent() -} - -// Example: Testing multiple files with batch operations -func TestBatchOperations(t *testing.T) { - batch := testutil.NewBatch(t) - - // Generate multiple files - serverCode := generateServerCode() - clientCode := generateClientCode() - typesCode := generateTypesCode() - - // Add all files to batch and compare - batch. - AddString("server.go.golden", serverCode). - AddString("client.go.golden", clientCode). - AddString("types.go.golden", typesCode). - Compare() -} - -// Example: Custom options for specific needs -func TestCustomOptions(t *testing.T) { - opts := testutil.Options{ - BasePath: "testdata/custom", - ContentType: testutil.ContentTypeGo, - FormatCode: true, - NormalizeWhitespace: true, - CreateMissing: true, // Create golden files if they don't exist - DiffContextLines: 5, // Show 5 lines of context in diffs - } - - gf := testutil.WithOptions(t, opts) - - code := generateComplexCode() - - gf.StringContent(code). - Path("complex.go.golden"). - CompareContent() -} - // Example: Format-aware comparisons func TestFormatAwareComparisons(t *testing.T) { - // Test Go code - automatically formatted + // Test Go code - automatically formatted (auto-detected by .go.golden) goCode := `package main import "fmt" func main(){fmt.Println("unformatted")}` - + testutil.AssertGo(t, "testdata/golden/formatted.go.golden", goCode) - // Test JSON - automatically pretty-printed + // Test JSON - automatically pretty-printed (auto-detected by .json.golden) jsonData := []byte(`{"name":"test","value":42,"items":["a","b","c"]}`) - - testutil.AssertJSON(t, "testdata/golden/config.json.golden", jsonData) -} - -// Example: Legacy migration -func TestLegacyMigration(t *testing.T) { - code := generateLegacyCode() - - // This is a drop-in replacement for the old compareOrUpdateGolden function - testutil.CompareOrUpdateGolden(t, code, "testdata/golden/legacy.golden") -} - -// Example: Conditional golden file creation -func TestConditionalCreation(t *testing.T) { - code := generateOptionalFeature() - - // Only create/compare if feature is enabled - if code != "" { - gf := testutil.NewGoldenFile(t, "testdata/golden") - gf.CompareOrCreate(code, "optional_feature.golden") - } + testutil.AssertJSON(t, "testdata/golden/config.json.golden", jsonData) } // Example: Testing with subtests @@ -143,15 +73,15 @@ func TestWithSubtests(t *testing.T) { // Example: Parallel golden file updates func TestParallelUpdates(t *testing.T) { // When running with -update, files are updated in parallel by default - files := map[string]string{ - "parallel1.golden": generateParallel1(), - "parallel2.golden": generateParallel2(), - "parallel3.golden": generateParallel3(), - "parallel4.golden": generateParallel4(), + files := map[string]string{} + for i := 1; i <= 4; i++ { + files[fmt.Sprintf("parallel%d.golden", i)] = generateParallel(i) } - gf := testutil.NewGoldenFile(t, "testdata/golden") - gf.CompareMultiple(files) + for golden, actual := range files { + gf := testutil.NewGoldenFile(t, "testdata/golden") + gf.StringContent(actual).Path(golden).CompareContent() + } } // Helper functions for examples @@ -178,45 +108,7 @@ func (s *serviceImpl) DoSomething(ctx context.Context) error { ` } -func generateServerCode() string { - return `package main - -import ( - "log" - "net/http" -) - -func main() { - http.HandleFunc("/", handler) - log.Fatal(http.ListenAndServe(":8080", nil)) -} - -func handler(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("Hello, Server!")) -} -` -} - -func generateClientCode() string { - return `package client - -import ( - "net/http" -) - -type Client struct { - baseURL string - http *http.Client -} - -func New(baseURL string) *Client { - return &Client{ - baseURL: baseURL, - http: &http.Client{}, - } -} -` -} +// removed verbose server/client helpers to keep file concise func generateTypesCode() string { return `package types @@ -242,18 +134,7 @@ func generateComplexCode() string { return generateServiceCode() + "\n" + generateTypesCode() } - -func generateLegacyCode() string { - return "// Legacy code example\n" + generateSimpleCode() -} - -func generateOptionalFeature() string { - // Simulate optional feature generation - if testing.Short() { - return "" - } - return "// Optional feature code\n" -} +// removed legacy code generator func generateSimpleCode() string { return `package simple @@ -277,7 +158,6 @@ func Find(id string) error { ` } -func generateParallel1() string { return fmt.Sprintf("// Parallel 1\n%s", generateSimpleCode()) } -func generateParallel2() string { return fmt.Sprintf("// Parallel 2\n%s", generateSimpleCode()) } -func generateParallel3() string { return fmt.Sprintf("// Parallel 3\n%s", generateSimpleCode()) } -func generateParallel4() string { return fmt.Sprintf("// Parallel 4\n%s", generateSimpleCode()) } \ No newline at end of file +func generateParallel(i int) string { + return fmt.Sprintf("// Parallel %d\n%s", i, generateSimpleCode()) +} diff --git a/codegen/testutil/golden.go b/codegen/testutil/golden.go index 449ed41441..ec436f6801 100644 --- a/codegen/testutil/golden.go +++ b/codegen/testutil/golden.go @@ -10,153 +10,45 @@ import ( "os" "path/filepath" "strings" - "sync" "testing" - "github.com/google/go-cmp/cmp" - "github.com/pmezard/go-difflib/difflib" + "github.com/stretchr/testify/require" ) var ( // Global flags for updating golden files - updateGolden = flag.Bool("update", false, "update golden files") - u = flag.Bool("u", false, "update golden files (shorthand)") - w = flag.Bool("w", false, "update golden files (legacy compatibility)") - - // Diff output control - verboseDiff = flag.Bool("golden.diff", false, "show detailed unified diffs for mismatches") - colorDiff = flag.Bool("golden.color", true, "colorize diff output") - - // Parallel update control - parallelUpdate = flag.Bool("golden.parallel", true, "update golden files in parallel") - - // Global registry for tracking golden file operations - goldenRegistry = ®istry{ - files: make(map[string]bool), - mu: sync.RWMutex{}, - } + update = flag.Bool("update", false, "update golden files") + u = flag.Bool("u", false, "update golden files (shorthand)") ) -// registry tracks golden file operations to prevent conflicts -type registry struct { - files map[string]bool - mu sync.RWMutex -} - -func (r *registry) register(path string) bool { - r.mu.Lock() - defer r.mu.Unlock() - if r.files[path] { - return false - } - r.files[path] = true - return true -} - -func (r *registry) unregister(path string) { - r.mu.Lock() - defer r.mu.Unlock() - delete(r.files, path) -} - // isUpdateMode returns true if any update flag is set func isUpdateMode() bool { - return *updateGolden || *u || *w -} - -// ContentType specifies the type of content for format-aware operations -type ContentType int - -const ( - // ContentTypeAuto detects content type from file extension - ContentTypeAuto ContentType = iota - // ContentTypeGo indicates Go source code - ContentTypeGo - // ContentTypeJSON indicates JSON data - ContentTypeJSON - // ContentTypeText indicates plain text - ContentTypeText - // ContentTypeGoTemplate indicates Go template code - ContentTypeGoTemplate -) - -// Options configures golden file operations -type Options struct { - // BasePath is the base directory for golden files (default: "testdata/golden") - BasePath string - - // ContentType specifies the content type for formatting - ContentType ContentType - - // FormatCode formats Go code before comparison (default: true for .go files) - FormatCode bool - - // NormalizeWhitespace trims trailing whitespace and ensures consistent line endings - NormalizeWhitespace bool - - // CreateMissing creates golden files if they don't exist - CreateMissing bool - - // DiffContextLines controls the number of context lines in diffs (default: 3) - DiffContextLines int - - // FileMode controls file permissions (default: 0644) - FileMode os.FileMode - - // UpdateMode allows overriding the global update mode - UpdateMode *bool + return *update || *u } -// DefaultOptions returns sensible defaults for most use cases -func DefaultOptions() Options { - return Options{ - BasePath: filepath.Join("testdata", "golden"), - ContentType: ContentTypeAuto, - FormatCode: true, - NormalizeWhitespace: true, - CreateMissing: false, - DiffContextLines: 3, - FileMode: 0644, - } -} +// DefaultBasePath is the default directory for golden files +const DefaultBasePath = "testdata/golden" // GoldenFile manages golden file testing operations with a fluent API type GoldenFile struct { - t testing.TB - options Options - content []byte - path string + t testing.TB + basePath string + update bool + content []byte + path string } // NewGoldenFile creates a new GoldenFile instance with default options func NewGoldenFile(t testing.TB, basePath string) *GoldenFile { t.Helper() - opts := DefaultOptions() - if basePath != "" { - opts.BasePath = basePath - } - return &GoldenFile{ - t: t, - options: opts, - } -} - -// WithOptions creates a new GoldenFile instance with custom options -func WithOptions(t testing.TB, opts Options) *GoldenFile { - t.Helper() - // Fill in defaults for unset options - if opts.BasePath == "" { - opts.BasePath = DefaultOptions().BasePath - } - if opts.DiffContextLines == 0 { - opts.DiffContextLines = DefaultOptions().DiffContextLines - } - if opts.FileMode == 0 { - opts.FileMode = DefaultOptions().FileMode - } return &GoldenFile{ - t: t, - options: opts, + t: t, + basePath: func() string { + if basePath != "" { + return basePath + } + return DefaultBasePath + }(), } } @@ -190,24 +82,15 @@ func (g *GoldenFile) CompareContent() { // Determine the full path goldenPath := g.path - if !filepath.IsAbs(g.path) && g.options.BasePath != "" { - goldenPath = filepath.Join(g.options.BasePath, g.path) - } - - // Register the file to prevent concurrent access - if !goldenRegistry.register(goldenPath) { - g.t.Fatalf("golden file %q is already being processed by another test", goldenPath) + if !filepath.IsAbs(g.path) && g.basePath != "" { + goldenPath = filepath.Join(g.basePath, g.path) } - defer goldenRegistry.unregister(goldenPath) // Prepare content content := g.prepareContent() // Check update mode - updateMode := isUpdateMode() - if g.options.UpdateMode != nil { - updateMode = *g.options.UpdateMode - } + updateMode := g.update || isUpdateMode() if updateMode { g.updateFile(content, goldenPath) @@ -216,11 +99,6 @@ func (g *GoldenFile) CompareContent() { // Check if file exists if _, err := os.Stat(goldenPath); os.IsNotExist(err) { - if g.options.CreateMissing { - g.updateFile(content, goldenPath) - g.t.Logf("Created new golden file: %s", goldenPath) - return - } g.t.Fatalf("golden file %q does not exist (run with -update to create)", goldenPath) } @@ -244,52 +122,35 @@ func (g *GoldenFile) CompareBytes(actual []byte, golden string) { func (g *GoldenFile) prepareContent() []byte { content := g.content - // Detect content type if auto - contentType := g.options.ContentType - if contentType == ContentTypeAuto && g.path != "" { - switch { - case strings.HasSuffix(g.path, ".go"): - contentType = ContentTypeGo - case strings.HasSuffix(g.path, ".json"): - contentType = ContentTypeJSON - case strings.HasSuffix(g.path, ".tmpl") || strings.HasSuffix(g.path, ".gotmpl"): - contentType = ContentTypeGoTemplate - default: - contentType = ContentTypeText - } - } + // Auto-detect format from file extension + isGo := strings.HasSuffix(g.path, ".go") || strings.HasSuffix(g.path, ".go.golden") + isJSON := strings.HasSuffix(g.path, ".json") || strings.HasSuffix(g.path, ".json.golden") - // Format based on content type - if g.options.FormatCode { - switch contentType { - case ContentTypeGo: - if formatted, err := format.Source(content); err == nil { + // Always format based on detected type + switch { + case isGo: + if formatted, err := format.Source(content); err == nil { + content = formatted + } + case isJSON: + var v any + if err := json.Unmarshal(content, &v); err == nil { + if formatted, err := json.MarshalIndent(v, "", " "); err == nil { content = formatted } - case ContentTypeJSON: - var v any - if err := json.Unmarshal(content, &v); err == nil { - if formatted, err := json.MarshalIndent(v, "", " "); err == nil { - content = formatted - } - } } } // Normalize whitespace - if g.options.NormalizeWhitespace { - // Convert Windows line endings to Unix - content = bytes.ReplaceAll(content, []byte("\r\n"), []byte("\n")) - // Trim trailing whitespace from each line - lines := strings.Split(string(content), "\n") - for i, line := range lines { - lines[i] = strings.TrimRight(line, " \t") - } - content = []byte(strings.Join(lines, "\n")) - // Ensure file ends with newline - if len(content) > 0 && content[len(content)-1] != '\n' { - content = append(content, '\n') - } + // Always normalize whitespace + content = bytes.ReplaceAll(content, []byte("\r\n"), []byte("\n")) + lines := strings.Split(string(content), "\n") + for i, line := range lines { + lines[i] = strings.TrimRight(line, " \t") + } + content = []byte(strings.Join(lines, "\n")) + if len(content) > 0 && content[len(content)-1] != '\n' { + content = append(content, '\n') } return content @@ -306,7 +167,7 @@ func (g *GoldenFile) updateFile(content []byte, goldenPath string) { } // Write the golden file - if err := os.WriteFile(goldenPath, content, g.options.FileMode); err != nil { + if err := os.WriteFile(goldenPath, content, 0644); err != nil { g.t.Fatalf("failed to update golden file %q: %v", goldenPath, err) } @@ -323,220 +184,36 @@ func (g *GoldenFile) compareContent(content []byte, goldenPath string) { } // Apply same transformations to golden content - if g.options.NormalizeWhitespace { - golden = bytes.ReplaceAll(golden, []byte("\r\n"), []byte("\n")) - lines := strings.Split(string(golden), "\n") - for i, line := range lines { - lines[i] = strings.TrimRight(line, " \t") - } - golden = []byte(strings.Join(lines, "\n")) - if len(golden) > 0 && golden[len(golden)-1] != '\n' { - golden = append(golden, '\n') - } - } - - if !bytes.Equal(content, golden) { - g.reportDifference(content, golden, goldenPath) - } -} - -// reportDifference reports the difference between content and golden -func (g *GoldenFile) reportDifference(content, golden []byte, goldenPath string) { - g.t.Helper() - - if *verboseDiff { - // Show detailed unified diff - diff := difflib.UnifiedDiff{ - A: strings.Split(string(golden), "\n"), - B: strings.Split(string(content), "\n"), - FromFile: goldenPath, - ToFile: "generated", - Context: g.options.DiffContextLines, - } - - diffStr, err := difflib.GetUnifiedDiffString(diff) - if err != nil { - g.t.Fatalf("failed to generate diff: %v", err) - } - - if *colorDiff { - diffStr = colorizeDiff(diffStr) - } - - g.t.Errorf("golden file mismatch for %q\n%s", goldenPath, diffStr) - } else { - // Use go-cmp for a more compact diff - if diff := cmp.Diff(string(golden), string(content)); diff != "" { - g.t.Errorf("golden file mismatch for %q (-want +got):\n%s", goldenPath, diff) - } - } - - g.t.Logf("Run with -update to update the golden file") -} - -// colorizeDiff adds ANSI color codes to diff output -func colorizeDiff(diff string) string { - const ( - red = "\033[31m" - green = "\033[32m" - cyan = "\033[36m" - reset = "\033[0m" - ) - - lines := strings.Split(diff, "\n") + golden = bytes.ReplaceAll(golden, []byte("\r\n"), []byte("\n")) + lines := strings.Split(string(golden), "\n") for i, line := range lines { - switch { - case strings.HasPrefix(line, "---") || strings.HasPrefix(line, "+++"): - lines[i] = cyan + line + reset - case strings.HasPrefix(line, "-"): - lines[i] = red + line + reset - case strings.HasPrefix(line, "+"): - lines[i] = green + line + reset - case strings.HasPrefix(line, "@@"): - lines[i] = cyan + line + reset - } + lines[i] = strings.TrimRight(line, " \t") } - return strings.Join(lines, "\n") -} - -// IsUpdateMode returns true if golden file update mode is enabled -func (g *GoldenFile) IsUpdateMode() bool { - if g.options.UpdateMode != nil { - return *g.options.UpdateMode + golden = []byte(strings.Join(lines, "\n")) + if len(golden) > 0 && golden[len(golden)-1] != '\n' { + golden = append(golden, '\n') } - return isUpdateMode() -} -// SetUpdateMode allows overriding the update mode for specific tests -func (g *GoldenFile) SetUpdateMode(update bool) { - g.options.UpdateMode = &update + if !bytes.Equal(content, golden) { + // Use testify for readable equality assertion + require.Equalf(g.t, string(golden), string(content), "golden file mismatch for %q", goldenPath) + g.t.Logf("Run with -update to update the golden file") + } } // Exists checks if a golden file exists func (g *GoldenFile) Exists(golden string) bool { goldenPath := golden if !filepath.IsAbs(golden) { - goldenPath = filepath.Join(g.options.BasePath, golden) + goldenPath = filepath.Join(g.basePath, golden) } _, err := os.Stat(goldenPath) return err == nil } -// CompareOrCreate compares content with a golden file if it exists, -// or creates it if it doesn't exist (useful for initial test creation) -func (g *GoldenFile) CompareOrCreate(actual string, golden string) { - g.t.Helper() - - // Temporarily enable CreateMissing - origCreateMissing := g.options.CreateMissing - g.options.CreateMissing = true - defer func() { g.options.CreateMissing = origCreateMissing }() - - g.StringContent(actual).Path(golden).CompareContent() -} - -// CompareMultiple compares multiple actual/golden file pairs -// The pairs parameter is a map where keys are golden file names and values are the actual content -func (g *GoldenFile) CompareMultiple(pairs map[string]string) { - g.t.Helper() - - // Type assert to *testing.T to use Run method - t, ok := g.t.(*testing.T) - if !ok { - // If not a *testing.T, just compare directly without subtests - for golden, actual := range pairs { - newG := &GoldenFile{t: g.t, options: g.options} - newG.StringContent(actual).Path(golden).CompareContent() - } - return - } - - if *parallelUpdate && isUpdateMode() { - // Update files in parallel - var wg sync.WaitGroup - for golden, actual := range pairs { - wg.Add(1) - go func(golden, actual string) { - defer wg.Done() - newG := &GoldenFile{t: g.t, options: g.options} - newG.StringContent(actual).Path(golden).CompareContent() - }(golden, actual) - } - wg.Wait() - } else { - // Run as subtests - for golden, actual := range pairs { - t.Run(filepath.Base(golden), func(t *testing.T) { - // Create a new GoldenFile instance to use the sub-test's t - subGolden := WithOptions(t, g.options) - subGolden.StringContent(actual).Path(golden).CompareContent() - }) - } - } -} - -// Batch provides batch operations for multiple golden files -type Batch struct { - t testing.TB - options Options - files []batchFile -} - -type batchFile struct { - path string - content []byte -} - -// NewBatch creates a new batch operation -func NewBatch(t testing.TB, opts ...Options) *Batch { - t.Helper() - options := DefaultOptions() - if len(opts) > 0 { - options = opts[0] - } - return &Batch{ - t: t, - options: options, - files: make([]batchFile, 0), - } -} - -// Add adds a file to the batch -func (b *Batch) Add(path string, content []byte) *Batch { - b.files = append(b.files, batchFile{path: path, content: content}) - return b -} - -// AddString adds a file with string content to the batch -func (b *Batch) AddString(path string, content string) *Batch { - return b.Add(path, []byte(content)) -} - -// Compare performs all comparisons in the batch -func (b *Batch) Compare() { - b.t.Helper() - - if *parallelUpdate && isUpdateMode() { - // Update files in parallel - var wg sync.WaitGroup - for _, file := range b.files { - wg.Add(1) - go func(file batchFile) { - defer wg.Done() - g := WithOptions(b.t, b.options) - g.Content(file.content).Path(file.path).CompareContent() - }(file) - } - wg.Wait() - } else { - // Compare sequentially - for _, file := range b.files { - g := WithOptions(b.t, b.options) - g.Content(file.content).Path(file.path).CompareContent() - } - } -} +// SetUpdateMode sets whether this GoldenFile should update golden files on compare. +func (g *GoldenFile) SetUpdateMode(update bool) { g.update = update } // CompareOrUpdateGolden provides a drop-in replacement for the legacy function // used throughout the codebase. New code should use GoldenFile instead. @@ -557,33 +234,37 @@ func CompareOrUpdateGolden(t *testing.T, actual, golden string) { // Assert provides a simple assertion API func Assert(t testing.TB, goldenPath string, got []byte) { t.Helper() - gf := &GoldenFile{t: t, options: DefaultOptions()} - gf.options.BasePath = "" + gf := &GoldenFile{t: t, basePath: ""} gf.Content(got).Path(goldenPath).CompareContent() } // AssertString provides a simple assertion API for strings func AssertString(t testing.TB, goldenPath string, got string) { t.Helper() - gf := &GoldenFile{t: t, options: DefaultOptions()} - gf.options.BasePath = "" + gf := &GoldenFile{t: t, basePath: ""} gf.StringContent(got).Path(goldenPath).CompareContent() } // AssertJSON compares JSON content with proper formatting func AssertJSON(t testing.TB, goldenPath string, got []byte) { t.Helper() - gf := &GoldenFile{t: t, options: DefaultOptions()} - gf.options.BasePath = "" - gf.options.ContentType = ContentTypeJSON + gf := &GoldenFile{t: t, basePath: ""} + var v any + if err := json.Unmarshal(got, &v); err == nil { + if formatted, err := json.MarshalIndent(v, "", " "); err == nil { + got = formatted + } + } gf.Content(got).Path(goldenPath).CompareContent() } // AssertGo compares Go source code with proper formatting func AssertGo(t testing.TB, goldenPath string, got string) { t.Helper() - gf := &GoldenFile{t: t, options: DefaultOptions()} - gf.options.BasePath = "" - gf.options.ContentType = ContentTypeGo - gf.StringContent(got).Path(goldenPath).CompareContent() + gf := &GoldenFile{t: t, basePath: ""} + formatted := []byte(got) + if src, err := format.Source([]byte(got)); err == nil { + formatted = src + } + gf.Content(formatted).Path(goldenPath).CompareContent() } diff --git a/codegen/testutil/golden_test.go b/codegen/testutil/golden_test.go index 9c8e3d4a21..358efad465 100644 --- a/codegen/testutil/golden_test.go +++ b/codegen/testutil/golden_test.go @@ -49,17 +49,16 @@ func TestGoldenFile(t *testing.T) { t.Run("CompareOrCreate", func(t *testing.T) { gf := testutil.NewGoldenFile(t, tmpDir) - - // Test creating new file + // Test creating new file (using update override) newFile := "new_file.golden" content := "new file content" - gf.CompareOrCreate(content, newFile) - + gf.SetUpdateMode(true) + gf.Compare(content, newFile) // Verify file was created assert.True(t, gf.Exists(newFile)) - - // Test comparing existing file - gf.CompareOrCreate(content, newFile) // Should pass + // Now compare without update + gf.SetUpdateMode(false) + gf.Compare(content, newFile) // Test comparing with different content would fail the test // We verify the file was created correctly above @@ -75,8 +74,10 @@ func TestGoldenFile(t *testing.T) { "file3.golden": "content 3", } - // Create files - gf.CompareMultiple(pairs) + // Create files (using update override) + for golden, actual := range pairs { + gf.Compare(actual, golden) + } // Verify all files were created for golden, expected := range pairs { diff --git a/go.mod b/go.mod index 17b6e0c09b..85a90933ae 100644 --- a/go.mod +++ b/go.mod @@ -9,12 +9,10 @@ require ( github.com/getkin/kin-openapi v0.132.0 github.com/go-chi/chi/v5 v5.2.2 github.com/gohugoio/hashstructure v0.5.0 - github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.3 github.com/manveru/faker v0.0.0-20171103152722-9fbc68a78c4d github.com/pkg/errors v0.9.1 - github.com/pmezard/go-difflib v1.0.0 github.com/stretchr/testify v1.10.0 golang.org/x/text v0.28.0 golang.org/x/tools v0.36.0 @@ -34,6 +32,7 @@ require ( github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/mod v0.27.0 // indirect golang.org/x/net v0.43.0 // indirect golang.org/x/sync v0.16.0 // indirect diff --git a/go.work.sum b/go.work.sum index 38cec6b8df..1e3636c24f 100644 --- a/go.work.sum +++ b/go.work.sum @@ -9,7 +9,6 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k= github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA= github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= @@ -23,17 +22,11 @@ github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JS github.com/golang/glog v1.2.4 h1:CNNw5U8lSiiBk7druxtSHHTsRWcxKoac6kZKm2peBBc= github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= @@ -48,15 +41,21 @@ go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPx golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4= +golang.org/x/telemetry v0.0.0-20250807160809-1a19826ec488/go.mod h1:fGb/2+tgXXjhjHsTNdVEEMZNWA0quBnfrO+AfoDSAKw= golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 h1:hE3bRWtU6uceqlh4fhrSnUyjKHMKB9KrTLLG+bc0ddM= diff --git a/jsonrpc/integration_tests/go.mod b/jsonrpc/integration_tests/go.mod index 6adfeb890d..cdc5b78c96 100644 --- a/jsonrpc/integration_tests/go.mod +++ b/jsonrpc/integration_tests/go.mod @@ -19,10 +19,10 @@ require ( github.com/manveru/faker v0.0.0-20171103152722-9fbc68a78c4d // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect - golang.org/x/mod v0.26.0 // indirect + golang.org/x/mod v0.27.0 // indirect golang.org/x/sync v0.16.0 // indirect - golang.org/x/text v0.27.0 // indirect - golang.org/x/tools v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/tools v0.36.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/jsonrpc/integration_tests/go.sum b/jsonrpc/integration_tests/go.sum index 79c68968c9..ddbc4480d1 100644 --- a/jsonrpc/integration_tests/go.sum +++ b/jsonrpc/integration_tests/go.sum @@ -1,31 +1,45 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 h1:MGKhKyiYrvMDZsmLR/+RGffQSXwEkXgfLSA08qDn9AI= github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598/go.mod h1:0FpDmbrt36utu8jEmeU05dPC9AB5tsLYVVi+ZHfyuwI= github.com/gohugoio/hashstructure v0.5.0 h1:G2fjSBU36RdwEJBWJ+919ERvOVqAg9tfcYp47K9swqg= github.com/gohugoio/hashstructure v0.5.0/go.mod h1:Ser0TniXuu/eauYmrwM4o64EBvySxNzITEOLlm4igec= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/manveru/faker v0.0.0-20171103152722-9fbc68a78c4d h1:Zj+PHjnhRYWBK6RqCDBcAhLXoi3TzC27Zad/Vn+gnVQ= github.com/manveru/faker v0.0.0-20171103152722-9fbc68a78c4d/go.mod h1:WZy8Q5coAB1zhY9AOBJP0O6J4BuDfbupUDavKY+I3+s= github.com/manveru/gobdd v0.0.0-20131210092515-f1a17fdd710b h1:3E44bLeN8uKYdfQqVQycPnaVviZdBLbizFhU49mtbe4= github.com/manveru/gobdd v0.0.0-20131210092515-f1a17fdd710b/go.mod h1:Bj8LjjP0ReT1eKt5QlKjwgi5AFm5mI6O1A2G4ChI0Ag= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=