diff --git a/examples/sample_objects.csv b/examples/sample_objects.csv index 7059b75d..84ae60b8 100644 --- a/examples/sample_objects.csv +++ b/examples/sample_objects.csv @@ -1,3 +1,3 @@ name,numberOfCats,likesApples,height -Gary,1,true,168.8 +,1,true,168.8 Samantha's Rabbit,2,false,-188.8 \ No newline at end of file diff --git a/pkg/yqlib/csv_test.go b/pkg/yqlib/csv_test.go index 4ab3306d..58f76c83 100644 --- a/pkg/yqlib/csv_test.go +++ b/pkg/yqlib/csv_test.go @@ -12,7 +12,9 @@ const csvSimple = `name,numberOfCats,likesApples,height Gary,1,true,168.8 Samantha's Rabbit,2,false,-188.8 ` - +const csvMissing = `name,numberOfCats,likesApples,height +,null,,168.8 +` const expectedUpdatedSimpleCsv = `name,numberOfCats,likesApples,height Gary,3,true,168.8 Samantha's Rabbit,2,false,-188.8 @@ -110,6 +112,13 @@ var csvScenarios = []formatScenario{ expected: csvSimpleMissingData, scenarioType: "encode-csv", }, + { + description: "decode csv missing", + skipDoc: true, + input: csvMissing, + expected: csvMissing, + scenarioType: "roundtrip-csv", + }, { description: "Parse CSV into an array of objects", subdescription: "First row is assumed to be the header row.", diff --git a/pkg/yqlib/lib.go b/pkg/yqlib/lib.go index 76e52b03..d678c4ce 100644 --- a/pkg/yqlib/lib.go +++ b/pkg/yqlib/lib.go @@ -249,6 +249,12 @@ func guessTagFromCustomType(node *yaml.Node) string { } func parseSnippet(value string) (*yaml.Node, error) { + if value == "" { + return &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!null", + }, nil + } decoder := NewYamlDecoder(ConfiguredYamlPreferences) err := decoder.Init(strings.NewReader(value)) if err != nil { diff --git a/pkg/yqlib/lib_test.go b/pkg/yqlib/lib_test.go index 810a0611..663afede 100644 --- a/pkg/yqlib/lib_test.go +++ b/pkg/yqlib/lib_test.go @@ -1,6 +1,11 @@ package yqlib -import "testing" +import ( + "testing" + + "github.com/mikefarah/yq/v4/test" + yaml "gopkg.in/yaml.v3" +) func TestGetLogger(t *testing.T) { l := GetLogger() @@ -8,3 +13,69 @@ func TestGetLogger(t *testing.T) { t.Fatal("GetLogger should return the yq logger instance, not a copy") } } + +type parseSnippetScenario struct { + snippet string + expected *yaml.Node +} + +var parseSnippetScenarios = []parseSnippetScenario{ + { + snippet: "", + expected: &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!null", + }, + }, + { + snippet: "3", + expected: &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!int", + Value: "3", + Line: 1, + Column: 1, + }, + }, + { + snippet: "cat", + expected: &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: "cat", + Line: 1, + Column: 1, + }, + }, + { + snippet: "3.1", + expected: &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!float", + Value: "3.1", + Line: 1, + Column: 1, + }, + }, + { + snippet: "true", + expected: &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!bool", + Value: "true", + Line: 1, + Column: 1, + }, + }, +} + +func TestParseSnippet(t *testing.T) { + for _, tt := range parseSnippetScenarios { + actual, err := parseSnippet(tt.snippet) + if err != nil { + t.Error(tt.snippet) + t.Error(err) + } + test.AssertResultComplexWithContext(t, tt.expected, actual, tt.snippet) + } +} diff --git a/pkg/yqlib/operators_test.go b/pkg/yqlib/operators_test.go index f3363dc6..79757895 100644 --- a/pkg/yqlib/operators_test.go +++ b/pkg/yqlib/operators_test.go @@ -31,7 +31,7 @@ type expressionScenario struct { } func TestMain(m *testing.M) { - logging.SetLevel(logging.DEBUG, "") + logging.SetLevel(logging.ERROR, "") Now = func() time.Time { return time.Date(2021, time.May, 19, 1, 2, 3, 4, time.UTC) }