123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603 |
- package mhayaMapStructure
- import (
- "reflect"
- "testing"
- "time"
- )
- // GH-1, GH-10, GH-96
- func TestDecode_NilValue(t *testing.T) {
- t.Parallel()
- tests := []struct {
- name string
- in interface{}
- target interface{}
- out interface{}
- metaKeys []string
- metaUnused []string
- }{
- {
- "all nil",
- &map[string]interface{}{
- "vfoo": nil,
- "vother": nil,
- },
- &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
- &Map{Vfoo: "", Vother: nil},
- []string{"Vfoo", "Vother"},
- []string{},
- },
- {
- "partial nil",
- &map[string]interface{}{
- "vfoo": "baz",
- "vother": nil,
- },
- &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
- &Map{Vfoo: "baz", Vother: nil},
- []string{"Vfoo", "Vother"},
- []string{},
- },
- {
- "partial decode",
- &map[string]interface{}{
- "vother": nil,
- },
- &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
- &Map{Vfoo: "foo", Vother: nil},
- []string{"Vother"},
- []string{},
- },
- {
- "unused values",
- &map[string]interface{}{
- "vbar": "bar",
- "vfoo": nil,
- "vother": nil,
- },
- &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
- &Map{Vfoo: "", Vother: nil},
- []string{"Vfoo", "Vother"},
- []string{"vbar"},
- },
- {
- "map interface all nil",
- &map[interface{}]interface{}{
- "vfoo": nil,
- "vother": nil,
- },
- &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
- &Map{Vfoo: "", Vother: nil},
- []string{"Vfoo", "Vother"},
- []string{},
- },
- {
- "map interface partial nil",
- &map[interface{}]interface{}{
- "vfoo": "baz",
- "vother": nil,
- },
- &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
- &Map{Vfoo: "baz", Vother: nil},
- []string{"Vfoo", "Vother"},
- []string{},
- },
- {
- "map interface partial decode",
- &map[interface{}]interface{}{
- "vother": nil,
- },
- &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
- &Map{Vfoo: "foo", Vother: nil},
- []string{"Vother"},
- []string{},
- },
- {
- "map interface unused values",
- &map[interface{}]interface{}{
- "vbar": "bar",
- "vfoo": nil,
- "vother": nil,
- },
- &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
- &Map{Vfoo: "", Vother: nil},
- []string{"Vfoo", "Vother"},
- []string{"vbar"},
- },
- }
- for _, tc := range tests {
- t.Run(tc.name, func(t *testing.T) {
- config := &DecoderConfig{
- Metadata: new(Metadata),
- Result: tc.target,
- ZeroFields: true,
- }
- decoder, err := NewDecoder(config)
- if err != nil {
- t.Fatalf("should not error: %s", err)
- }
- err = decoder.Decode(tc.in)
- if err != nil {
- t.Fatalf("should not error: %s", err)
- }
- if !reflect.DeepEqual(tc.out, tc.target) {
- t.Fatalf("%q: TestDecode_NilValue() expected: %#v, got: %#v", tc.name, tc.out, tc.target)
- }
- if !reflect.DeepEqual(tc.metaKeys, config.Metadata.Keys) {
- t.Fatalf("%q: Metadata.Keys mismatch expected: %#v, got: %#v", tc.name, tc.metaKeys, config.Metadata.Keys)
- }
- if !reflect.DeepEqual(tc.metaUnused, config.Metadata.Unused) {
- t.Fatalf("%q: Metadata.Unused mismatch expected: %#v, got: %#v", tc.name, tc.metaUnused, config.Metadata.Unused)
- }
- })
- }
- }
- // #48
- func TestNestedTypePointerWithDefaults(t *testing.T) {
- t.Parallel()
- input := map[string]interface{}{
- "vfoo": "foo",
- "vbar": map[string]interface{}{
- "vstring": "foo",
- "vint": 42,
- "vbool": true,
- },
- }
- result := NestedPointer{
- Vbar: &Basic{
- Vuint: 42,
- },
- }
- 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)
- }
- // this is the error
- if result.Vbar.Vuint != 42 {
- t.Errorf("vuint value should be 42: %#v", result.Vbar.Vuint)
- }
- }
- type NestedSlice struct {
- Vfoo string
- Vbars []Basic
- Vempty []Basic
- }
- // #48
- func TestNestedTypeSliceWithDefaults(t *testing.T) {
- t.Parallel()
- input := map[string]interface{}{
- "vfoo": "foo",
- "vbars": []map[string]interface{}{
- {"vstring": "foo", "vint": 42, "vbool": true},
- {"vint": 42, "vbool": true},
- },
- "vempty": []map[string]interface{}{
- {"vstring": "foo", "vint": 42, "vbool": true},
- {"vint": 42, "vbool": true},
- },
- }
- result := NestedSlice{
- Vbars: []Basic{
- {Vuint: 42},
- {Vstring: "foo"},
- },
- }
- 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.Vbars[0].Vstring != "foo" {
- t.Errorf("vstring value should be 'foo': %#v", result.Vbars[0].Vstring)
- }
- // this is the error
- if result.Vbars[0].Vuint != 42 {
- t.Errorf("vuint value should be 42: %#v", result.Vbars[0].Vuint)
- }
- }
- // #48 workaround
- func TestNestedTypeWithDefaults(t *testing.T) {
- t.Parallel()
- input := map[string]interface{}{
- "vfoo": "foo",
- "vbar": map[string]interface{}{
- "vstring": "foo",
- "vint": 42,
- "vbool": true,
- },
- }
- result := Nested{
- Vbar: Basic{
- Vuint: 42,
- },
- }
- 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)
- }
- // this is the error
- if result.Vbar.Vuint != 42 {
- t.Errorf("vuint value should be 42: %#v", result.Vbar.Vuint)
- }
- }
- // #67 panic() on extending slices (decodeSlice with disabled ZeroValues)
- func TestDecodeSliceToEmptySliceWOZeroing(t *testing.T) {
- t.Parallel()
- type TestStruct struct {
- Vfoo []string
- }
- decode := func(m interface{}, rawVal interface{}) error {
- config := &DecoderConfig{
- Metadata: nil,
- Result: rawVal,
- ZeroFields: false,
- }
- decoder, err := NewDecoder(config)
- if err != nil {
- return err
- }
- return decoder.Decode(m)
- }
- {
- input := map[string]interface{}{
- "vfoo": []string{"1"},
- }
- result := &TestStruct{}
- err := decode(input, &result)
- if err != nil {
- t.Fatalf("got an err: %s", err.Error())
- }
- }
- {
- input := map[string]interface{}{
- "vfoo": []string{"1"},
- }
- result := &TestStruct{
- Vfoo: []string{},
- }
- err := decode(input, &result)
- if err != nil {
- t.Fatalf("got an err: %s", err.Error())
- }
- }
- {
- input := map[string]interface{}{
- "vfoo": []string{"2", "3"},
- }
- result := &TestStruct{
- Vfoo: []string{"1"},
- }
- err := decode(input, &result)
- if err != nil {
- t.Fatalf("got an err: %s", err.Error())
- }
- }
- }
- // #70
- func TestNextSquashMapstructure(t *testing.T) {
- data := &struct {
- Level1 struct {
- Level2 struct {
- Foo string
- } `mapstructure:",squash"`
- } `mapstructure:",squash"`
- }{}
- err := Decode(map[interface{}]interface{}{"foo": "baz"}, &data)
- if err != nil {
- t.Fatalf("should not error: %s", err)
- }
- if data.Level1.Level2.Foo != "baz" {
- t.Fatal("value should be baz")
- }
- }
- type ImplementsInterfacePointerReceiver struct {
- Name string
- }
- func (i *ImplementsInterfacePointerReceiver) DoStuff() {}
- type ImplementsInterfaceValueReceiver string
- func (i ImplementsInterfaceValueReceiver) DoStuff() {}
- // GH-140 Type error when using DecodeHook to decode into interface
- func TestDecode_DecodeHookInterface(t *testing.T) {
- t.Parallel()
- type Interface interface {
- DoStuff()
- }
- type DecodeIntoInterface struct {
- Test Interface
- }
- testData := map[string]string{"test": "test"}
- stringToPointerInterfaceDecodeHook := func(from, to reflect.Type, data interface{}) (interface{}, error) {
- if from.Kind() != reflect.String {
- return data, nil
- }
- if to != reflect.TypeOf((*Interface)(nil)).Elem() {
- return data, nil
- }
- // Ensure interface is satisfied
- var impl Interface = &ImplementsInterfacePointerReceiver{data.(string)}
- return impl, nil
- }
- stringToValueInterfaceDecodeHook := func(from, to reflect.Type, data interface{}) (interface{}, error) {
- if from.Kind() != reflect.String {
- return data, nil
- }
- if to != reflect.TypeOf((*Interface)(nil)).Elem() {
- return data, nil
- }
- // Ensure interface is satisfied
- var impl Interface = ImplementsInterfaceValueReceiver(data.(string))
- return impl, nil
- }
- {
- decodeInto := new(DecodeIntoInterface)
- decoder, _ := NewDecoder(&DecoderConfig{
- DecodeHook: stringToPointerInterfaceDecodeHook,
- Result: decodeInto,
- })
- err := decoder.Decode(testData)
- if err != nil {
- t.Fatalf("Decode returned error: %s", err)
- }
- expected := &ImplementsInterfacePointerReceiver{"test"}
- if !reflect.DeepEqual(decodeInto.Test, expected) {
- t.Fatalf("expected: %#v (%T), got: %#v (%T)", decodeInto.Test, decodeInto.Test, expected, expected)
- }
- }
- {
- decodeInto := new(DecodeIntoInterface)
- decoder, _ := NewDecoder(&DecoderConfig{
- DecodeHook: stringToValueInterfaceDecodeHook,
- Result: decodeInto,
- })
- err := decoder.Decode(testData)
- if err != nil {
- t.Fatalf("Decode returned error: %s", err)
- }
- expected := ImplementsInterfaceValueReceiver("test")
- if !reflect.DeepEqual(decodeInto.Test, expected) {
- t.Fatalf("expected: %#v (%T), got: %#v (%T)", decodeInto.Test, decodeInto.Test, expected, expected)
- }
- }
- }
- // #103 Check for data type before trying to access its composants prevent a panic error
- // in decodeSlice
- func TestDecodeBadDataTypeInSlice(t *testing.T) {
- t.Parallel()
- input := map[string]interface{}{
- "Toto": "titi",
- }
- result := []struct {
- Toto string
- }{}
- if err := Decode(input, &result); err == nil {
- t.Error("An error was expected, got nil")
- }
- }
- // #202 Ensure that intermediate maps in the struct -> struct decode process are settable
- // and not just the elements within them.
- func TestDecodeIntermediateMapsSettable(t *testing.T) {
- type Timestamp struct {
- Seconds int64
- Nanos int32
- }
- type TsWrapper struct {
- Timestamp *Timestamp
- }
- type TimeWrapper struct {
- Timestamp time.Time
- }
- input := TimeWrapper{
- Timestamp: time.Unix(123456789, 987654),
- }
- expected := TsWrapper{
- Timestamp: &Timestamp{
- Seconds: 123456789,
- Nanos: 987654,
- },
- }
- timePtrType := reflect.TypeOf((*time.Time)(nil))
- mapStrInfType := reflect.TypeOf((map[string]interface{})(nil))
- var actual TsWrapper
- decoder, err := NewDecoder(&DecoderConfig{
- Result: &actual,
- DecodeHook: func(from, to reflect.Type, data interface{}) (interface{}, error) {
- if from == timePtrType && to == mapStrInfType {
- ts := data.(*time.Time)
- nanos := ts.UnixNano()
- seconds := nanos / 1000000000
- nanos = nanos % 1000000000
- return &map[string]interface{}{
- "Seconds": seconds,
- "Nanos": int32(nanos),
- }, nil
- }
- return data, nil
- },
- })
- if err != nil {
- t.Fatalf("failed to create decoder: %v", err)
- }
- if err := decoder.Decode(&input); err != nil {
- t.Fatalf("failed to decode input: %v", err)
- }
- if !reflect.DeepEqual(expected, actual) {
- t.Fatalf("expected: %#[1]v (%[1]T), got: %#[2]v (%[2]T)", expected, actual)
- }
- }
- // GH-206: decodeInt throws an error for an empty string
- func TestDecode_weakEmptyStringToInt(t *testing.T) {
- input := map[string]interface{}{
- "StringToInt": "",
- "StringToUint": "",
- "StringToBool": "",
- "StringToFloat": "",
- }
- expectedResultWeak := TypeConversionResult{
- StringToInt: 0,
- StringToUint: 0,
- StringToBool: false,
- StringToFloat: 0,
- }
- // Test weak type conversion
- var resultWeak TypeConversionResult
- err := WeakDecode(input, &resultWeak)
- 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)
- }
- }
- // GH-228: Squash cause *time.Time set to zero
- func TestMapSquash(t *testing.T) {
- type AA struct {
- T *time.Time
- }
- type A struct {
- AA
- }
- v := time.Now()
- in := &AA{
- T: &v,
- }
- out := &A{}
- d, err := NewDecoder(&DecoderConfig{
- Squash: true,
- Result: out,
- })
- if err != nil {
- t.Fatalf("err: %s", err)
- }
- if err := d.Decode(in); err != nil {
- t.Fatalf("err: %s", err)
- }
- // these failed
- if !v.Equal(*out.T) {
- t.Fatal("expected equal")
- }
- if out.T.IsZero() {
- t.Fatal("expected false")
- }
- }
|