From 369fe56e2d6d69c4c826f05676c87e4c9c17b8cc Mon Sep 17 00:00:00 2001 From: Mike Farah Date: Fri, 11 Jul 2025 09:59:59 +1000 Subject: [PATCH] Adding test file (CursorAI) --- cmd/utils_test.go | 930 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 930 insertions(+) create mode 100644 cmd/utils_test.go diff --git a/cmd/utils_test.go b/cmd/utils_test.go new file mode 100644 index 00000000..3f613ecf --- /dev/null +++ b/cmd/utils_test.go @@ -0,0 +1,930 @@ +package cmd + +import ( + "fmt" + "os" + "strings" + "testing" + + "github.com/mikefarah/yq/v4/pkg/yqlib" + "github.com/spf13/cobra" +) + +func TestIsAutomaticOutputFormat(t *testing.T) { + tests := []struct { + name string + format string + expected bool + }{ + {"empty format", "", true}, + {"auto format", "auto", true}, + {"short auto format", "a", true}, + {"json format", "json", false}, + {"yaml format", "yaml", false}, + {"xml format", "xml", false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Save original value + originalFormat := outputFormat + defer func() { outputFormat = originalFormat }() + + outputFormat = tt.format + result := isAutomaticOutputFormat() + if result != tt.expected { + t.Errorf("isAutomaticOutputFormat() = %v, want %v", result, tt.expected) + } + }) + } +} + +func TestMaybeFile(t *testing.T) { + // Create a temporary file for testing + tempFile, err := os.CreateTemp("", "test") + if err != nil { + t.Fatalf("Failed to create temp file: %v", err) + } + defer os.Remove(tempFile.Name()) + tempFile.Close() + + // Create a temporary directory for testing + tempDir, err := os.MkdirTemp("", "test") + if err != nil { + t.Fatalf("Failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + tests := []struct { + name string + path string + expected bool + }{ + {"existing file", tempFile.Name(), true}, + {"existing directory", tempDir, false}, + {"non-existent path", "/path/that/does/not/exist", false}, + {"empty string", "", false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := maybeFile(tt.path) + if result != tt.expected { + t.Errorf("maybeFile(%q) = %v, want %v", tt.path, result, tt.expected) + } + }) + } +} + +func TestProcessArgs(t *testing.T) { + // Create a temporary file for testing + tempFile, err := os.CreateTemp("", "test") + if err != nil { + t.Fatalf("Failed to create temp file: %v", err) + } + defer os.Remove(tempFile.Name()) + tempFile.Close() + + // Create a temporary .yq file for testing + tempYqFile, err := os.Create("test.yq") + if err != nil { + t.Fatalf("Failed to create temp yq file: %v", err) + } + defer os.Remove(tempYqFile.Name()) + if _, err = tempYqFile.WriteString(".a.b"); err != nil { + t.Fatalf("Failed to write to temp yq file: %v", err) + } + tempYqFile.Close() + + tests := []struct { + name string + args []string + forceExpression string + expressionFile string + expectedExpr string + expectedArgs []string + expectError bool + }{ + { + name: "empty args", + args: []string{}, + forceExpression: "", + expressionFile: "", + expectedExpr: "", + expectedArgs: []string{}, + expectError: false, + }, + { + name: "force expression", + args: []string{"file1"}, + forceExpression: ".a.b", + expressionFile: "", + expectedExpr: ".a.b", + expectedArgs: []string{"file1"}, + expectError: false, + }, + { + name: "expression as first arg", + args: []string{".a.b", "file1"}, + forceExpression: "", + expressionFile: "", + expectedExpr: ".a.b", + expectedArgs: []string{"file1"}, + expectError: false, + }, + { + name: "file as first arg", + args: []string{tempFile.Name()}, + forceExpression: "", + expressionFile: "", + expectedExpr: "", + expectedArgs: []string{tempFile.Name()}, + expectError: false, + }, + { + name: "yq file as first arg", + args: []string{tempYqFile.Name(), "things"}, + forceExpression: "", + expressionFile: "", + expectedExpr: ".a.b", + expectedArgs: []string{"things"}, + expectError: false, + }, + { + name: "dash as first arg", + args: []string{"-"}, + forceExpression: "", + expressionFile: "", + expectedExpr: "", + expectedArgs: []string{"-"}, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Save original values + originalForceExpression := forceExpression + originalExpressionFile := expressionFile + defer func() { + forceExpression = originalForceExpression + expressionFile = originalExpressionFile + }() + + forceExpression = tt.forceExpression + expressionFile = tt.expressionFile + + expr, args, err := processArgs(tt.args) + if tt.expectError { + if err == nil { + t.Errorf("processArgs() expected error but got none") + } + return + } + + if err != nil { + t.Errorf("processArgs() unexpected error: %v", err) + return + } + + if expr != tt.expectedExpr { + t.Errorf("processArgs() expression = %v, want %v", expr, tt.expectedExpr) + } + + if !stringsEqual(args, tt.expectedArgs) { + t.Errorf("processArgs() args = %v, want %v", args, tt.expectedArgs) + } + }) + } +} + +func TestConfigureDecoder(t *testing.T) { + tests := []struct { + name string + inputFormat string + evaluateTogether bool + expectError bool + expectType string + }{ + { + name: "yaml format", + inputFormat: "yaml", + evaluateTogether: false, + expectError: false, + expectType: "yamlDecoder", + }, + { + name: "json format", + inputFormat: "json", + evaluateTogether: true, + expectError: false, + expectType: "jsonDecoder", + }, + { + name: "xml format", + inputFormat: "xml", + evaluateTogether: false, + expectError: false, + expectType: "xmlDecoder", + }, + { + name: "invalid format", + inputFormat: "invalid", + evaluateTogether: false, + expectError: true, + expectType: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Save original value + originalInputFormat := inputFormat + defer func() { inputFormat = originalInputFormat }() + + inputFormat = tt.inputFormat + + decoder, err := configureDecoder(tt.evaluateTogether) + if tt.expectError { + if err == nil { + t.Errorf("configureDecoder() expected error but got none") + } + if decoder != nil { + t.Errorf("configureDecoder() expected nil decoder but got %v", decoder) + } + return + } + + if err != nil { + t.Errorf("configureDecoder() unexpected error: %v", err) + return + } + + if decoder == nil { + t.Errorf("configureDecoder() expected decoder but got nil") + return + } + + typeStr := fmt.Sprintf("%T", decoder) + if !strings.Contains(typeStr, tt.expectType) { + t.Errorf("configureDecoder() expected type to contain %q but got %q", tt.expectType, typeStr) + } + }) + } +} + +func TestConfigurePrinterWriter(t *testing.T) { + yqlib.InitExpressionParser() + + tests := []struct { + name string + splitFileExp string + format *yqlib.Format + forceColor bool + expectError bool + expectMulti bool + expectColorsEnabled bool + }{ + { + name: "single printer writer", + splitFileExp: "", + format: &yqlib.Format{}, + forceColor: false, + expectError: false, + expectMulti: false, + expectColorsEnabled: false, + }, + { + name: "multi printer writer with valid expression", + splitFileExp: ".a.b", + format: &yqlib.Format{}, + forceColor: true, + expectError: false, + expectMulti: true, + expectColorsEnabled: true, + }, + { + name: "multi printer writer with invalid expression", + splitFileExp: "[invalid", + format: &yqlib.Format{}, + forceColor: false, + expectError: true, + expectMulti: false, + expectColorsEnabled: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Save original values + originalSplitFileExp := splitFileExp + originalForceColor := forceColor + originalColorsEnabled := colorsEnabled + defer func() { + splitFileExp = originalSplitFileExp + forceColor = originalForceColor + colorsEnabled = originalColorsEnabled + }() + + splitFileExp = tt.splitFileExp + forceColor = tt.forceColor + colorsEnabled = false // Reset to test the setting + + writer, err := configurePrinterWriter(tt.format, os.Stdout) + if tt.expectError { + if err == nil { + t.Errorf("configurePrinterWriter() expected error but got none") + } + if writer != nil { + t.Errorf("configurePrinterWriter() expected nil writer but got %v", writer) + } + return + } + + if err != nil { + t.Errorf("configurePrinterWriter() unexpected error: %v", err) + return + } + + if writer == nil { + t.Errorf("configurePrinterWriter() expected writer but got nil") + return + } + + // Explicitly check colorsEnabled + if colorsEnabled != tt.expectColorsEnabled { + t.Errorf("configurePrinterWriter() colorsEnabled = %v, want %v", colorsEnabled, tt.expectColorsEnabled) + } + + // Check the type of the returned writer + writerType := fmt.Sprintf("%T", writer) + if tt.expectMulti { + if !strings.Contains(writerType, "multiPrintWriter") { + t.Errorf("configurePrinterWriter() expected multiPrintWriter but got %s", writerType) + } + } else { + if !strings.Contains(writerType, "singlePrinterWriter") { + t.Errorf("configurePrinterWriter() expected singlePrinterWriter but got %s", writerType) + } + } + }) + } +} + +func TestConfigureEncoder(t *testing.T) { + tests := []struct { + name string + outputFormat string + expectError bool + expectType string + }{ + { + name: "yaml format", + outputFormat: "yaml", + expectError: false, + expectType: "yamlEncoder", + }, + { + name: "json format", + outputFormat: "json", + expectError: false, + expectType: "jsonEncoder", + }, + { + name: "xml format", + outputFormat: "xml", + expectError: false, + expectType: "xmlEncoder", + }, + { + name: "properties format", + outputFormat: "properties", + expectError: false, + expectType: "propertiesEncoder", + }, + { + name: "invalid format", + outputFormat: "invalid", + expectError: true, + expectType: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Save original values + originalOutputFormat := outputFormat + originalIndent := indent + originalUnwrapScalar := unwrapScalar + originalColorsEnabled := colorsEnabled + originalNoDocSeparators := noDocSeparators + defer func() { + outputFormat = originalOutputFormat + indent = originalIndent + unwrapScalar = originalUnwrapScalar + colorsEnabled = originalColorsEnabled + noDocSeparators = originalNoDocSeparators + }() + + outputFormat = tt.outputFormat + indent = 2 + unwrapScalar = false + colorsEnabled = false + noDocSeparators = false + + encoder, err := configureEncoder() + if tt.expectError { + if err == nil { + t.Errorf("configureEncoder() expected error but got none") + } + if encoder != nil { + t.Errorf("configureEncoder() expected nil encoder but got %v", encoder) + } + return + } + + if err != nil { + t.Errorf("configureEncoder() unexpected error: %v", err) + return + } + + if encoder == nil { + t.Errorf("configureEncoder() expected encoder but got nil") + return + } + + typeStr := fmt.Sprintf("%T", encoder) + if !strings.Contains(typeStr, tt.expectType) { + t.Errorf("configureEncoder() expected type to contain %q but got %q", tt.expectType, typeStr) + } + }) + } +} + +func TestInitCommand(t *testing.T) { + // Create a temporary file for testing + tempFile, err := os.CreateTemp("", "test") + if err != nil { + t.Fatalf("Failed to create temp file: %v", err) + } + defer os.Remove(tempFile.Name()) + tempFile.Close() + + // Create a temporary split file + tempSplitFile, err := os.CreateTemp("", "split") + if err != nil { + t.Fatalf("Failed to create temp split file: %v", err) + } + defer os.Remove(tempSplitFile.Name()) + if _, err = tempSplitFile.WriteString(".a.b"); err != nil { + t.Fatalf("Failed to write to temp split file: %v", err) + } + tempSplitFile.Close() + + tests := []struct { + name string + args []string + writeInplace bool + frontMatter string + nullInput bool + splitFileExpFile string + splitFileExp string + outputToJSON bool + expectError bool + errorContains string + expectExpr string + expectArgs []string + }{ + { + name: "basic command", + args: []string{tempFile.Name()}, + writeInplace: false, + frontMatter: "", + nullInput: false, + expectError: false, + expectExpr: "", + expectArgs: []string{tempFile.Name()}, + }, + { + name: "write inplace with no args", + args: []string{}, + writeInplace: true, + frontMatter: "", + nullInput: false, + expectError: true, + errorContains: "write in place flag only applicable when giving an expression and at least one file", + }, + { + name: "write inplace with dash", + args: []string{"-"}, + writeInplace: true, + frontMatter: "", + nullInput: false, + expectError: true, + errorContains: "write in place flag only applicable when giving an expression and at least one file", + }, + { + name: "front matter with no args", + args: []string{}, + writeInplace: false, + frontMatter: "extract", + nullInput: false, + expectError: true, + errorContains: "front matter flag only applicable when giving an expression and at least one file", + }, + { + name: "write inplace with split file", + args: []string{tempFile.Name()}, + writeInplace: true, + frontMatter: "", + nullInput: false, + splitFileExp: ".a.b", + expectError: true, + errorContains: "write in place cannot be used with split file", + }, + { + name: "null input with args", + args: []string{tempFile.Name()}, + writeInplace: false, + frontMatter: "", + nullInput: true, + expectError: true, + errorContains: "cannot pass files in when using null-input flag", + }, + { + name: "split file expression from file", + args: []string{tempFile.Name()}, + writeInplace: false, + frontMatter: "", + nullInput: false, + splitFileExpFile: tempSplitFile.Name(), + expectError: false, + expectExpr: "", + expectArgs: []string{tempFile.Name()}, + }, + { + name: "output to JSON", + args: []string{tempFile.Name()}, + writeInplace: false, + frontMatter: "", + nullInput: false, + outputToJSON: true, + expectError: false, + expectExpr: "", + expectArgs: []string{tempFile.Name()}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Save original values + originalWriteInplace := writeInplace + originalFrontMatter := frontMatter + originalNullInput := nullInput + originalSplitFileExpFile := splitFileExpFile + originalSplitFileExp := splitFileExp + originalOutputToJSON := outputToJSON + originalInputFormat := inputFormat + originalOutputFormat := outputFormat + originalForceColor := forceColor + originalForceNoColor := forceNoColor + originalColorsEnabled := colorsEnabled + defer func() { + writeInplace = originalWriteInplace + frontMatter = originalFrontMatter + nullInput = originalNullInput + splitFileExpFile = originalSplitFileExpFile + splitFileExp = originalSplitFileExp + outputToJSON = originalOutputToJSON + inputFormat = originalInputFormat + outputFormat = originalOutputFormat + forceColor = originalForceColor + forceNoColor = originalForceNoColor + colorsEnabled = originalColorsEnabled + }() + + writeInplace = tt.writeInplace + frontMatter = tt.frontMatter + nullInput = tt.nullInput + splitFileExpFile = tt.splitFileExpFile + splitFileExp = tt.splitFileExp + outputToJSON = tt.outputToJSON + inputFormat = "auto" + outputFormat = "auto" + forceColor = false + forceNoColor = false + colorsEnabled = false + + cmd := &cobra.Command{} + expr, args, err := initCommand(cmd, tt.args) + if tt.expectError { + if err == nil { + t.Errorf("initCommand() expected error but got none") + return + } + if tt.errorContains != "" && !strings.Contains(err.Error(), tt.errorContains) { + t.Errorf("initCommand() error '%v' does not contain '%v'", err.Error(), tt.errorContains) + } + return + } + + if err != nil { + t.Errorf("initCommand() unexpected error: %v", err) + return + } + + if expr != tt.expectExpr { + t.Errorf("initCommand() expr = %v, want %v", expr, tt.expectExpr) + } + if !stringsEqual(args, tt.expectArgs) { + t.Errorf("initCommand() args = %v, want %v", args, tt.expectArgs) + } + }) + } +} + +func TestProcessArgsWithExpressionFile(t *testing.T) { + // Create a temporary .yq file with Windows line endings + tempYqFile, err := os.CreateTemp("", "test.yq") + if err != nil { + t.Fatalf("Failed to create temp yq file: %v", err) + } + defer os.Remove(tempYqFile.Name()) + if _, err = tempYqFile.WriteString(".a.b\r\n.c.d"); err != nil { + t.Fatalf("Failed to write to temp yq file: %v", err) + } + tempYqFile.Close() + + // Save original values + originalExpressionFile := expressionFile + defer func() { expressionFile = originalExpressionFile }() + + expressionFile = tempYqFile.Name() + + expr, args, err := processArgs([]string{"file1"}) + if err != nil { + t.Errorf("processArgs() unexpected error: %v", err) + return + } + + expectedExpr := ".a.b\n.c.d" // Should convert \r\n to \n + if expr != expectedExpr { + t.Errorf("processArgs() expression = %v, want %v", expr, expectedExpr) + } + + expectedArgs := []string{"file1"} + if !stringsEqual(args, expectedArgs) { + t.Errorf("processArgs() args = %v, want %v", args, expectedArgs) + } +} + +func TestProcessArgsWithNonExistentExpressionFile(t *testing.T) { + // Save original values + originalExpressionFile := expressionFile + defer func() { expressionFile = originalExpressionFile }() + + expressionFile = "/path/that/does/not/exist" + + expr, args, err := processArgs([]string{"file1"}) + if err == nil { + t.Errorf("processArgs() expected error but got none") + } + if expr != "" { + t.Errorf("processArgs() expected empty expression but got %v", expr) + } + if args != nil { + t.Errorf("processArgs() expected nil args but got %v", args) + } +} + +func TestInitCommandWithInvalidOutputFormat(t *testing.T) { + // Create a temporary file for testing + tempFile, err := os.CreateTemp("", "test") + if err != nil { + t.Fatalf("Failed to create temp file: %v", err) + } + defer os.Remove(tempFile.Name()) + tempFile.Close() + + // Save original values + originalInputFormat := inputFormat + originalOutputFormat := outputFormat + defer func() { + inputFormat = originalInputFormat + outputFormat = originalOutputFormat + }() + + inputFormat = "auto" + outputFormat = "invalid" + + cmd := &cobra.Command{} + expr, args, err := initCommand(cmd, []string{tempFile.Name()}) + if err == nil { + t.Errorf("initCommand() expected error but got none") + } + if expr != "" { + t.Errorf("initCommand() expected empty expression but got %v", expr) + } + if args != nil { + t.Errorf("initCommand() expected nil args but got %v", args) + } +} + +func TestInitCommandWithUnknownInputFormat(t *testing.T) { + // Create a temporary file with unknown extension + tempFile, err := os.CreateTemp("", "test.unknown") + if err != nil { + t.Fatalf("Failed to create temp file: %v", err) + } + defer os.Remove(tempFile.Name()) + tempFile.Close() + + // Save original values + originalInputFormat := inputFormat + originalOutputFormat := outputFormat + defer func() { + inputFormat = originalInputFormat + outputFormat = originalOutputFormat + }() + + inputFormat = "auto" + outputFormat = "auto" + + cmd := &cobra.Command{} + expr, args, err := initCommand(cmd, []string{tempFile.Name()}) + if err != nil { + t.Errorf("initCommand() unexpected error: %v", err) + return + } + + // expr can be empty when no expression is provided + _ = expr + if args == nil { + t.Errorf("initCommand() expected non-nil args") + } +} + +func TestConfigurePrinterWriterWithInvalidSplitExpression(t *testing.T) { + // Save original value + originalSplitFileExp := splitFileExp + defer func() { splitFileExp = originalSplitFileExp }() + + splitFileExp = "[invalid expression" + + writer, err := configurePrinterWriter(&yqlib.Format{}, os.Stdout) + if err == nil { + t.Errorf("configurePrinterWriter() expected error but got none") + } + if writer != nil { + t.Errorf("configurePrinterWriter() expected nil writer but got %v", writer) + } + if err != nil && !strings.Contains(err.Error(), "bad split document expression") { + t.Errorf("configurePrinterWriter() error '%v' does not contain expected message", err.Error()) + } +} + +func TestMaybeFileWithDirectory(t *testing.T) { + // Create a temporary directory + tempDir, err := os.MkdirTemp("", "test") + if err != nil { + t.Fatalf("Failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + result := maybeFile(tempDir) + if result { + t.Errorf("maybeFile(%q) = %v, want false", tempDir, result) + } +} + +func TestProcessStdInArgsWithDash(t *testing.T) { + args := []string{"-", "file1"} + result := processStdInArgs(args) + if !stringsEqual(result, args) { + t.Errorf("processStdInArgs() = %v, want %v", result, args) + } +} + +func TestProcessArgsWithYqFileExtension(t *testing.T) { + tempYqFile, err := os.Create("test.yq") + if err != nil { + t.Fatalf("Failed to create temp yq file: %v", err) + } + defer os.Remove(tempYqFile.Name()) + if _, err = tempYqFile.WriteString(".a.b"); err != nil { + t.Fatalf("Failed to write to temp yq file: %v", err) + } + tempYqFile.Close() + + // Save original values + originalExpressionFile := expressionFile + originalForceExpression := forceExpression + defer func() { + expressionFile = originalExpressionFile + forceExpression = originalForceExpression + }() + + // Reset expressionFile to empty to test the auto-detection + expressionFile = "" + forceExpression = "" + + // Debug: check the conditions manually + t.Logf("expressionFile: %q", expressionFile) + t.Logf("forceExpression: %q", forceExpression) + t.Logf("tempYqFile.Name(): %q", tempYqFile.Name()) + t.Logf("strings.HasSuffix(tempYqFile.Name(), '.yq'): %v", strings.HasSuffix(tempYqFile.Name(), ".yq")) + t.Logf("maybeFile(tempYqFile.Name()): %v", maybeFile(tempYqFile.Name())) + + // Test with only the yq file as argument (should be treated as expression file) + expr, args, err := processArgs([]string{tempYqFile.Name()}) + if err != nil { + t.Errorf("processArgs() unexpected error: %v", err) + return + } + + if expr != ".a.b" { + t.Errorf("processArgs() expression = %v, want .a.b", expr) + } + + expectedArgs := []string{} + if !stringsEqual(args, expectedArgs) { + t.Errorf("processArgs() args = %v, want %v", args, expectedArgs) + } +} + +func TestConfigureEncoderWithYamlFormat(t *testing.T) { + // Save original values + originalOutputFormat := outputFormat + originalIndent := indent + originalUnwrapScalar := unwrapScalar + originalColorsEnabled := colorsEnabled + originalNoDocSeparators := noDocSeparators + defer func() { + outputFormat = originalOutputFormat + indent = originalIndent + unwrapScalar = originalUnwrapScalar + colorsEnabled = originalColorsEnabled + noDocSeparators = originalNoDocSeparators + }() + + outputFormat = "yaml" + indent = 4 + unwrapScalar = true + colorsEnabled = true + noDocSeparators = true + + encoder, err := configureEncoder() + if err != nil { + t.Errorf("configureEncoder() unexpected error: %v", err) + return + } + + if encoder == nil { + t.Errorf("configureEncoder() expected encoder but got nil") + } +} + +func TestConfigureEncoderWithPropertiesFormat(t *testing.T) { + // Save original values + originalOutputFormat := outputFormat + originalIndent := indent + originalUnwrapScalar := unwrapScalar + originalColorsEnabled := colorsEnabled + originalNoDocSeparators := noDocSeparators + defer func() { + outputFormat = originalOutputFormat + indent = originalIndent + unwrapScalar = originalUnwrapScalar + colorsEnabled = originalColorsEnabled + noDocSeparators = originalNoDocSeparators + }() + + outputFormat = "properties" + indent = 2 + unwrapScalar = false + colorsEnabled = false + noDocSeparators = false + + encoder, err := configureEncoder() + if err != nil { + t.Errorf("configureEncoder() unexpected error: %v", err) + return + } + + if encoder == nil { + t.Errorf("configureEncoder() expected encoder but got nil") + } +} + +// Helper function to compare string slices +func stringsEqual(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i := range a { + if a[i] != b[i] { + return false + } + } + return true +}