summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mitchellh/mapstructure/mapstructure_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mitchellh/mapstructure/mapstructure_test.go')
-rw-r--r--vendor/github.com/mitchellh/mapstructure/mapstructure_test.go1193
1 files changed, 1193 insertions, 0 deletions
diff --git a/vendor/github.com/mitchellh/mapstructure/mapstructure_test.go b/vendor/github.com/mitchellh/mapstructure/mapstructure_test.go
new file mode 100644
index 000000000..932779d93
--- /dev/null
+++ b/vendor/github.com/mitchellh/mapstructure/mapstructure_test.go
@@ -0,0 +1,1193 @@
+package mapstructure
+
+import (
+ "encoding/json"
+ "io"
+ "reflect"
+ "sort"
+ "strings"
+ "testing"
+)
+
+type Basic struct {
+ Vstring string
+ Vint int
+ Vuint uint
+ Vbool bool
+ Vfloat float64
+ Vextra string
+ vsilent bool
+ Vdata interface{}
+ VjsonInt int
+ VjsonFloat float64
+ VjsonNumber json.Number
+}
+
+type BasicSquash struct {
+ Test Basic `mapstructure:",squash"`
+}
+
+type Embedded struct {
+ Basic
+ Vunique string
+}
+
+type EmbeddedPointer struct {
+ *Basic
+ Vunique string
+}
+
+type EmbeddedSquash struct {
+ Basic `mapstructure:",squash"`
+ Vunique string
+}
+
+type SliceAlias []string
+
+type EmbeddedSlice struct {
+ SliceAlias `mapstructure:"slice_alias"`
+ Vunique string
+}
+
+type SquashOnNonStructType struct {
+ InvalidSquashType int `mapstructure:",squash"`
+}
+
+type Map struct {
+ Vfoo string
+ Vother map[string]string
+}
+
+type MapOfStruct struct {
+ Value map[string]Basic
+}
+
+type Nested struct {
+ Vfoo string
+ Vbar Basic
+}
+
+type NestedPointer struct {
+ Vfoo string
+ Vbar *Basic
+}
+
+type NilInterface struct {
+ W io.Writer
+}
+
+type Slice struct {
+ Vfoo string
+ Vbar []string
+}
+
+type SliceOfStruct struct {
+ Value []Basic
+}
+
+type Func struct {
+ Foo func() string
+}
+
+type Tagged struct {
+ Extra string `mapstructure:"bar,what,what"`
+ Value string `mapstructure:"foo"`
+}
+
+type TypeConversionResult struct {
+ IntToFloat float32
+ IntToUint uint
+ IntToBool bool
+ IntToString string
+ UintToInt int
+ UintToFloat float32
+ UintToBool bool
+ UintToString string
+ BoolToInt int
+ BoolToUint uint
+ BoolToFloat float32
+ BoolToString string
+ FloatToInt int
+ FloatToUint uint
+ FloatToBool bool
+ FloatToString string
+ SliceUint8ToString string
+ StringToInt int
+ StringToUint uint
+ StringToBool bool
+ StringToFloat float32
+ StringToStrSlice []string
+ StringToIntSlice []int
+ SliceToMap map[string]interface{}
+ MapToSlice []interface{}
+}
+
+func TestBasicTypes(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vstring": "foo",
+ "vint": 42,
+ "Vuint": 42,
+ "vbool": true,
+ "Vfloat": 42.42,
+ "vsilent": true,
+ "vdata": 42,
+ "vjsonInt": json.Number("1234"),
+ "vjsonFloat": json.Number("1234.5"),
+ "vjsonNumber": json.Number("1234.5"),
+ }
+
+ var result Basic
+ err := Decode(input, &result)
+ if err != nil {
+ t.Errorf("got an err: %s", err.Error())
+ t.FailNow()
+ }
+
+ if result.Vstring != "foo" {
+ t.Errorf("vstring value should be 'foo': %#v", result.Vstring)
+ }
+
+ if result.Vint != 42 {
+ t.Errorf("vint value should be 42: %#v", result.Vint)
+ }
+
+ if result.Vuint != 42 {
+ t.Errorf("vuint value should be 42: %#v", result.Vuint)
+ }
+
+ if result.Vbool != true {
+ t.Errorf("vbool value should be true: %#v", result.Vbool)
+ }
+
+ if result.Vfloat != 42.42 {
+ t.Errorf("vfloat value should be 42.42: %#v", result.Vfloat)
+ }
+
+ if result.Vextra != "" {
+ t.Errorf("vextra value should be empty: %#v", result.Vextra)
+ }
+
+ if result.vsilent != false {
+ t.Error("vsilent should not be set, it is unexported")
+ }
+
+ if result.Vdata != 42 {
+ t.Error("vdata should be valid")
+ }
+
+ if result.VjsonInt != 1234 {
+ t.Errorf("vjsonint value should be 1234: %#v", result.VjsonInt)
+ }
+
+ if result.VjsonFloat != 1234.5 {
+ t.Errorf("vjsonfloat value should be 1234.5: %#v", result.VjsonFloat)
+ }
+
+ if !reflect.DeepEqual(result.VjsonNumber, json.Number("1234.5")) {
+ t.Errorf("vjsonnumber value should be '1234.5': %T, %#v", result.VjsonNumber, result.VjsonNumber)
+ }
+}
+
+func TestBasic_IntWithFloat(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vint": float64(42),
+ }
+
+ var result Basic
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("got an err: %s", err)
+ }
+}
+
+func TestBasic_Merge(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vint": 42,
+ }
+
+ var result Basic
+ result.Vuint = 100
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("got an err: %s", err)
+ }
+
+ expected := Basic{
+ Vint: 42,
+ Vuint: 100,
+ }
+ if !reflect.DeepEqual(result, expected) {
+ t.Fatalf("bad: %#v", result)
+ }
+}
+
+func TestDecode_BasicSquash(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vstring": "foo",
+ }
+
+ var result BasicSquash
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("got an err: %s", err.Error())
+ }
+
+ if result.Test.Vstring != "foo" {
+ t.Errorf("vstring value should be 'foo': %#v", result.Test.Vstring)
+ }
+}
+
+func TestDecode_Embedded(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vstring": "foo",
+ "Basic": map[string]interface{}{
+ "vstring": "innerfoo",
+ },
+ "vunique": "bar",
+ }
+
+ var result Embedded
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("got an err: %s", err.Error())
+ }
+
+ if result.Vstring != "innerfoo" {
+ t.Errorf("vstring value should be 'innerfoo': %#v", result.Vstring)
+ }
+
+ if result.Vunique != "bar" {
+ t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
+ }
+}
+
+func TestDecode_EmbeddedPointer(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vstring": "foo",
+ "Basic": map[string]interface{}{
+ "vstring": "innerfoo",
+ },
+ "vunique": "bar",
+ }
+
+ var result EmbeddedPointer
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ expected := EmbeddedPointer{
+ Basic: &Basic{
+ Vstring: "innerfoo",
+ },
+ Vunique: "bar",
+ }
+ if !reflect.DeepEqual(result, expected) {
+ t.Fatalf("bad: %#v", result)
+ }
+}
+
+func TestDecode_EmbeddedSlice(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "slice_alias": []string{"foo", "bar"},
+ "vunique": "bar",
+ }
+
+ var result EmbeddedSlice
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("got an err: %s", err.Error())
+ }
+
+ if !reflect.DeepEqual(result.SliceAlias, SliceAlias([]string{"foo", "bar"})) {
+ t.Errorf("slice value: %#v", result.SliceAlias)
+ }
+
+ if result.Vunique != "bar" {
+ t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
+ }
+}
+
+func TestDecode_EmbeddedSquash(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vstring": "foo",
+ "vunique": "bar",
+ }
+
+ var result EmbeddedSquash
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("got an err: %s", err.Error())
+ }
+
+ if result.Vstring != "foo" {
+ t.Errorf("vstring value should be 'foo': %#v", result.Vstring)
+ }
+
+ if result.Vunique != "bar" {
+ t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
+ }
+}
+
+func TestDecode_SquashOnNonStructType(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "InvalidSquashType": 42,
+ }
+
+ var result SquashOnNonStructType
+ err := Decode(input, &result)
+ if err == nil {
+ t.Fatal("unexpected success decoding invalid squash field type")
+ } else if !strings.Contains(err.Error(), "unsupported type for squash") {
+ t.Fatalf("unexpected error message for invalid squash field type: %s", err)
+ }
+}
+
+func TestDecode_DecodeHook(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vint": "WHAT",
+ }
+
+ decodeHook := func(from reflect.Kind, to reflect.Kind, v interface{}) (interface{}, error) {
+ if from == reflect.String && to != reflect.String {
+ return 5, nil
+ }
+
+ return v, nil
+ }
+
+ var result Basic
+ config := &DecoderConfig{
+ DecodeHook: decodeHook,
+ Result: &result,
+ }
+
+ decoder, err := NewDecoder(config)
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ err = decoder.Decode(input)
+ if err != nil {
+ t.Fatalf("got an err: %s", err)
+ }
+
+ if result.Vint != 5 {
+ t.Errorf("vint should be 5: %#v", result.Vint)
+ }
+}
+
+func TestDecode_DecodeHookType(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vint": "WHAT",
+ }
+
+ decodeHook := func(from reflect.Type, to reflect.Type, v interface{}) (interface{}, error) {
+ if from.Kind() == reflect.String &&
+ to.Kind() != reflect.String {
+ return 5, nil
+ }
+
+ return v, nil
+ }
+
+ var result Basic
+ config := &DecoderConfig{
+ DecodeHook: decodeHook,
+ Result: &result,
+ }
+
+ decoder, err := NewDecoder(config)
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ err = decoder.Decode(input)
+ if err != nil {
+ t.Fatalf("got an err: %s", err)
+ }
+
+ if result.Vint != 5 {
+ t.Errorf("vint should be 5: %#v", result.Vint)
+ }
+}
+
+func TestDecode_Nil(t *testing.T) {
+ t.Parallel()
+
+ var input interface{} = nil
+ result := Basic{
+ Vstring: "foo",
+ }
+
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if result.Vstring != "foo" {
+ t.Fatalf("bad: %#v", result.Vstring)
+ }
+}
+
+func TestDecode_NilInterfaceHook(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "w": "",
+ }
+
+ decodeHook := func(f, t reflect.Type, v interface{}) (interface{}, error) {
+ if t.String() == "io.Writer" {
+ return nil, nil
+ }
+
+ return v, nil
+ }
+
+ var result NilInterface
+ config := &DecoderConfig{
+ DecodeHook: decodeHook,
+ Result: &result,
+ }
+
+ decoder, err := NewDecoder(config)
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ err = decoder.Decode(input)
+ if err != nil {
+ t.Fatalf("got an err: %s", err)
+ }
+
+ if result.W != nil {
+ t.Errorf("W should be nil: %#v", result.W)
+ }
+}
+
+func TestDecode_FuncHook(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "foo": "baz",
+ }
+
+ decodeHook := func(f, t reflect.Type, v interface{}) (interface{}, error) {
+ if t.Kind() != reflect.Func {
+ return v, nil
+ }
+ val := v.(string)
+ return func() string { return val }, nil
+ }
+
+ var result Func
+ config := &DecoderConfig{
+ DecodeHook: decodeHook,
+ Result: &result,
+ }
+
+ decoder, err := NewDecoder(config)
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ err = decoder.Decode(input)
+ if err != nil {
+ t.Fatalf("got an err: %s", err)
+ }
+
+ if result.Foo() != "baz" {
+ t.Errorf("Foo call result should be 'baz': %s", result.Foo())
+ }
+}
+
+func TestDecode_NonStruct(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "foo": "bar",
+ "bar": "baz",
+ }
+
+ var result map[string]string
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if result["foo"] != "bar" {
+ t.Fatal("foo is not bar")
+ }
+}
+
+func TestDecode_StructMatch(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vbar": Basic{
+ Vstring: "foo",
+ },
+ }
+
+ var result Nested
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("got an err: %s", err.Error())
+ }
+
+ if result.Vbar.Vstring != "foo" {
+ t.Errorf("bad: %#v", result)
+ }
+}
+
+func TestDecode_TypeConversion(t *testing.T) {
+ input := map[string]interface{}{
+ "IntToFloat": 42,
+ "IntToUint": 42,
+ "IntToBool": 1,
+ "IntToString": 42,
+ "UintToInt": 42,
+ "UintToFloat": 42,
+ "UintToBool": 42,
+ "UintToString": 42,
+ "BoolToInt": true,
+ "BoolToUint": true,
+ "BoolToFloat": true,
+ "BoolToString": true,
+ "FloatToInt": 42.42,
+ "FloatToUint": 42.42,
+ "FloatToBool": 42.42,
+ "FloatToString": 42.42,
+ "SliceUint8ToString": []uint8("foo"),
+ "StringToInt": "42",
+ "StringToUint": "42",
+ "StringToBool": "1",
+ "StringToFloat": "42.42",
+ "StringToStrSlice": "A",
+ "StringToIntSlice": "42",
+ "SliceToMap": []interface{}{},
+ "MapToSlice": map[string]interface{}{},
+ }
+
+ expectedResultStrict := TypeConversionResult{
+ IntToFloat: 42.0,
+ IntToUint: 42,
+ UintToInt: 42,
+ UintToFloat: 42,
+ BoolToInt: 0,
+ BoolToUint: 0,
+ BoolToFloat: 0,
+ FloatToInt: 42,
+ FloatToUint: 42,
+ }
+
+ expectedResultWeak := TypeConversionResult{
+ IntToFloat: 42.0,
+ IntToUint: 42,
+ IntToBool: true,
+ IntToString: "42",
+ UintToInt: 42,
+ UintToFloat: 42,
+ UintToBool: true,
+ UintToString: "42",
+ BoolToInt: 1,
+ BoolToUint: 1,
+ BoolToFloat: 1,
+ BoolToString: "1",
+ FloatToInt: 42,
+ FloatToUint: 42,
+ FloatToBool: true,
+ FloatToString: "42.42",
+ SliceUint8ToString: "foo",
+ StringToInt: 42,
+ StringToUint: 42,
+ StringToBool: true,
+ StringToFloat: 42.42,
+ StringToStrSlice: []string{"A"},
+ StringToIntSlice: []int{42},
+ SliceToMap: map[string]interface{}{},
+ MapToSlice: []interface{}{},
+ }
+
+ // Test strict type conversion
+ var resultStrict TypeConversionResult
+ err := Decode(input, &resultStrict)
+ if err == nil {
+ t.Errorf("should return an error")
+ }
+ if !reflect.DeepEqual(resultStrict, expectedResultStrict) {
+ t.Errorf("expected %v, got: %v", expectedResultStrict, resultStrict)
+ }
+
+ // Test weak type conversion
+ var decoder *Decoder
+ var resultWeak TypeConversionResult
+
+ config := &DecoderConfig{
+ WeaklyTypedInput: true,
+ Result: &resultWeak,
+ }
+
+ decoder, err = NewDecoder(config)
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ err = decoder.Decode(input)
+ if err != nil {
+ t.Fatalf("got an err: %s", err)
+ }
+
+ if !reflect.DeepEqual(resultWeak, expectedResultWeak) {
+ t.Errorf("expected \n%#v, got: \n%#v", expectedResultWeak, resultWeak)
+ }
+}
+
+func TestDecoder_ErrorUnused(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vstring": "hello",
+ "foo": "bar",
+ }
+
+ var result Basic
+ config := &DecoderConfig{
+ ErrorUnused: true,
+ Result: &result,
+ }
+
+ decoder, err := NewDecoder(config)
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ err = decoder.Decode(input)
+ if err == nil {
+ t.Fatal("expected error")
+ }
+}
+
+func TestMap(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vfoo": "foo",
+ "vother": map[interface{}]interface{}{
+ "foo": "foo",
+ "bar": "bar",
+ },
+ }
+
+ var result Map
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("got an error: %s", err)
+ }
+
+ if result.Vfoo != "foo" {
+ t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
+ }
+
+ if result.Vother == nil {
+ t.Fatal("vother should not be nil")
+ }
+
+ if len(result.Vother) != 2 {
+ t.Error("vother should have two items")
+ }
+
+ if result.Vother["foo"] != "foo" {
+ t.Errorf("'foo' key should be foo, got: %#v", result.Vother["foo"])
+ }
+
+ if result.Vother["bar"] != "bar" {
+ t.Errorf("'bar' key should be bar, got: %#v", result.Vother["bar"])
+ }
+}
+
+func TestMapMerge(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vfoo": "foo",
+ "vother": map[interface{}]interface{}{
+ "foo": "foo",
+ "bar": "bar",
+ },
+ }
+
+ var result Map
+ result.Vother = map[string]string{"hello": "world"}
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("got an error: %s", err)
+ }
+
+ if result.Vfoo != "foo" {
+ t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
+ }
+
+ expected := map[string]string{
+ "foo": "foo",
+ "bar": "bar",
+ "hello": "world",
+ }
+ if !reflect.DeepEqual(result.Vother, expected) {
+ t.Errorf("bad: %#v", result.Vother)
+ }
+}
+
+func TestMapOfStruct(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "value": map[string]interface{}{
+ "foo": map[string]string{"vstring": "one"},
+ "bar": map[string]string{"vstring": "two"},
+ },
+ }
+
+ var result MapOfStruct
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("got an err: %s", err)
+ }
+
+ if result.Value == nil {
+ t.Fatal("value should not be nil")
+ }
+
+ if len(result.Value) != 2 {
+ t.Error("value should have two items")
+ }
+
+ if result.Value["foo"].Vstring != "one" {
+ t.Errorf("foo value should be 'one', got: %s", result.Value["foo"].Vstring)
+ }
+
+ if result.Value["bar"].Vstring != "two" {
+ t.Errorf("bar value should be 'two', got: %s", result.Value["bar"].Vstring)
+ }
+}
+
+func TestNestedType(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vfoo": "foo",
+ "vbar": map[string]interface{}{
+ "vstring": "foo",
+ "vint": 42,
+ "vbool": true,
+ },
+ }
+
+ var result Nested
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("got an err: %s", err.Error())
+ }
+
+ if result.Vfoo != "foo" {
+ t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
+ }
+
+ if result.Vbar.Vstring != "foo" {
+ t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring)
+ }
+
+ if result.Vbar.Vint != 42 {
+ t.Errorf("vint value should be 42: %#v", result.Vbar.Vint)
+ }
+
+ if result.Vbar.Vbool != true {
+ t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool)
+ }
+
+ if result.Vbar.Vextra != "" {
+ t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra)
+ }
+}
+
+func TestNestedTypePointer(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vfoo": "foo",
+ "vbar": &map[string]interface{}{
+ "vstring": "foo",
+ "vint": 42,
+ "vbool": true,
+ },
+ }
+
+ var result NestedPointer
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("got an err: %s", err.Error())
+ }
+
+ if result.Vfoo != "foo" {
+ t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
+ }
+
+ if result.Vbar.Vstring != "foo" {
+ t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring)
+ }
+
+ if result.Vbar.Vint != 42 {
+ t.Errorf("vint value should be 42: %#v", result.Vbar.Vint)
+ }
+
+ if result.Vbar.Vbool != true {
+ t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool)
+ }
+
+ if result.Vbar.Vextra != "" {
+ t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra)
+ }
+}
+
+func TestSlice(t *testing.T) {
+ t.Parallel()
+
+ inputStringSlice := map[string]interface{}{
+ "vfoo": "foo",
+ "vbar": []string{"foo", "bar", "baz"},
+ }
+
+ inputStringSlicePointer := map[string]interface{}{
+ "vfoo": "foo",
+ "vbar": &[]string{"foo", "bar", "baz"},
+ }
+
+ outputStringSlice := &Slice{
+ "foo",
+ []string{"foo", "bar", "baz"},
+ }
+
+ testSliceInput(t, inputStringSlice, outputStringSlice)
+ testSliceInput(t, inputStringSlicePointer, outputStringSlice)
+}
+
+func TestInvalidSlice(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vfoo": "foo",
+ "vbar": 42,
+ }
+
+ result := Slice{}
+ err := Decode(input, &result)
+ if err == nil {
+ t.Errorf("expected failure")
+ }
+}
+
+func TestSliceOfStruct(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "value": []map[string]interface{}{
+ {"vstring": "one"},
+ {"vstring": "two"},
+ },
+ }
+
+ var result SliceOfStruct
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("got unexpected error: %s", err)
+ }
+
+ if len(result.Value) != 2 {
+ t.Fatalf("expected two values, got %d", len(result.Value))
+ }
+
+ if result.Value[0].Vstring != "one" {
+ t.Errorf("first value should be 'one', got: %s", result.Value[0].Vstring)
+ }
+
+ if result.Value[1].Vstring != "two" {
+ t.Errorf("second value should be 'two', got: %s", result.Value[1].Vstring)
+ }
+}
+
+func TestSliceToMap(t *testing.T) {
+ t.Parallel()
+
+ input := []map[string]interface{}{
+ {
+ "foo": "bar",
+ },
+ {
+ "bar": "baz",
+ },
+ }
+
+ var result map[string]interface{}
+ err := WeakDecode(input, &result)
+ if err != nil {
+ t.Fatalf("got an error: %s", err)
+ }
+
+ expected := map[string]interface{}{
+ "foo": "bar",
+ "bar": "baz",
+ }
+ if !reflect.DeepEqual(result, expected) {
+ t.Errorf("bad: %#v", result)
+ }
+}
+
+func TestInvalidType(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vstring": 42,
+ }
+
+ var result Basic
+ err := Decode(input, &result)
+ if err == nil {
+ t.Fatal("error should exist")
+ }
+
+ derr, ok := err.(*Error)
+ if !ok {
+ t.Fatalf("error should be kind of Error, instead: %#v", err)
+ }
+
+ if derr.Errors[0] != "'Vstring' expected type 'string', got unconvertible type 'int'" {
+ t.Errorf("got unexpected error: %s", err)
+ }
+
+ inputNegIntUint := map[string]interface{}{
+ "vuint": -42,
+ }
+
+ err = Decode(inputNegIntUint, &result)
+ if err == nil {
+ t.Fatal("error should exist")
+ }
+
+ derr, ok = err.(*Error)
+ if !ok {
+ t.Fatalf("error should be kind of Error, instead: %#v", err)
+ }
+
+ if derr.Errors[0] != "cannot parse 'Vuint', -42 overflows uint" {
+ t.Errorf("got unexpected error: %s", err)
+ }
+
+ inputNegFloatUint := map[string]interface{}{
+ "vuint": -42.0,
+ }
+
+ err = Decode(inputNegFloatUint, &result)
+ if err == nil {
+ t.Fatal("error should exist")
+ }
+
+ derr, ok = err.(*Error)
+ if !ok {
+ t.Fatalf("error should be kind of Error, instead: %#v", err)
+ }
+
+ if derr.Errors[0] != "cannot parse 'Vuint', -42.000000 overflows uint" {
+ t.Errorf("got unexpected error: %s", err)
+ }
+}
+
+func TestMetadata(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vfoo": "foo",
+ "vbar": map[string]interface{}{
+ "vstring": "foo",
+ "Vuint": 42,
+ "foo": "bar",
+ },
+ "bar": "nil",
+ }
+
+ var md Metadata
+ var result Nested
+ config := &DecoderConfig{
+ Metadata: &md,
+ Result: &result,
+ }
+
+ decoder, err := NewDecoder(config)
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ err = decoder.Decode(input)
+ if err != nil {
+ t.Fatalf("err: %s", err.Error())
+ }
+
+ expectedKeys := []string{"Vbar", "Vbar.Vstring", "Vbar.Vuint", "Vfoo"}
+ sort.Strings(md.Keys)
+ if !reflect.DeepEqual(md.Keys, expectedKeys) {
+ t.Fatalf("bad keys: %#v", md.Keys)
+ }
+
+ expectedUnused := []string{"Vbar.foo", "bar"}
+ if !reflect.DeepEqual(md.Unused, expectedUnused) {
+ t.Fatalf("bad unused: %#v", md.Unused)
+ }
+}
+
+func TestMetadata_Embedded(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vstring": "foo",
+ "vunique": "bar",
+ }
+
+ var md Metadata
+ var result EmbeddedSquash
+ config := &DecoderConfig{
+ Metadata: &md,
+ Result: &result,
+ }
+
+ decoder, err := NewDecoder(config)
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ err = decoder.Decode(input)
+ if err != nil {
+ t.Fatalf("err: %s", err.Error())
+ }
+
+ expectedKeys := []string{"Vstring", "Vunique"}
+
+ sort.Strings(md.Keys)
+ if !reflect.DeepEqual(md.Keys, expectedKeys) {
+ t.Fatalf("bad keys: %#v", md.Keys)
+ }
+
+ expectedUnused := []string{}
+ if !reflect.DeepEqual(md.Unused, expectedUnused) {
+ t.Fatalf("bad unused: %#v", md.Unused)
+ }
+}
+
+func TestNonPtrValue(t *testing.T) {
+ t.Parallel()
+
+ err := Decode(map[string]interface{}{}, Basic{})
+ if err == nil {
+ t.Fatal("error should exist")
+ }
+
+ if err.Error() != "result must be a pointer" {
+ t.Errorf("got unexpected error: %s", err)
+ }
+}
+
+func TestTagged(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "foo": "bar",
+ "bar": "value",
+ }
+
+ var result Tagged
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+
+ if result.Value != "bar" {
+ t.Errorf("value should be 'bar', got: %#v", result.Value)
+ }
+
+ if result.Extra != "value" {
+ t.Errorf("extra should be 'value', got: %#v", result.Extra)
+ }
+}
+
+func TestWeakDecode(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "foo": "4",
+ "bar": "value",
+ }
+
+ var result struct {
+ Foo int
+ Bar string
+ }
+
+ if err := WeakDecode(input, &result); err != nil {
+ t.Fatalf("err: %s", err)
+ }
+ if result.Foo != 4 {
+ t.Fatalf("bad: %#v", result)
+ }
+ if result.Bar != "value" {
+ t.Fatalf("bad: %#v", result)
+ }
+}
+
+func testSliceInput(t *testing.T, input map[string]interface{}, expected *Slice) {
+ var result Slice
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("got error: %s", err)
+ }
+
+ if result.Vfoo != expected.Vfoo {
+ t.Errorf("Vfoo expected '%s', got '%s'", expected.Vfoo, result.Vfoo)
+ }
+
+ if result.Vbar == nil {
+ t.Fatalf("Vbar a slice, got '%#v'", result.Vbar)
+ }
+
+ if len(result.Vbar) != len(expected.Vbar) {
+ t.Errorf("Vbar length should be %d, got %d", len(expected.Vbar), len(result.Vbar))
+ }
+
+ for i, v := range result.Vbar {
+ if v != expected.Vbar[i] {
+ t.Errorf(
+ "Vbar[%d] should be '%#v', got '%#v'",
+ i, expected.Vbar[i], v)
+ }
+ }
+}