From 7f72595a12fbc7bc2870804fd5e502a63a85bdbf Mon Sep 17 00:00:00 2001 From: Mike Farah Date: Sun, 12 Oct 2025 15:38:40 +1100 Subject: [PATCH] Cursor generated unit tests --- .gitignore | 1 + cmd/evaluate_all_command_test.go | 328 +++++++++++++++++++++++ cmd/evaluate_sequence_command_test.go | 276 +++++++++++++++++++ cmd/root_test.go | 264 ++++++++++++++++++ pkg/yqlib/candidate_node_test.go | 3 - pkg/yqlib/chown_linux_test.go | 139 ++++++++++ pkg/yqlib/color_print_test.go | 153 +++++++++++ pkg/yqlib/data_tree_navigator_test.go | 2 - pkg/yqlib/printer_test.go | 3 - pkg/yqlib/write_in_place_handler_test.go | 222 +++++++++++++++ scripts/coverage.sh | 66 +++++ yq_test.go | 187 +++++++++++++ 12 files changed, 1636 insertions(+), 8 deletions(-) create mode 100644 cmd/evaluate_all_command_test.go create mode 100644 cmd/evaluate_sequence_command_test.go create mode 100644 cmd/root_test.go create mode 100644 pkg/yqlib/chown_linux_test.go create mode 100644 pkg/yqlib/color_print_test.go create mode 100644 pkg/yqlib/write_in_place_handler_test.go create mode 100644 yq_test.go diff --git a/.gitignore b/.gitignore index 15b884fe..8bedd020 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ _testmain.go cover.out coverage.out coverage.html +coverage_sorted.txt *.exe *.test *.prof diff --git a/cmd/evaluate_all_command_test.go b/cmd/evaluate_all_command_test.go new file mode 100644 index 00000000..36147245 --- /dev/null +++ b/cmd/evaluate_all_command_test.go @@ -0,0 +1,328 @@ +package cmd + +import ( + "bytes" + "os" + "path/filepath" + "strings" + "testing" +) + +func TestCreateEvaluateAllCommand(t *testing.T) { + cmd := createEvaluateAllCommand() + + if cmd == nil { + t.Fatal("createEvaluateAllCommand returned nil") + } + + // Test basic command properties + if cmd.Use != "eval-all [expression] [yaml_file1]..." { + t.Errorf("Expected Use to be 'eval-all [expression] [yaml_file1]...', got %q", cmd.Use) + } + + if cmd.Short == "" { + t.Error("Expected Short description to be non-empty") + } + + if cmd.Long == "" { + t.Error("Expected Long description to be non-empty") + } + + // Test aliases + expectedAliases := []string{"ea"} + if len(cmd.Aliases) != len(expectedAliases) { + t.Errorf("Expected %d aliases, got %d", len(expectedAliases), len(cmd.Aliases)) + } + + for i, expected := range expectedAliases { + if i >= len(cmd.Aliases) || cmd.Aliases[i] != expected { + t.Errorf("Expected alias %d to be %q, got %q", i, expected, cmd.Aliases[i]) + } + } +} + +func TestEvaluateAll_NoArgs(t *testing.T) { + // Create a temporary command + cmd := createEvaluateAllCommand() + + // Set up command to capture output + var output bytes.Buffer + cmd.SetOut(&output) + + // Test with no arguments and no null input + nullInput = false + defer func() { nullInput = false }() + + err := evaluateAll(cmd, []string{}) + + // Should not error, but should print usage + if err != nil { + t.Errorf("evaluateAll with no args should not error, got: %v", err) + } + + // Should have printed usage information + if output.Len() == 0 { + t.Error("Expected usage information to be printed") + } +} + +func TestEvaluateAll_NullInput(t *testing.T) { + // Create a temporary command + cmd := createEvaluateAllCommand() + + // Set up command to capture output + var output bytes.Buffer + cmd.SetOut(&output) + + // Test with null input + nullInput = true + defer func() { nullInput = false }() + + err := evaluateAll(cmd, []string{}) + + // Should not error when using null input + if err != nil { + t.Errorf("evaluateAll with null input should not error, got: %v", err) + } +} + +func TestEvaluateAll_WithSingleFile(t *testing.T) { + // Create a temporary YAML file + tempDir := t.TempDir() + yamlFile := filepath.Join(tempDir, "test.yaml") + yamlContent := []byte("name: test\nage: 25\n") + err := os.WriteFile(yamlFile, yamlContent, 0600) + if err != nil { + t.Fatalf("Failed to create test YAML file: %v", err) + } + + // Create a temporary command + cmd := createEvaluateAllCommand() + + // Set up command to capture output + var output bytes.Buffer + cmd.SetOut(&output) + + // Test with a single file + err = evaluateAll(cmd, []string{yamlFile}) + + // Should not error + if err != nil { + t.Errorf("evaluateAll with single file should not error, got: %v", err) + } + + // Should have some output + if output.Len() == 0 { + t.Error("Expected output from evaluateAll with single file") + } +} + +func TestEvaluateAll_WithMultipleFiles(t *testing.T) { + // Create temporary YAML files + tempDir := t.TempDir() + + yamlFile1 := filepath.Join(tempDir, "test1.yaml") + yamlContent1 := []byte("name: test1\nage: 25\n") + err := os.WriteFile(yamlFile1, yamlContent1, 0600) + if err != nil { + t.Fatalf("Failed to create test YAML file 1: %v", err) + } + + yamlFile2 := filepath.Join(tempDir, "test2.yaml") + yamlContent2 := []byte("name: test2\nage: 30\n") + err = os.WriteFile(yamlFile2, yamlContent2, 0600) + if err != nil { + t.Fatalf("Failed to create test YAML file 2: %v", err) + } + + // Create a temporary command + cmd := createEvaluateAllCommand() + + // Set up command to capture output + var output bytes.Buffer + cmd.SetOut(&output) + + // Test with multiple files + err = evaluateAll(cmd, []string{yamlFile1, yamlFile2}) + + // Should not error + if err != nil { + t.Errorf("evaluateAll with multiple files should not error, got: %v", err) + } + + // Should have output + if output.Len() == 0 { + t.Error("Expected output from evaluateAll with multiple files") + } +} + +func TestEvaluateAll_WithExpression(t *testing.T) { + // Create a temporary YAML file + tempDir := t.TempDir() + yamlFile := filepath.Join(tempDir, "test.yaml") + yamlContent := []byte("name: test\nage: 25\n") + err := os.WriteFile(yamlFile, yamlContent, 0600) + if err != nil { + t.Fatalf("Failed to create test YAML file: %v", err) + } + + // Create a temporary command + cmd := createEvaluateAllCommand() + + // Set up command to capture output + var output bytes.Buffer + cmd.SetOut(&output) + + // Test with expression + err = evaluateAll(cmd, []string{".name", yamlFile}) + + // Should not error + if err != nil { + t.Errorf("evaluateAll with expression should not error, got: %v", err) + } + + // Should have output + if output.Len() == 0 { + t.Error("Expected output from evaluateAll with expression") + } +} + +func TestEvaluateAll_WriteInPlace(t *testing.T) { + // Create a temporary YAML file + tempDir := t.TempDir() + yamlFile := filepath.Join(tempDir, "test.yaml") + yamlContent := []byte("name: test\nage: 25\n") + err := os.WriteFile(yamlFile, yamlContent, 0600) + if err != nil { + t.Fatalf("Failed to create test YAML file: %v", err) + } + + // Create a temporary command + cmd := createEvaluateAllCommand() + + // Set up command to capture output + var output bytes.Buffer + cmd.SetOut(&output) + + // Enable write in place + originalWriteInplace := writeInplace + writeInplace = true + defer func() { writeInplace = originalWriteInplace }() + + // Test with write in place + err = evaluateAll(cmd, []string{".name = \"updated\"", yamlFile}) + + // Should not error + if err != nil { + t.Errorf("evaluateAll with write in place should not error, got: %v", err) + } + + // Verify the file was updated + updatedContent, err := os.ReadFile(yamlFile) + if err != nil { + t.Fatalf("Failed to read updated file: %v", err) + } + + // Should contain the updated content + if !strings.Contains(string(updatedContent), "updated") { + t.Errorf("Expected file to contain 'updated', got: %s", string(updatedContent)) + } +} + +func TestEvaluateAll_ExitStatus(t *testing.T) { + // Create a temporary YAML file + tempDir := t.TempDir() + yamlFile := filepath.Join(tempDir, "test.yaml") + yamlContent := []byte("name: test\nage: 25\n") + err := os.WriteFile(yamlFile, yamlContent, 0600) + if err != nil { + t.Fatalf("Failed to create test YAML file: %v", err) + } + + // Create a temporary command + cmd := createEvaluateAllCommand() + + // Set up command to capture output + var output bytes.Buffer + cmd.SetOut(&output) + + // Enable exit status + originalExitStatus := exitStatus + exitStatus = true + defer func() { exitStatus = originalExitStatus }() + + // Test with expression that should find no matches + err = evaluateAll(cmd, []string{".nonexistent", yamlFile}) + + // Should error when no matches found and exit status is enabled + if err == nil { + t.Error("Expected error when no matches found and exit status is enabled") + } +} + +func TestEvaluateAll_WithMultipleDocuments(t *testing.T) { + // Create a temporary YAML file with multiple documents + tempDir := t.TempDir() + yamlFile := filepath.Join(tempDir, "test.yaml") + yamlContent := []byte("---\nname: doc1\nage: 25\n---\nname: doc2\nage: 30\n") + err := os.WriteFile(yamlFile, yamlContent, 0600) + if err != nil { + t.Fatalf("Failed to create test YAML file: %v", err) + } + + // Create a temporary command + cmd := createEvaluateAllCommand() + + // Set up command to capture output + var output bytes.Buffer + cmd.SetOut(&output) + + // Test with multiple documents + err = evaluateAll(cmd, []string{".", yamlFile}) + + // Should not error + if err != nil { + t.Errorf("evaluateAll with multiple documents should not error, got: %v", err) + } + + // Should have output + if output.Len() == 0 { + t.Error("Expected output from evaluateAll with multiple documents") + } +} + +func TestEvaluateAll_NulSepOutput(t *testing.T) { + // Create a temporary YAML file + tempDir := t.TempDir() + yamlFile := filepath.Join(tempDir, "test.yaml") + yamlContent := []byte("name: test\nage: 25\n") + err := os.WriteFile(yamlFile, yamlContent, 0600) + if err != nil { + t.Fatalf("Failed to create test YAML file: %v", err) + } + + // Create a temporary command + cmd := createEvaluateAllCommand() + + // Set up command to capture output + var output bytes.Buffer + cmd.SetOut(&output) + + // Enable nul separator output + originalNulSepOutput := nulSepOutput + nulSepOutput = true + defer func() { nulSepOutput = originalNulSepOutput }() + + // Test with nul separator output + err = evaluateAll(cmd, []string{".name", yamlFile}) + + // Should not error + if err != nil { + t.Errorf("evaluateAll with nul separator output should not error, got: %v", err) + } + + // Should have output + if output.Len() == 0 { + t.Error("Expected output from evaluateAll with nul separator output") + } +} diff --git a/cmd/evaluate_sequence_command_test.go b/cmd/evaluate_sequence_command_test.go new file mode 100644 index 00000000..539b4a20 --- /dev/null +++ b/cmd/evaluate_sequence_command_test.go @@ -0,0 +1,276 @@ +package cmd + +import ( + "bytes" + "os" + "path/filepath" + "strings" + "testing" +) + +func TestCreateEvaluateSequenceCommand(t *testing.T) { + cmd := createEvaluateSequenceCommand() + + if cmd == nil { + t.Fatal("createEvaluateSequenceCommand returned nil") + } + + // Test basic command properties + if cmd.Use != "eval [expression] [yaml_file1]..." { + t.Errorf("Expected Use to be 'eval [expression] [yaml_file1]...', got %q", cmd.Use) + } + + if cmd.Short == "" { + t.Error("Expected Short description to be non-empty") + } + + if cmd.Long == "" { + t.Error("Expected Long description to be non-empty") + } + + // Test aliases + expectedAliases := []string{"e"} + if len(cmd.Aliases) != len(expectedAliases) { + t.Errorf("Expected %d aliases, got %d", len(expectedAliases), len(cmd.Aliases)) + } + + for i, expected := range expectedAliases { + if i >= len(cmd.Aliases) || cmd.Aliases[i] != expected { + t.Errorf("Expected alias %d to be %q, got %q", i, expected, cmd.Aliases[i]) + } + } +} + +func TestProcessExpression(t *testing.T) { + // Reset global variables + originalPrettyPrint := prettyPrint + defer func() { prettyPrint = originalPrettyPrint }() + + tests := []struct { + name string + prettyPrint bool + expression string + expected string + }{ + { + name: "empty expression without pretty print", + prettyPrint: false, + expression: "", + expected: "", + }, + { + name: "empty expression with pretty print", + prettyPrint: true, + expression: "", + expected: `(... | (select(tag != "!!str"), select(tag == "!!str") | select(test("(?i)^(y|yes|n|no|on|off)$") | not)) ) style=""`, + }, + { + name: "simple expression without pretty print", + prettyPrint: false, + expression: ".a.b", + expected: ".a.b", + }, + { + name: "simple expression with pretty print", + prettyPrint: true, + expression: ".a.b", + expected: `.a.b | (... | (select(tag != "!!str"), select(tag == "!!str") | select(test("(?i)^(y|yes|n|no|on|off)$") | not)) ) style=""`, + }, + { + name: "complex expression with pretty print", + prettyPrint: true, + expression: ".items[] | select(.active == true)", + expected: `.items[] | select(.active == true) | (... | (select(tag != "!!str"), select(tag == "!!str") | select(test("(?i)^(y|yes|n|no|on|off)$") | not)) ) style=""`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + prettyPrint = tt.prettyPrint + result := processExpression(tt.expression) + if result != tt.expected { + t.Errorf("processExpression(%q) = %q, want %q", tt.expression, result, tt.expected) + } + }) + } +} + +func TestEvaluateSequence_NoArgs(t *testing.T) { + // Create a temporary command + cmd := createEvaluateSequenceCommand() + + // Set up command to capture output + var output bytes.Buffer + cmd.SetOut(&output) + + // Test with no arguments and no null input + nullInput = false + defer func() { nullInput = false }() + + err := evaluateSequence(cmd, []string{}) + + // Should not error, but should print usage + if err != nil { + t.Errorf("evaluateSequence with no args should not error, got: %v", err) + } + + // Should have printed usage information + if output.Len() == 0 { + t.Error("Expected usage information to be printed") + } +} + +func TestEvaluateSequence_NullInput(t *testing.T) { + // Create a temporary command + cmd := createEvaluateSequenceCommand() + + // Set up command to capture output + var output bytes.Buffer + cmd.SetOut(&output) + + // Test with null input + nullInput = true + defer func() { nullInput = false }() + + err := evaluateSequence(cmd, []string{}) + + // Should not error when using null input + if err != nil { + t.Errorf("evaluateSequence with null input should not error, got: %v", err) + } +} + +func TestEvaluateSequence_WithFile(t *testing.T) { + // Create a temporary YAML file + tempDir := t.TempDir() + yamlFile := filepath.Join(tempDir, "test.yaml") + yamlContent := []byte("name: test\nage: 25\n") + err := os.WriteFile(yamlFile, yamlContent, 0600) + if err != nil { + t.Fatalf("Failed to create test YAML file: %v", err) + } + + // Create a temporary command + cmd := createEvaluateSequenceCommand() + + // Set up command to capture output + var output bytes.Buffer + cmd.SetOut(&output) + + // Test with a file + err = evaluateSequence(cmd, []string{yamlFile}) + + // Should not error + if err != nil { + t.Errorf("evaluateSequence with file should not error, got: %v", err) + } + + // Should have some output + if output.Len() == 0 { + t.Error("Expected output from evaluateSequence with file") + } +} + +func TestEvaluateSequence_WithExpressionAndFile(t *testing.T) { + // Create a temporary YAML file + tempDir := t.TempDir() + yamlFile := filepath.Join(tempDir, "test.yaml") + yamlContent := []byte("name: test\nage: 25\n") + err := os.WriteFile(yamlFile, yamlContent, 0600) + if err != nil { + t.Fatalf("Failed to create test YAML file: %v", err) + } + + // Create a temporary command + cmd := createEvaluateSequenceCommand() + + // Set up command to capture output + var output bytes.Buffer + cmd.SetOut(&output) + + // Test with expression and file + err = evaluateSequence(cmd, []string{".name", yamlFile}) + + // Should not error + if err != nil { + t.Errorf("evaluateSequence with expression and file should not error, got: %v", err) + } + + // Should have output + if output.Len() == 0 { + t.Error("Expected output from evaluateSequence with expression and file") + } +} + +func TestEvaluateSequence_WriteInPlace(t *testing.T) { + // Create a temporary YAML file + tempDir := t.TempDir() + yamlFile := filepath.Join(tempDir, "test.yaml") + yamlContent := []byte("name: test\nage: 25\n") + err := os.WriteFile(yamlFile, yamlContent, 0600) + if err != nil { + t.Fatalf("Failed to create test YAML file: %v", err) + } + + // Create a temporary command + cmd := createEvaluateSequenceCommand() + + // Set up command to capture output + var output bytes.Buffer + cmd.SetOut(&output) + + // Enable write in place + originalWriteInplace := writeInplace + writeInplace = true + defer func() { writeInplace = originalWriteInplace }() + + // Test with write in place + err = evaluateSequence(cmd, []string{".name = \"updated\"", yamlFile}) + + // Should not error + if err != nil { + t.Errorf("evaluateSequence with write in place should not error, got: %v", err) + } + + // Verify the file was updated + updatedContent, err := os.ReadFile(yamlFile) + if err != nil { + t.Fatalf("Failed to read updated file: %v", err) + } + + // Should contain the updated content + if !strings.Contains(string(updatedContent), "updated") { + t.Errorf("Expected file to contain 'updated', got: %s", string(updatedContent)) + } +} + +func TestEvaluateSequence_ExitStatus(t *testing.T) { + // Create a temporary YAML file + tempDir := t.TempDir() + yamlFile := filepath.Join(tempDir, "test.yaml") + yamlContent := []byte("name: test\nage: 25\n") + err := os.WriteFile(yamlFile, yamlContent, 0600) + if err != nil { + t.Fatalf("Failed to create test YAML file: %v", err) + } + + // Create a temporary command + cmd := createEvaluateSequenceCommand() + + // Set up command to capture output + var output bytes.Buffer + cmd.SetOut(&output) + + // Enable exit status + originalExitStatus := exitStatus + exitStatus = true + defer func() { exitStatus = originalExitStatus }() + + // Test with expression that should find no matches + err = evaluateSequence(cmd, []string{".nonexistent", yamlFile}) + + // Should error when no matches found and exit status is enabled + if err == nil { + t.Error("Expected error when no matches found and exit status is enabled") + } +} diff --git a/cmd/root_test.go b/cmd/root_test.go new file mode 100644 index 00000000..c141f800 --- /dev/null +++ b/cmd/root_test.go @@ -0,0 +1,264 @@ +package cmd + +import ( + "strings" + "testing" +) + +func TestNewRuneVar(t *testing.T) { + var r rune + runeVar := newRuneVar(&r) + + if runeVar == nil { + t.Fatal("newRuneVar returned nil") + } +} + +func TestRuneValue_String(t *testing.T) { + tests := []struct { + name string + runeVal rune + expected string + }{ + { + name: "simple character", + runeVal: 'a', + expected: "a", + }, + { + name: "special character", + runeVal: '\n', + expected: "\n", + }, + { + name: "unicode character", + runeVal: 'ñ', + expected: "ñ", + }, + { + name: "zero rune", + runeVal: 0, + expected: string(rune(0)), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + runeVal := runeValue(tt.runeVal) + result := runeVal.String() + if result != tt.expected { + t.Errorf("runeValue.String() = %q, want %q", result, tt.expected) + } + }) + } +} + +func TestRuneValue_Set(t *testing.T) { + tests := []struct { + name string + input string + expected rune + expectError bool + }{ + { + name: "simple character", + input: "a", + expected: 'a', + expectError: false, + }, + { + name: "newline escape", + input: "\\n", + expected: '\n', + expectError: false, + }, + { + name: "tab escape", + input: "\\t", + expected: '\t', + expectError: false, + }, + { + name: "carriage return escape", + input: "\\r", + expected: '\r', + expectError: false, + }, + { + name: "form feed escape", + input: "\\f", + expected: '\f', + expectError: false, + }, + { + name: "vertical tab escape", + input: "\\v", + expected: '\v', + expectError: false, + }, + { + name: "empty string", + input: "", + expected: 0, + expectError: true, + }, + { + name: "multiple characters", + input: "ab", + expected: 0, + expectError: true, + }, + { + name: "special character", + input: "ñ", + expected: 'ñ', + expectError: true, // This will fail because the Set function checks len(val) != 1 + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var r rune + runeVal := newRuneVar(&r) + + err := runeVal.Set(tt.input) + + if tt.expectError { + if err == nil { + t.Errorf("Expected error for input %q, but got none", tt.input) + } + } else { + if err != nil { + t.Errorf("Unexpected error for input %q: %v", tt.input, err) + } + if r != tt.expected { + t.Errorf("Expected rune %q (%d), got %q (%d)", + string(tt.expected), tt.expected, string(r), r) + } + } + }) + } +} + +func TestRuneValue_Set_ErrorMessages(t *testing.T) { + tests := []struct { + name string + input string + expectedError string + }{ + { + name: "empty string error", + input: "", + expectedError: "[] is not a valid character. Must be length 1 was 0", + }, + { + name: "multiple characters error", + input: "abc", + expectedError: "[abc] is not a valid character. Must be length 1 was 3", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var r rune + runeVal := newRuneVar(&r) + + err := runeVal.Set(tt.input) + + if err == nil { + t.Errorf("Expected error for input %q, but got none", tt.input) + return + } + + if !strings.Contains(err.Error(), tt.expectedError) { + t.Errorf("Expected error message to contain %q, got %q", + tt.expectedError, err.Error()) + } + }) + } +} + +func TestRuneValue_Type(t *testing.T) { + var r rune + runeVal := newRuneVar(&r) + + result := runeVal.Type() + expected := "char" + + if result != expected { + t.Errorf("runeValue.Type() = %q, want %q", result, expected) + } +} + +func TestNew(t *testing.T) { + rootCmd := New() + + if rootCmd == nil { + t.Fatal("New() returned nil") + } + + // Test basic command properties + if rootCmd.Use != "yq" { + t.Errorf("Expected Use to be 'yq', got %q", rootCmd.Use) + } + + if rootCmd.Short == "" { + t.Error("Expected Short description to be non-empty") + } + + if rootCmd.Long == "" { + t.Error("Expected Long description to be non-empty") + } + + // Test that the command has the expected subcommands + expectedCommands := []string{"eval", "eval-all", "completion"} + actualCommands := make([]string, 0, len(rootCmd.Commands())) + + for _, cmd := range rootCmd.Commands() { + actualCommands = append(actualCommands, cmd.Name()) + } + + for _, expected := range expectedCommands { + found := false + for _, actual := range actualCommands { + if actual == expected { + found = true + break + } + } + if !found { + t.Errorf("Expected command %q not found in actual commands: %v", + expected, actualCommands) + } + } +} + +func TestNew_FlagCompletions(t *testing.T) { + rootCmd := New() + + // Test that flag completion functions are registered + // This is a basic smoke test - we can't easily test the actual completion logic + // without more complex setup + flags := []string{ + "output-format", + "input-format", + "xml-attribute-prefix", + "xml-content-name", + "xml-proc-inst-prefix", + "xml-directive-name", + "lua-prefix", + "lua-suffix", + "properties-separator", + "indent", + "front-matter", + "expression", + "split-exp", + } + + for _, flagName := range flags { + flag := rootCmd.PersistentFlags().Lookup(flagName) + if flag == nil { + t.Errorf("Expected flag %q to exist", flagName) + } + } +} diff --git a/pkg/yqlib/candidate_node_test.go b/pkg/yqlib/candidate_node_test.go index 6a256bc3..58f436d7 100644 --- a/pkg/yqlib/candidate_node_test.go +++ b/pkg/yqlib/candidate_node_test.go @@ -152,8 +152,6 @@ func TestCandidateNodeAddKeyValueChild(t *testing.T) { key := CandidateNode{Value: "cool", IsMapKey: true} node := CandidateNode{} - // if we use a key in a new node as a value, it should no longer be marked as a key - _, keyIsValueNow := node.AddKeyValueChild(&CandidateNode{Value: "newKey"}, &key) test.AssertResult(t, keyIsValueNow.IsMapKey, false) @@ -242,7 +240,6 @@ func TestCandidateNodeGetNicePath(t *testing.T) { nicePath = arrayNode.GetNicePath() test.AssertResult(t, "[0]", nicePath) - // Test key with dots (should not be bracketed when it's the first element) dotKey := createStringScalarNode("key.with.dots") dotNode := CandidateNode{Key: dotKey} nicePath = dotNode.GetNicePath() diff --git a/pkg/yqlib/chown_linux_test.go b/pkg/yqlib/chown_linux_test.go new file mode 100644 index 00000000..674b3bd9 --- /dev/null +++ b/pkg/yqlib/chown_linux_test.go @@ -0,0 +1,139 @@ +//go:build linux + +package yqlib + +import ( + "os" + "path/filepath" + "testing" + "time" +) + +func TestChangeOwner(t *testing.T) { + // Create a temporary file for testing + tempDir := t.TempDir() + testFile := filepath.Join(tempDir, "testfile.txt") + + // Create a test file + err := os.WriteFile(testFile, []byte("test content"), 0600) + if err != nil { + t.Fatalf("Failed to create test file: %v", err) + } + + // Get file info + info, err := os.Stat(testFile) + if err != nil { + t.Fatalf("Failed to stat test file: %v", err) + } + + // Create another temporary file to change ownership of + tempFile, err := os.CreateTemp(tempDir, "chown_test_*.txt") + if err != nil { + t.Fatalf("Failed to create temp file: %v", err) + } + defer os.Remove(tempFile.Name()) + tempFile.Close() + + // Test changeOwner function + err = changeOwner(info, tempFile) + if err != nil { + t.Errorf("changeOwner failed: %v", err) + } + + // Verify that the function doesn't panic with valid input + tempFile2, err := os.CreateTemp(tempDir, "chown_test2_*.txt") + if err != nil { + t.Fatalf("Failed to create second temp file: %v", err) + } + defer os.Remove(tempFile2.Name()) + tempFile2.Close() + + // Test with the second file + err = changeOwner(info, tempFile2) + if err != nil { + t.Errorf("changeOwner failed on second file: %v", err) + } +} + +func TestChangeOwnerWithInvalidFileInfo(t *testing.T) { + // Create a mock file info that doesn't have syscall.Stat_t + mockInfo := &mockFileInfo{ + name: "mock", + size: 0, + mode: 0600, + } + + // Create a temporary file + tempFile, err := os.CreateTemp(t.TempDir(), "chown_test_*.txt") + if err != nil { + t.Fatalf("Failed to create temp file: %v", err) + } + defer os.Remove(tempFile.Name()) + tempFile.Close() + + // Test changeOwner with mock file info (should not panic) + err = changeOwner(mockInfo, tempFile) + if err != nil { + t.Errorf("changeOwner failed with mock file info: %v", err) + } +} + +func TestChangeOwnerWithNonExistentFile(t *testing.T) { + // Create a temporary file + tempFile, err := os.CreateTemp(t.TempDir(), "chown_test_*.txt") + if err != nil { + t.Fatalf("Failed to create temp file: %v", err) + } + defer os.Remove(tempFile.Name()) + tempFile.Close() + + // Get file info + info, err := os.Stat(tempFile.Name()) + if err != nil { + t.Fatalf("Failed to stat temp file: %v", err) + } + + // Remove the file + os.Remove(tempFile.Name()) + + err = changeOwner(info, tempFile) + // The function should not panic even if the file doesn't exist + if err != nil { + t.Logf("Expected error when changing owner of non-existent file: %v", err) + } +} + +// mockFileInfo implements fs.FileInfo but doesn't have syscall.Stat_t +type mockFileInfo struct { + name string + size int64 + mode os.FileMode +} + +func (m *mockFileInfo) Name() string { return m.name } +func (m *mockFileInfo) Size() int64 { return m.size } +func (m *mockFileInfo) Mode() os.FileMode { return m.mode } +func (m *mockFileInfo) ModTime() time.Time { return time.Time{} } +func (m *mockFileInfo) IsDir() bool { return false } +func (m *mockFileInfo) Sys() interface{} { return nil } // This will cause the type assertion to fail + +func TestChangeOwnerWithSyscallStatT(t *testing.T) { + // Create a temporary file + tempFile, err := os.CreateTemp(t.TempDir(), "chown_test_*.txt") + if err != nil { + t.Fatalf("Failed to create temp file: %v", err) + } + defer os.Remove(tempFile.Name()) + tempFile.Close() + + // Get file info + info, err := os.Stat(tempFile.Name()) + if err != nil { + t.Fatalf("Failed to stat temp file: %v", err) + } + + err = changeOwner(info, tempFile) + if err != nil { + t.Logf("changeOwner returned error (this might be expected in some environments): %v", err) + } +} diff --git a/pkg/yqlib/color_print_test.go b/pkg/yqlib/color_print_test.go new file mode 100644 index 00000000..904c3d59 --- /dev/null +++ b/pkg/yqlib/color_print_test.go @@ -0,0 +1,153 @@ +package yqlib + +import ( + "bytes" + "strings" + "testing" + + "github.com/fatih/color" +) + +func TestFormat(t *testing.T) { + tests := []struct { + name string + attr color.Attribute + expected string + }{ + { + name: "reset color", + attr: color.Reset, + expected: "\x1b[0m", + }, + { + name: "red color", + attr: color.FgRed, + expected: "\x1b[31m", + }, + { + name: "green color", + attr: color.FgGreen, + expected: "\x1b[32m", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := format(tt.attr) + if result != tt.expected { + t.Errorf("format(%d) = %q, want %q", tt.attr, result, tt.expected) + } + }) + } +} + +func TestColorizeAndPrint(t *testing.T) { + tests := []struct { + name string + yamlBytes []byte + expectErr bool + }{ + { + name: "simple yaml", + yamlBytes: []byte("name: test\nage: 25\n"), + expectErr: false, + }, + { + name: "yaml with strings", + yamlBytes: []byte("name: \"hello world\"\nactive: true\ncount: 42\n"), + expectErr: false, + }, + { + name: "yaml with anchors and aliases", + yamlBytes: []byte("default: &default\n name: test\nuser: *default\n"), + expectErr: false, + }, + { + name: "yaml with comments", + yamlBytes: []byte("# This is a comment\nname: test\n"), + expectErr: false, + }, + { + name: "empty yaml", + yamlBytes: []byte(""), + expectErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var buf bytes.Buffer + err := colorizeAndPrint(tt.yamlBytes, &buf) + + if tt.expectErr && err == nil { + t.Error("Expected error but got none") + } + if !tt.expectErr && err != nil { + t.Errorf("Unexpected error: %v", err) + } + + // Check that output contains escape sequences (color codes) + if !tt.expectErr && len(tt.yamlBytes) > 0 { + output := buf.String() + if !strings.Contains(output, "\x1b[") { + t.Error("Expected output to contain color escape sequences") + } + } + }) + } +} + +func TestColorizeAndPrintWithDifferentYamlTypes(t *testing.T) { + testCases := []struct { + name string + yaml string + expectErr bool + }{ + { + name: "boolean values", + yaml: "active: true\ninactive: false\n", + }, + { + name: "numeric values", + yaml: "integer: 42\nfloat: 3.14\nnegative: -10\n", + }, + { + name: "map keys", + yaml: "user:\n name: john\n age: 30\n", + }, + { + name: "string values", + yaml: "message: \"hello world\"\ndescription: 'single quotes'\n", + }, + { + name: "mixed types", + yaml: "config:\n debug: true\n port: 8080\n host: \"localhost\"\n", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var buf bytes.Buffer + err := colorizeAndPrint([]byte(tc.yaml), &buf) + + if tc.expectErr && err == nil { + t.Error("Expected error but got none") + } + if !tc.expectErr && err != nil { + t.Errorf("Unexpected error: %v", err) + } + + // Verify output contains color codes + if !tc.expectErr { + output := buf.String() + if !strings.Contains(output, "\x1b[") { + t.Error("Expected output to contain color escape sequences") + } + // Should end with newline + if !strings.HasSuffix(output, "\n") { + t.Error("Expected output to end with newline") + } + } + }) + } +} diff --git a/pkg/yqlib/data_tree_navigator_test.go b/pkg/yqlib/data_tree_navigator_test.go index a0e2e59f..f0ac37b9 100644 --- a/pkg/yqlib/data_tree_navigator_test.go +++ b/pkg/yqlib/data_tree_navigator_test.go @@ -312,7 +312,6 @@ func TestDeeplyAssign_ErrorHandling(t *testing.T) { Value: "value", } - // Try to assign to a path on a scalar (should fail) path := []interface{}{"key"} err := navigator.DeeplyAssign(context, path, assignNode) @@ -321,7 +320,6 @@ func TestDeeplyAssign_ErrorHandling(t *testing.T) { t.Logf("Actual error: %v", err) } - // This should fail because we can't assign to a scalar test.AssertResult(t, nil, err) } diff --git a/pkg/yqlib/printer_test.go b/pkg/yqlib/printer_test.go index 6ee305bd..d55c9b41 100644 --- a/pkg/yqlib/printer_test.go +++ b/pkg/yqlib/printer_test.go @@ -452,7 +452,6 @@ func TestPrinterPrintedAnything(t *testing.T) { var writer = bufio.NewWriter(&output) printer := NewSimpleYamlPrinter(writer, true, 2, true) - // Initially should be false test.AssertResult(t, false, printer.PrintedAnything()) // Print a scalar value @@ -495,7 +494,6 @@ func TestPrinterSetNulSepOutput(t *testing.T) { // Test setting NUL separator output printer.SetNulSepOutput(true) - // No direct way to test this, but it should not cause errors test.AssertResult(t, true, true) // Placeholder assertion printer.SetNulSepOutput(false) @@ -511,6 +509,5 @@ func TestPrinterSetAppendix(t *testing.T) { // Test setting appendix appendix := strings.NewReader("appendix content") printer.SetAppendix(appendix) - // No direct way to test this, but it should not cause errors test.AssertResult(t, true, true) // Placeholder assertion } diff --git a/pkg/yqlib/write_in_place_handler_test.go b/pkg/yqlib/write_in_place_handler_test.go new file mode 100644 index 00000000..a15bdf32 --- /dev/null +++ b/pkg/yqlib/write_in_place_handler_test.go @@ -0,0 +1,222 @@ +package yqlib + +import ( + "os" + "path/filepath" + "testing" +) + +func TestWriteInPlaceHandlerImpl_CreateTempFile(t *testing.T) { + // Create a temporary directory and file for testing + tempDir := t.TempDir() + inputFile := filepath.Join(tempDir, "input.yaml") + + // Create input file with some content + content := []byte("test: value\n") + err := os.WriteFile(inputFile, content, 0600) + if err != nil { + t.Fatalf("Failed to create input file: %v", err) + } + + handler := NewWriteInPlaceHandler(inputFile) + tempFile, err := handler.CreateTempFile() + + if err != nil { + t.Fatalf("CreateTempFile failed: %v", err) + } + + if tempFile == nil { + t.Fatal("CreateTempFile returned nil file") + } + + // Clean up + tempFile.Close() + os.Remove(tempFile.Name()) +} + +func TestWriteInPlaceHandlerImpl_CreateTempFile_NonExistentInput(t *testing.T) { + // Test with non-existent input file + handler := NewWriteInPlaceHandler("/non/existent/file.yaml") + tempFile, err := handler.CreateTempFile() + + if err == nil { + t.Error("Expected error for non-existent input file, got nil") + } + + if tempFile != nil { + t.Error("Expected nil temp file for non-existent input file") + tempFile.Close() + } +} + +func TestWriteInPlaceHandlerImpl_FinishWriteInPlace_Success(t *testing.T) { + // Create a temporary directory and file for testing + tempDir := t.TempDir() + inputFile := filepath.Join(tempDir, "input.yaml") + + // Create input file with some content + content := []byte("test: value\n") + err := os.WriteFile(inputFile, content, 0600) + if err != nil { + t.Fatalf("Failed to create input file: %v", err) + } + + handler := NewWriteInPlaceHandler(inputFile) + tempFile, err := handler.CreateTempFile() + if err != nil { + t.Fatalf("CreateTempFile failed: %v", err) + } + defer tempFile.Close() + + // Write some content to temp file + tempContent := []byte("updated: content\n") + _, err = tempFile.Write(tempContent) + if err != nil { + t.Fatalf("Failed to write to temp file: %v", err) + } + tempFile.Close() + + // Test successful finish + err = handler.FinishWriteInPlace(true) + if err != nil { + t.Fatalf("FinishWriteInPlace failed: %v", err) + } + + // Verify the original file was updated + updatedContent, err := os.ReadFile(inputFile) + if err != nil { + t.Fatalf("Failed to read updated file: %v", err) + } + + if string(updatedContent) != string(tempContent) { + t.Errorf("File content not updated correctly. Expected %q, got %q", + string(tempContent), string(updatedContent)) + } +} + +func TestWriteInPlaceHandlerImpl_FinishWriteInPlace_Failure(t *testing.T) { + // Create a temporary directory and file for testing + tempDir := t.TempDir() + inputFile := filepath.Join(tempDir, "input.yaml") + + // Create input file with some content + content := []byte("test: value\n") + err := os.WriteFile(inputFile, content, 0600) + if err != nil { + t.Fatalf("Failed to create input file: %v", err) + } + + handler := NewWriteInPlaceHandler(inputFile) + tempFile, err := handler.CreateTempFile() + if err != nil { + t.Fatalf("CreateTempFile failed: %v", err) + } + defer tempFile.Close() + + // Write some content to temp file + tempContent := []byte("updated: content\n") + _, err = tempFile.Write(tempContent) + if err != nil { + t.Fatalf("Failed to write to temp file: %v", err) + } + tempFile.Close() + + // Test failure finish (should not update the original file) + err = handler.FinishWriteInPlace(false) + if err != nil { + t.Fatalf("FinishWriteInPlace failed: %v", err) + } + + // Verify the original file was NOT updated + originalContent, err := os.ReadFile(inputFile) + if err != nil { + t.Fatalf("Failed to read original file: %v", err) + } + + if string(originalContent) != string(content) { + t.Errorf("File content should not have been updated. Expected %q, got %q", + string(content), string(originalContent)) + } +} + +func TestWriteInPlaceHandlerImpl_CreateTempFile_Permissions(t *testing.T) { + // Create a temporary directory and file for testing + tempDir := t.TempDir() + inputFile := filepath.Join(tempDir, "input.yaml") + + // Create input file with specific permissions + content := []byte("test: value\n") + err := os.WriteFile(inputFile, content, 0600) + if err != nil { + t.Fatalf("Failed to create input file: %v", err) + } + + handler := NewWriteInPlaceHandler(inputFile) + tempFile, err := handler.CreateTempFile() + if err != nil { + t.Fatalf("CreateTempFile failed: %v", err) + } + defer tempFile.Close() + + // Check that temp file has same permissions as input file + tempFileInfo, err := os.Stat(tempFile.Name()) + if err != nil { + t.Fatalf("Failed to stat temp file: %v", err) + } + + inputFileInfo, err := os.Stat(inputFile) + if err != nil { + t.Fatalf("Failed to stat input file: %v", err) + } + + if tempFileInfo.Mode() != inputFileInfo.Mode() { + t.Errorf("Temp file permissions don't match input file. Expected %v, got %v", + inputFileInfo.Mode(), tempFileInfo.Mode()) + } +} + +func TestWriteInPlaceHandlerImpl_Integration(t *testing.T) { + // Create a temporary directory and file for testing + tempDir := t.TempDir() + inputFile := filepath.Join(tempDir, "integration_test.yaml") + + // Create input file with some content + originalContent := []byte("original: content\n") + err := os.WriteFile(inputFile, originalContent, 0600) + if err != nil { + t.Fatalf("Failed to create input file: %v", err) + } + + handler := NewWriteInPlaceHandler(inputFile) + + // Create temp file + tempFile, err := handler.CreateTempFile() + if err != nil { + t.Fatalf("CreateTempFile failed: %v", err) + } + + // Write new content to temp file + newContent := []byte("new: content\n") + _, err = tempFile.Write(newContent) + if err != nil { + t.Fatalf("Failed to write to temp file: %v", err) + } + tempFile.Close() + + // Finish with success + err = handler.FinishWriteInPlace(true) + if err != nil { + t.Fatalf("FinishWriteInPlace failed: %v", err) + } + + // Verify the file was updated + finalContent, err := os.ReadFile(inputFile) + if err != nil { + t.Fatalf("Failed to read final file: %v", err) + } + + if string(finalContent) != string(newContent) { + t.Errorf("File not updated correctly. Expected %q, got %q", + string(newContent), string(finalContent)) + } +} diff --git a/scripts/coverage.sh b/scripts/coverage.sh index 88b4b974..a64e16b5 100755 --- a/scripts/coverage.sh +++ b/scripts/coverage.sh @@ -2,5 +2,71 @@ set -e +echo "Running tests and generating coverage..." go test -coverprofile=coverage.out -v $(go list ./... | grep -v -E 'examples' | grep -v -E 'test') + +echo "Generating HTML coverage report..." go tool cover -html=coverage.out -o coverage.html + +echo "" +echo "Generating sorted coverage table..." + +# Create a simple approach using grep and sed to extract file coverage +# First, get the total coverage +total_coverage=$(go tool cover -func=coverage.out | grep "^total:" | sed 's/.*([^)]*)[[:space:]]*\([0-9.]*\)%.*/\1/') + +# Extract file-level coverage by finding the last occurrence of each file +go tool cover -func=coverage.out | grep -E "\.go:[0-9]+:" | \ +sed 's/^\([^:]*\.go\):.*[[:space:]]\([0-9.]*\)%.*/\2 \1/' | \ +sort -k2 | \ +awk '{file_coverage[$2] = $1} END {for (file in file_coverage) printf "%.2f %s\n", file_coverage[file], file}' | \ +sort -nr > coverage_sorted.txt + +# Add total coverage to the file +if [[ -n "$total_coverage" && "$total_coverage" != "0" ]]; then + echo "TOTAL: $total_coverage" >> coverage_sorted.txt +fi + +echo "" +echo "Coverage Summary (sorted by percentage - lowest coverage first):" +echo "=================================================================" +printf "%-60s %10s %12s\n" "FILE" "COVERAGE" "STATUS" +echo "=================================================================" + +# Display results with status indicators +tail -n +1 coverage_sorted.txt | while read percent file; do + if [[ "$file" == "TOTAL:" ]]; then + echo "" + printf "%-60s %8s%% %12s\n" "OVERALL PROJECT COVERAGE" "$percent" "📊 TOTAL" + echo "=================================================================" + continue + fi + + filename=$(basename "$file") + status="" + if (( $(echo "$percent < 50" | bc -l 2>/dev/null || echo "0") )); then + status="🔴 CRITICAL" + elif (( $(echo "$percent < 70" | bc -l 2>/dev/null || echo "0") )); then + status="🟡 LOW" + elif (( $(echo "$percent < 90" | bc -l 2>/dev/null || echo "0") )); then + status="🟢 GOOD" + else + status="✅ EXCELLENT" + fi + + printf "%-60s %8s%% %12s\n" "$filename" "$percent" "$status" +done + +echo "" +echo "Top 10 files needing attention (lowest coverage):" +echo "=================================================" +grep -v "TOTAL:" coverage_sorted.txt | tail -10 | while read percent file; do + filename=$(basename "$file") + printf "%-60s %8.1f%%\n" "$filename" "$percent" +done + +echo "" +echo "Coverage reports generated:" +echo "- HTML report: coverage.html (detailed line-by-line coverage)" +echo "- Sorted table: coverage_sorted.txt" +echo "- Use 'go tool cover -func=coverage.out' for function-level details" diff --git a/yq_test.go b/yq_test.go new file mode 100644 index 00000000..beb25ef5 --- /dev/null +++ b/yq_test.go @@ -0,0 +1,187 @@ +package main + +import ( + "testing" + + command "github.com/mikefarah/yq/v4/cmd" +) + +func TestMainFunction(t *testing.T) { + // This is a basic smoke test for the main function + // We can't easily test the main function directly since it calls os.Exit + // But we can test the logic that would be executed + + cmd := command.New() + if cmd == nil { + t.Fatal("command.New() returned nil") + } + + if cmd.Use != "yq" { + t.Errorf("Expected command Use to be 'yq', got %q", cmd.Use) + } +} + +func TestMainFunctionLogic(t *testing.T) { + // Test the logic that would be executed in main() + cmd := command.New() + + args := []string{} + _, _, err := cmd.Find(args) + if err != nil { + t.Errorf("Expected no error with empty args, but got: %v", err) + } + + args = []string{"invalid-command"} + _, _, err = cmd.Find(args) + if err == nil { + t.Error("Expected error when invalid command found, but got nil") + } + + args = []string{"eval"} + _, _, err = cmd.Find(args) + if err != nil { + t.Errorf("Expected no error with valid command 'eval', got: %v", err) + } + + args = []string{"__complete"} + _, _, err = cmd.Find(args) + if err == nil { + t.Error("Expected error when no command found for '__complete', but got nil") + } +} + +func TestMainFunctionWithArgs(t *testing.T) { + // Test the argument processing logic + cmd := command.New() + + args := []string{} + _, _, err := cmd.Find(args) + if err != nil { + t.Errorf("Expected no error with empty args, but got: %v", err) + } + + // When Find fails and args[0] is not "__complete", main would set args to ["eval"] + original args + // This is the logic: newArgs := []string{"eval"} + // cmd.SetArgs(append(newArgs, os.Args[1:]...)) + + args = []string{"invalid"} + _, _, err = cmd.Find(args) + if err == nil { + t.Error("Expected error with invalid command") + } + + args = []string{"__complete"} + _, _, err = cmd.Find(args) + if err == nil { + t.Error("Expected error with __complete command") + } +} + +func TestMainFunctionExecution(t *testing.T) { + // Test that the command can be executed without crashing + cmd := command.New() + + cmd.SetArgs([]string{"--version"}) + + // We can't easily test os.Exit(1) behaviour, but we can test that + // the command structure is correct and can be configured + if cmd == nil { + t.Fatal("Command should not be nil") + } + + if cmd.Use != "yq" { + t.Errorf("Expected command Use to be 'yq', got %q", cmd.Use) + } +} + +func TestMainFunctionErrorHandling(t *testing.T) { + // Test the error handling logic that would be in main() + cmd := command.New() + + args := []string{"nonexistent-command"} + _, _, err := cmd.Find(args) + if err == nil { + t.Error("Expected error with nonexistent command") + } + + // The main function logic would be: + // if err != nil && args[0] != "__complete" { + // newArgs := []string{"eval"} + // cmd.SetArgs(append(newArgs, os.Args[1:]...)) + // } + + // Test that this logic would work + if args[0] != "__complete" { + // This is what main() would do + newArgs := []string{"eval"} + cmd.SetArgs(append(newArgs, args...)) + + // We can't easily verify the args were set correctly since cmd.Args is a function + // But we can test that SetArgs doesn't crash and the command is still valid + if cmd == nil { + t.Error("Command should not be nil after SetArgs") + } + + _, _, err := cmd.Find([]string{"eval"}) + if err != nil { + t.Errorf("Should be able to find eval command after SetArgs: %v", err) + } + } +} + +func TestMainFunctionWithCompletionCommand(t *testing.T) { + // Test that __complete command doesn't trigger default command logic + cmd := command.New() + + args := []string{"__complete"} + _, _, err := cmd.Find(args) + if err == nil { + t.Error("Expected error with __complete command") + } + + // The main function logic would be: + // if err != nil && args[0] != "__complete" { + // // This should NOT execute for __complete + // } + + // Verify that __complete doesn't trigger the default command logic + if args[0] == "__complete" { + // This means the default command logic should NOT execute + t.Log("__complete command correctly identified, default command logic should not execute") + } +} + +func TestMainFunctionIntegration(t *testing.T) { + // Integration test to verify the main function logic works end-to-end + + cmd := command.New() + cmd.SetArgs([]string{"eval", "--help"}) + + // This should not crash (we can't test the actual execution due to os.Exit) + if cmd == nil { + t.Fatal("Command should not be nil") + } + + cmd2 := command.New() + cmd2.SetArgs([]string{"invalid-command"}) + + // Simulate the main function logic + args := []string{"invalid-command"} + _, _, err := cmd2.Find(args) + if err != nil { + // This is what main() would do + newArgs := []string{"eval"} + cmd2.SetArgs(append(newArgs, args...)) + } + + // We can't directly access cmd.Args since it's a function, but we can test + // that SetArgs worked by ensuring the command is still functional + if cmd2 == nil { + t.Error("Command should not be nil after SetArgs") + } + + _, _, err = cmd2.Find([]string{"eval"}) + if err != nil { + t.Errorf("Should be able to find eval command after SetArgs: %v", err) + } +}