yq/cmd/utils_test.go
2025-07-11 09:59:59 +10:00

931 lines
24 KiB
Go

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
}