2018-12-19 14:29:25 +00:00
|
|
|
package yaml
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
"reflect"
|
|
|
|
"strconv"
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
type MarshalTest struct {
|
|
|
|
A string
|
|
|
|
B int64
|
|
|
|
// Would like to test float64, but it's not supported in go-yaml.
|
|
|
|
// (See https://github.com/go-yaml/yaml/issues/83.)
|
|
|
|
C float32
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMarshal(t *testing.T) {
|
|
|
|
f32String := strconv.FormatFloat(math.MaxFloat32, 'g', -1, 32)
|
|
|
|
s := MarshalTest{"a", math.MaxInt64, math.MaxFloat32}
|
|
|
|
e := []byte(fmt.Sprintf("A: a\nB: %d\nC: %s\n", math.MaxInt64, f32String))
|
|
|
|
|
|
|
|
y, err := Marshal(s)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("error marshaling YAML: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(y, e) {
|
|
|
|
t.Errorf("marshal YAML was unsuccessful, expected: %#v, got: %#v",
|
|
|
|
string(e), string(y))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type UnmarshalString struct {
|
|
|
|
A string
|
|
|
|
True string
|
|
|
|
}
|
|
|
|
|
|
|
|
type UnmarshalStringMap struct {
|
|
|
|
A map[string]string
|
|
|
|
}
|
|
|
|
|
|
|
|
type UnmarshalNestedString struct {
|
|
|
|
A NestedString
|
|
|
|
}
|
|
|
|
|
|
|
|
type NestedString struct {
|
|
|
|
A string
|
|
|
|
}
|
|
|
|
|
|
|
|
type UnmarshalSlice struct {
|
|
|
|
A []NestedSlice
|
|
|
|
}
|
|
|
|
|
|
|
|
type NestedSlice struct {
|
|
|
|
B string
|
|
|
|
C *string
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestUnmarshal(t *testing.T) {
|
|
|
|
y := []byte("a: 1")
|
|
|
|
s1 := UnmarshalString{}
|
|
|
|
e1 := UnmarshalString{A: "1"}
|
|
|
|
unmarshal(t, y, &s1, &e1)
|
|
|
|
|
|
|
|
y = []byte("a: true")
|
|
|
|
s1 = UnmarshalString{}
|
|
|
|
e1 = UnmarshalString{A: "true"}
|
|
|
|
unmarshal(t, y, &s1, &e1)
|
|
|
|
|
|
|
|
y = []byte("true: 1")
|
|
|
|
s1 = UnmarshalString{}
|
|
|
|
e1 = UnmarshalString{True: "1"}
|
|
|
|
unmarshal(t, y, &s1, &e1)
|
|
|
|
|
|
|
|
y = []byte("a:\n a: 1")
|
|
|
|
s2 := UnmarshalNestedString{}
|
|
|
|
e2 := UnmarshalNestedString{NestedString{"1"}}
|
|
|
|
unmarshal(t, y, &s2, &e2)
|
|
|
|
|
|
|
|
y = []byte("a:\n - b: abc\n c: def\n - b: 123\n c: 456\n")
|
|
|
|
s3 := UnmarshalSlice{}
|
|
|
|
e3 := UnmarshalSlice{[]NestedSlice{NestedSlice{"abc", strPtr("def")}, NestedSlice{"123", strPtr("456")}}}
|
|
|
|
unmarshal(t, y, &s3, &e3)
|
|
|
|
|
|
|
|
y = []byte("a:\n b: 1")
|
|
|
|
s4 := UnmarshalStringMap{}
|
|
|
|
e4 := UnmarshalStringMap{map[string]string{"b": "1"}}
|
|
|
|
unmarshal(t, y, &s4, &e4)
|
|
|
|
|
|
|
|
y = []byte(`
|
|
|
|
a:
|
|
|
|
name: TestA
|
|
|
|
b:
|
|
|
|
name: TestB
|
|
|
|
`)
|
|
|
|
type NamedThing struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
}
|
|
|
|
s5 := map[string]*NamedThing{}
|
|
|
|
e5 := map[string]*NamedThing{
|
|
|
|
"a": &NamedThing{Name: "TestA"},
|
|
|
|
"b": &NamedThing{Name: "TestB"},
|
|
|
|
}
|
|
|
|
unmarshal(t, y, &s5, &e5)
|
|
|
|
}
|
|
|
|
|
2019-01-15 16:20:41 +00:00
|
|
|
func unmarshal(t *testing.T, y []byte, s, e interface{}, opts ...JSONOpt) {
|
|
|
|
err := Unmarshal(y, s, opts...)
|
2018-12-19 14:29:25 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("error unmarshaling YAML: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(s, e) {
|
|
|
|
t.Errorf("unmarshal YAML was unsuccessful, expected: %+#v, got: %+#v",
|
|
|
|
e, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-15 16:20:41 +00:00
|
|
|
func TestUnmarshalStrict(t *testing.T) {
|
|
|
|
y := []byte("a: 1")
|
|
|
|
s1 := UnmarshalString{}
|
|
|
|
e1 := UnmarshalString{A: "1"}
|
|
|
|
unmarshalStrict(t, y, &s1, &e1)
|
|
|
|
|
|
|
|
y = []byte("a: true")
|
|
|
|
s1 = UnmarshalString{}
|
|
|
|
e1 = UnmarshalString{A: "true"}
|
|
|
|
unmarshalStrict(t, y, &s1, &e1)
|
|
|
|
|
|
|
|
y = []byte("true: 1")
|
|
|
|
s1 = UnmarshalString{}
|
|
|
|
e1 = UnmarshalString{True: "1"}
|
|
|
|
unmarshalStrict(t, y, &s1, &e1)
|
|
|
|
|
|
|
|
y = []byte("a:\n a: 1")
|
|
|
|
s2 := UnmarshalNestedString{}
|
|
|
|
e2 := UnmarshalNestedString{NestedString{"1"}}
|
|
|
|
unmarshalStrict(t, y, &s2, &e2)
|
|
|
|
|
|
|
|
y = []byte("a:\n - b: abc\n c: def\n - b: 123\n c: 456\n")
|
|
|
|
s3 := UnmarshalSlice{}
|
|
|
|
e3 := UnmarshalSlice{[]NestedSlice{NestedSlice{"abc", strPtr("def")}, NestedSlice{"123", strPtr("456")}}}
|
|
|
|
unmarshalStrict(t, y, &s3, &e3)
|
|
|
|
|
|
|
|
y = []byte("a:\n b: 1")
|
|
|
|
s4 := UnmarshalStringMap{}
|
|
|
|
e4 := UnmarshalStringMap{map[string]string{"b": "1"}}
|
|
|
|
unmarshalStrict(t, y, &s4, &e4)
|
|
|
|
|
|
|
|
y = []byte(`
|
|
|
|
a:
|
|
|
|
name: TestA
|
|
|
|
b:
|
|
|
|
name: TestB
|
|
|
|
`)
|
|
|
|
type NamedThing struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
}
|
|
|
|
s5 := map[string]*NamedThing{}
|
|
|
|
e5 := map[string]*NamedThing{
|
|
|
|
"a": &NamedThing{Name: "TestA"},
|
|
|
|
"b": &NamedThing{Name: "TestB"},
|
|
|
|
}
|
|
|
|
unmarshal(t, y, &s5, &e5)
|
|
|
|
|
|
|
|
// When using not-so-strict unmarshal, we should
|
|
|
|
// be picking up the ID-1 as the value in the "id" field
|
|
|
|
y = []byte(`
|
|
|
|
a:
|
|
|
|
name: TestA
|
|
|
|
id: ID-A
|
|
|
|
id: ID-1
|
|
|
|
`)
|
|
|
|
type NamedThing2 struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
ID string `json:"id"`
|
|
|
|
}
|
|
|
|
s6 := map[string]*NamedThing2{}
|
|
|
|
e6 := map[string]*NamedThing2{
|
|
|
|
"a": {Name: "TestA", ID: "ID-1"},
|
|
|
|
}
|
|
|
|
unmarshal(t, y, &s6, &e6)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestUnmarshalStrictFails(t *testing.T) {
|
|
|
|
y := []byte("a: true\na: false")
|
|
|
|
s1 := UnmarshalString{}
|
|
|
|
unmarshalStrictFail(t, y, &s1)
|
|
|
|
|
|
|
|
y = []byte("a:\n - b: abc\n c: 32\n b: 123")
|
|
|
|
s2 := UnmarshalSlice{}
|
|
|
|
unmarshalStrictFail(t, y, &s2)
|
|
|
|
|
|
|
|
y = []byte("a:\n b: 1\n c: 3")
|
|
|
|
s3 := UnmarshalStringMap{}
|
|
|
|
unmarshalStrictFail(t, y, &s3)
|
|
|
|
|
|
|
|
type NamedThing struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
ID string `json:"id"`
|
|
|
|
}
|
|
|
|
// When using strict unmarshal, we should see
|
|
|
|
// the unmarshal fail if there are multiple keys
|
|
|
|
y = []byte(`
|
|
|
|
a:
|
|
|
|
name: TestA
|
|
|
|
id: ID-A
|
|
|
|
id: ID-1
|
|
|
|
`)
|
|
|
|
s4 := NamedThing{}
|
|
|
|
unmarshalStrictFail(t, y, &s4)
|
|
|
|
|
|
|
|
// Strict unmarshal should fail for unknown fields
|
|
|
|
y = []byte(`
|
|
|
|
name: TestB
|
|
|
|
id: ID-B
|
|
|
|
unknown: Some-Value
|
|
|
|
`)
|
|
|
|
s5 := NamedThing{}
|
|
|
|
unmarshalStrictFail(t, y, &s5)
|
|
|
|
}
|
|
|
|
|
|
|
|
func unmarshalStrict(t *testing.T, y []byte, s, e interface{}, opts ...JSONOpt) {
|
|
|
|
err := UnmarshalStrict(y, s, opts...)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("error unmarshaling YAML: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(s, e) {
|
|
|
|
t.Errorf("unmarshal YAML was unsuccessful, expected: %+#v, got: %+#v",
|
|
|
|
e, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func unmarshalStrictFail(t *testing.T, y []byte, s interface{}, opts ...JSONOpt) {
|
|
|
|
err := UnmarshalStrict(y, s, opts...)
|
|
|
|
if err == nil {
|
|
|
|
t.Errorf("error unmarshaling YAML: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-19 14:29:25 +00:00
|
|
|
type Case struct {
|
|
|
|
input string
|
|
|
|
output string
|
|
|
|
// By default we test that reversing the output == input. But if there is a
|
|
|
|
// difference in the reversed output, you can optionally specify it here.
|
|
|
|
reverse *string
|
|
|
|
}
|
|
|
|
|
|
|
|
type RunType int
|
|
|
|
|
|
|
|
const (
|
|
|
|
RunTypeJSONToYAML RunType = iota
|
|
|
|
RunTypeYAMLToJSON
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestJSONToYAML(t *testing.T) {
|
|
|
|
cases := []Case{
|
|
|
|
{
|
|
|
|
`{"t":"a"}`,
|
|
|
|
"t: a\n",
|
|
|
|
nil,
|
|
|
|
}, {
|
|
|
|
`{"t":null}`,
|
|
|
|
"t: null\n",
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
runCases(t, RunTypeJSONToYAML, cases)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestYAMLToJSON(t *testing.T) {
|
|
|
|
cases := []Case{
|
|
|
|
{
|
|
|
|
"t: a\n",
|
|
|
|
`{"t":"a"}`,
|
|
|
|
nil,
|
|
|
|
}, {
|
|
|
|
"t: \n",
|
|
|
|
`{"t":null}`,
|
|
|
|
strPtr("t: null\n"),
|
|
|
|
}, {
|
|
|
|
"t: null\n",
|
|
|
|
`{"t":null}`,
|
|
|
|
nil,
|
|
|
|
}, {
|
|
|
|
"1: a\n",
|
|
|
|
`{"1":"a"}`,
|
|
|
|
strPtr("\"1\": a\n"),
|
|
|
|
}, {
|
|
|
|
"1000000000000000000000000000000000000: a\n",
|
|
|
|
`{"1e+36":"a"}`,
|
|
|
|
strPtr("\"1e+36\": a\n"),
|
|
|
|
}, {
|
|
|
|
"1e+36: a\n",
|
|
|
|
`{"1e+36":"a"}`,
|
|
|
|
strPtr("\"1e+36\": a\n"),
|
|
|
|
}, {
|
|
|
|
"\"1e+36\": a\n",
|
|
|
|
`{"1e+36":"a"}`,
|
|
|
|
nil,
|
|
|
|
}, {
|
|
|
|
"\"1.2\": a\n",
|
|
|
|
`{"1.2":"a"}`,
|
|
|
|
nil,
|
|
|
|
}, {
|
|
|
|
"- t: a\n",
|
|
|
|
`[{"t":"a"}]`,
|
|
|
|
nil,
|
|
|
|
}, {
|
|
|
|
"- t: a\n" +
|
|
|
|
"- t:\n" +
|
|
|
|
" b: 1\n" +
|
|
|
|
" c: 2\n",
|
|
|
|
`[{"t":"a"},{"t":{"b":1,"c":2}}]`,
|
|
|
|
nil,
|
|
|
|
}, {
|
|
|
|
`[{t: a}, {t: {b: 1, c: 2}}]`,
|
|
|
|
`[{"t":"a"},{"t":{"b":1,"c":2}}]`,
|
|
|
|
strPtr("- t: a\n" +
|
|
|
|
"- t:\n" +
|
|
|
|
" b: 1\n" +
|
|
|
|
" c: 2\n"),
|
|
|
|
}, {
|
|
|
|
"- t: \n",
|
|
|
|
`[{"t":null}]`,
|
|
|
|
strPtr("- t: null\n"),
|
|
|
|
}, {
|
|
|
|
"- t: null\n",
|
|
|
|
`[{"t":null}]`,
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cases that should produce errors.
|
|
|
|
_ = []Case{
|
|
|
|
{
|
|
|
|
"~: a",
|
|
|
|
`{"null":"a"}`,
|
|
|
|
nil,
|
|
|
|
}, {
|
|
|
|
"a: !!binary gIGC\n",
|
|
|
|
"{\"a\":\"\x80\x81\x82\"}",
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
runCases(t, RunTypeYAMLToJSON, cases)
|
|
|
|
}
|
|
|
|
|
|
|
|
func runCases(t *testing.T, runType RunType, cases []Case) {
|
|
|
|
var f func([]byte) ([]byte, error)
|
|
|
|
var invF func([]byte) ([]byte, error)
|
|
|
|
var msg string
|
|
|
|
var invMsg string
|
|
|
|
if runType == RunTypeJSONToYAML {
|
|
|
|
f = JSONToYAML
|
|
|
|
invF = YAMLToJSON
|
|
|
|
msg = "JSON to YAML"
|
|
|
|
invMsg = "YAML back to JSON"
|
|
|
|
} else {
|
|
|
|
f = YAMLToJSON
|
|
|
|
invF = JSONToYAML
|
|
|
|
msg = "YAML to JSON"
|
|
|
|
invMsg = "JSON back to YAML"
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range cases {
|
|
|
|
// Convert the string.
|
|
|
|
t.Logf("converting %s\n", c.input)
|
|
|
|
output, err := f([]byte(c.input))
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Failed to convert %s, input: `%s`, err: %v", msg, c.input, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check it against the expected output.
|
|
|
|
if string(output) != c.output {
|
|
|
|
t.Errorf("Failed to convert %s, input: `%s`, expected `%s`, got `%s`",
|
|
|
|
msg, c.input, c.output, string(output))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the string that we will compare the reversed output to.
|
|
|
|
reverse := c.input
|
|
|
|
// If a special reverse string was specified, use that instead.
|
|
|
|
if c.reverse != nil {
|
|
|
|
reverse = *c.reverse
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reverse the output.
|
|
|
|
input, err := invF(output)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Failed to convert %s, input: `%s`, err: %v", invMsg, string(output), err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the reverse is equal to the input (or to *c.reverse).
|
|
|
|
if string(input) != reverse {
|
|
|
|
t.Errorf("Failed to convert %s, input: `%s`, expected `%s`, got `%s`",
|
|
|
|
invMsg, string(output), reverse, string(input))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// To be able to easily fill in the *Case.reverse string above.
|
|
|
|
func strPtr(s string) *string {
|
|
|
|
return &s
|
|
|
|
}
|
2019-01-15 16:20:41 +00:00
|
|
|
|
|
|
|
func TestYAMLToJSONStrict(t *testing.T) {
|
|
|
|
const data = `
|
|
|
|
foo: bar
|
|
|
|
foo: baz
|
|
|
|
`
|
|
|
|
if _, err := YAMLToJSON([]byte(data)); err != nil {
|
|
|
|
t.Error("expected YAMLtoJSON to pass on duplicate field names")
|
|
|
|
}
|
|
|
|
if _, err := YAMLToJSONStrict([]byte(data)); err == nil {
|
|
|
|
t.Error("expected YAMLtoJSONStrict to fail on duplicate field names")
|
|
|
|
}
|
|
|
|
}
|