...

Source file src/github.com/mitchellh/mapstructure/decode_hooks_test.go

Documentation: github.com/mitchellh/mapstructure

     1  package mapstructure
     2  
     3  import (
     4  	"errors"
     5  	"math/big"
     6  	"net"
     7  	"reflect"
     8  	"testing"
     9  	"time"
    10  )
    11  
    12  func TestComposeDecodeHookFunc(t *testing.T) {
    13  	f1 := func(
    14  		f reflect.Kind,
    15  		t reflect.Kind,
    16  		data interface{}) (interface{}, error) {
    17  		return data.(string) + "foo", nil
    18  	}
    19  
    20  	f2 := func(
    21  		f reflect.Kind,
    22  		t reflect.Kind,
    23  		data interface{}) (interface{}, error) {
    24  		return data.(string) + "bar", nil
    25  	}
    26  
    27  	f := ComposeDecodeHookFunc(f1, f2)
    28  
    29  	result, err := DecodeHookExec(
    30  		f, reflect.ValueOf(""), reflect.ValueOf([]byte("")))
    31  	if err != nil {
    32  		t.Fatalf("bad: %s", err)
    33  	}
    34  	if result.(string) != "foobar" {
    35  		t.Fatalf("bad: %#v", result)
    36  	}
    37  }
    38  
    39  func TestComposeDecodeHookFunc_err(t *testing.T) {
    40  	f1 := func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) {
    41  		return nil, errors.New("foo")
    42  	}
    43  
    44  	f2 := func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) {
    45  		panic("NOPE")
    46  	}
    47  
    48  	f := ComposeDecodeHookFunc(f1, f2)
    49  
    50  	_, err := DecodeHookExec(
    51  		f, reflect.ValueOf(""), reflect.ValueOf([]byte("")))
    52  	if err.Error() != "foo" {
    53  		t.Fatalf("bad: %s", err)
    54  	}
    55  }
    56  
    57  func TestComposeDecodeHookFunc_kinds(t *testing.T) {
    58  	var f2From reflect.Kind
    59  
    60  	f1 := func(
    61  		f reflect.Kind,
    62  		t reflect.Kind,
    63  		data interface{}) (interface{}, error) {
    64  		return int(42), nil
    65  	}
    66  
    67  	f2 := func(
    68  		f reflect.Kind,
    69  		t reflect.Kind,
    70  		data interface{}) (interface{}, error) {
    71  		f2From = f
    72  		return data, nil
    73  	}
    74  
    75  	f := ComposeDecodeHookFunc(f1, f2)
    76  
    77  	_, err := DecodeHookExec(
    78  		f, reflect.ValueOf(""), reflect.ValueOf([]byte("")))
    79  	if err != nil {
    80  		t.Fatalf("bad: %s", err)
    81  	}
    82  	if f2From != reflect.Int {
    83  		t.Fatalf("bad: %#v", f2From)
    84  	}
    85  }
    86  
    87  func TestOrComposeDecodeHookFunc(t *testing.T) {
    88  	f1 := func(
    89  		f reflect.Kind,
    90  		t reflect.Kind,
    91  		data interface{}) (interface{}, error) {
    92  		return data.(string) + "foo", nil
    93  	}
    94  
    95  	f2 := func(
    96  		f reflect.Kind,
    97  		t reflect.Kind,
    98  		data interface{}) (interface{}, error) {
    99  		return data.(string) + "bar", nil
   100  	}
   101  
   102  	f := OrComposeDecodeHookFunc(f1, f2)
   103  
   104  	result, err := DecodeHookExec(
   105  		f, reflect.ValueOf(""), reflect.ValueOf([]byte("")))
   106  	if err != nil {
   107  		t.Fatalf("bad: %s", err)
   108  	}
   109  	if result.(string) != "foo" {
   110  		t.Fatalf("bad: %#v", result)
   111  	}
   112  }
   113  
   114  func TestOrComposeDecodeHookFunc_correctValueIsLast(t *testing.T) {
   115  	f1 := func(
   116  		f reflect.Kind,
   117  		t reflect.Kind,
   118  		data interface{}) (interface{}, error) {
   119  		return nil, errors.New("f1 error")
   120  	}
   121  
   122  	f2 := func(
   123  		f reflect.Kind,
   124  		t reflect.Kind,
   125  		data interface{}) (interface{}, error) {
   126  		return nil, errors.New("f2 error")
   127  	}
   128  
   129  	f3 := func(
   130  		f reflect.Kind,
   131  		t reflect.Kind,
   132  		data interface{}) (interface{}, error) {
   133  		return data.(string) + "bar", nil
   134  	}
   135  
   136  	f := OrComposeDecodeHookFunc(f1, f2, f3)
   137  
   138  	result, err := DecodeHookExec(
   139  		f, reflect.ValueOf(""), reflect.ValueOf([]byte("")))
   140  	if err != nil {
   141  		t.Fatalf("bad: %s", err)
   142  	}
   143  	if result.(string) != "bar" {
   144  		t.Fatalf("bad: %#v", result)
   145  	}
   146  }
   147  
   148  func TestOrComposeDecodeHookFunc_err(t *testing.T) {
   149  	f1 := func(
   150  		f reflect.Kind,
   151  		t reflect.Kind,
   152  		data interface{}) (interface{}, error) {
   153  		return nil, errors.New("f1 error")
   154  	}
   155  
   156  	f2 := func(
   157  		f reflect.Kind,
   158  		t reflect.Kind,
   159  		data interface{}) (interface{}, error) {
   160  		return nil, errors.New("f2 error")
   161  	}
   162  
   163  	f := OrComposeDecodeHookFunc(f1, f2)
   164  
   165  	_, err := DecodeHookExec(
   166  		f, reflect.ValueOf(""), reflect.ValueOf([]byte("")))
   167  	if err == nil {
   168  		t.Fatalf("bad: should return an error")
   169  	}
   170  	if err.Error() != "f1 error\nf2 error\n" {
   171  		t.Fatalf("bad: %s", err)
   172  	}
   173  }
   174  
   175  func TestComposeDecodeHookFunc_safe_nofuncs(t *testing.T) {
   176  	f := ComposeDecodeHookFunc()
   177  	type myStruct2 struct {
   178  		MyInt int
   179  	}
   180  
   181  	type myStruct1 struct {
   182  		Blah map[string]myStruct2
   183  	}
   184  
   185  	src := &myStruct1{Blah: map[string]myStruct2{
   186  		"test": {
   187  			MyInt: 1,
   188  		},
   189  	}}
   190  
   191  	dst := &myStruct1{}
   192  	dConf := &DecoderConfig{
   193  		Result:      dst,
   194  		ErrorUnused: true,
   195  		DecodeHook:  f,
   196  	}
   197  	d, err := NewDecoder(dConf)
   198  	if err != nil {
   199  		t.Fatal(err)
   200  	}
   201  	err = d.Decode(src)
   202  	if err != nil {
   203  		t.Fatal(err)
   204  	}
   205  }
   206  
   207  func TestStringToSliceHookFunc(t *testing.T) {
   208  	f := StringToSliceHookFunc(",")
   209  
   210  	strValue := reflect.ValueOf("42")
   211  	sliceValue := reflect.ValueOf([]byte("42"))
   212  	cases := []struct {
   213  		f, t   reflect.Value
   214  		result interface{}
   215  		err    bool
   216  	}{
   217  		{sliceValue, sliceValue, []byte("42"), false},
   218  		{strValue, strValue, "42", false},
   219  		{
   220  			reflect.ValueOf("foo,bar,baz"),
   221  			sliceValue,
   222  			[]string{"foo", "bar", "baz"},
   223  			false,
   224  		},
   225  		{
   226  			reflect.ValueOf(""),
   227  			sliceValue,
   228  			[]string{},
   229  			false,
   230  		},
   231  	}
   232  
   233  	for i, tc := range cases {
   234  		actual, err := DecodeHookExec(f, tc.f, tc.t)
   235  		if tc.err != (err != nil) {
   236  			t.Fatalf("case %d: expected err %#v", i, tc.err)
   237  		}
   238  		if !reflect.DeepEqual(actual, tc.result) {
   239  			t.Fatalf(
   240  				"case %d: expected %#v, got %#v",
   241  				i, tc.result, actual)
   242  		}
   243  	}
   244  }
   245  
   246  func TestStringToTimeDurationHookFunc(t *testing.T) {
   247  	f := StringToTimeDurationHookFunc()
   248  
   249  	timeValue := reflect.ValueOf(time.Duration(5))
   250  	strValue := reflect.ValueOf("")
   251  	cases := []struct {
   252  		f, t   reflect.Value
   253  		result interface{}
   254  		err    bool
   255  	}{
   256  		{reflect.ValueOf("5s"), timeValue, 5 * time.Second, false},
   257  		{reflect.ValueOf("5"), timeValue, time.Duration(0), true},
   258  		{reflect.ValueOf("5"), strValue, "5", false},
   259  	}
   260  
   261  	for i, tc := range cases {
   262  		actual, err := DecodeHookExec(f, tc.f, tc.t)
   263  		if tc.err != (err != nil) {
   264  			t.Fatalf("case %d: expected err %#v", i, tc.err)
   265  		}
   266  		if !reflect.DeepEqual(actual, tc.result) {
   267  			t.Fatalf(
   268  				"case %d: expected %#v, got %#v",
   269  				i, tc.result, actual)
   270  		}
   271  	}
   272  }
   273  
   274  func TestStringToTimeHookFunc(t *testing.T) {
   275  	strValue := reflect.ValueOf("5")
   276  	timeValue := reflect.ValueOf(time.Time{})
   277  	cases := []struct {
   278  		f, t   reflect.Value
   279  		layout string
   280  		result interface{}
   281  		err    bool
   282  	}{
   283  		{reflect.ValueOf("2006-01-02T15:04:05Z"), timeValue, time.RFC3339,
   284  			time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC), false},
   285  		{strValue, timeValue, time.RFC3339, time.Time{}, true},
   286  		{strValue, strValue, time.RFC3339, "5", false},
   287  	}
   288  
   289  	for i, tc := range cases {
   290  		f := StringToTimeHookFunc(tc.layout)
   291  		actual, err := DecodeHookExec(f, tc.f, tc.t)
   292  		if tc.err != (err != nil) {
   293  			t.Fatalf("case %d: expected err %#v", i, tc.err)
   294  		}
   295  		if !reflect.DeepEqual(actual, tc.result) {
   296  			t.Fatalf(
   297  				"case %d: expected %#v, got %#v",
   298  				i, tc.result, actual)
   299  		}
   300  	}
   301  }
   302  
   303  func TestStringToIPHookFunc(t *testing.T) {
   304  	strValue := reflect.ValueOf("5")
   305  	ipValue := reflect.ValueOf(net.IP{})
   306  	cases := []struct {
   307  		f, t   reflect.Value
   308  		result interface{}
   309  		err    bool
   310  	}{
   311  		{reflect.ValueOf("1.2.3.4"), ipValue,
   312  			net.IPv4(0x01, 0x02, 0x03, 0x04), false},
   313  		{strValue, ipValue, net.IP{}, true},
   314  		{strValue, strValue, "5", false},
   315  	}
   316  
   317  	for i, tc := range cases {
   318  		f := StringToIPHookFunc()
   319  		actual, err := DecodeHookExec(f, tc.f, tc.t)
   320  		if tc.err != (err != nil) {
   321  			t.Fatalf("case %d: expected err %#v", i, tc.err)
   322  		}
   323  		if !reflect.DeepEqual(actual, tc.result) {
   324  			t.Fatalf(
   325  				"case %d: expected %#v, got %#v",
   326  				i, tc.result, actual)
   327  		}
   328  	}
   329  }
   330  
   331  func TestStringToIPNetHookFunc(t *testing.T) {
   332  	strValue := reflect.ValueOf("5")
   333  	ipNetValue := reflect.ValueOf(net.IPNet{})
   334  	var nilNet *net.IPNet = nil
   335  
   336  	cases := []struct {
   337  		f, t   reflect.Value
   338  		result interface{}
   339  		err    bool
   340  	}{
   341  		{reflect.ValueOf("1.2.3.4/24"), ipNetValue,
   342  			&net.IPNet{
   343  				IP:   net.IP{0x01, 0x02, 0x03, 0x00},
   344  				Mask: net.IPv4Mask(0xff, 0xff, 0xff, 0x00),
   345  			}, false},
   346  		{strValue, ipNetValue, nilNet, true},
   347  		{strValue, strValue, "5", false},
   348  	}
   349  
   350  	for i, tc := range cases {
   351  		f := StringToIPNetHookFunc()
   352  		actual, err := DecodeHookExec(f, tc.f, tc.t)
   353  		if tc.err != (err != nil) {
   354  			t.Fatalf("case %d: expected err %#v", i, tc.err)
   355  		}
   356  		if !reflect.DeepEqual(actual, tc.result) {
   357  			t.Fatalf(
   358  				"case %d: expected %#v, got %#v",
   359  				i, tc.result, actual)
   360  		}
   361  	}
   362  }
   363  
   364  func TestWeaklyTypedHook(t *testing.T) {
   365  	var f DecodeHookFunc = WeaklyTypedHook
   366  
   367  	strValue := reflect.ValueOf("")
   368  	cases := []struct {
   369  		f, t   reflect.Value
   370  		result interface{}
   371  		err    bool
   372  	}{
   373  		// TO STRING
   374  		{
   375  			reflect.ValueOf(false),
   376  			strValue,
   377  			"0",
   378  			false,
   379  		},
   380  
   381  		{
   382  			reflect.ValueOf(true),
   383  			strValue,
   384  			"1",
   385  			false,
   386  		},
   387  
   388  		{
   389  			reflect.ValueOf(float32(7)),
   390  			strValue,
   391  			"7",
   392  			false,
   393  		},
   394  
   395  		{
   396  			reflect.ValueOf(int(7)),
   397  			strValue,
   398  			"7",
   399  			false,
   400  		},
   401  
   402  		{
   403  			reflect.ValueOf([]uint8("foo")),
   404  			strValue,
   405  			"foo",
   406  			false,
   407  		},
   408  
   409  		{
   410  			reflect.ValueOf(uint(7)),
   411  			strValue,
   412  			"7",
   413  			false,
   414  		},
   415  	}
   416  
   417  	for i, tc := range cases {
   418  		actual, err := DecodeHookExec(f, tc.f, tc.t)
   419  		if tc.err != (err != nil) {
   420  			t.Fatalf("case %d: expected err %#v", i, tc.err)
   421  		}
   422  		if !reflect.DeepEqual(actual, tc.result) {
   423  			t.Fatalf(
   424  				"case %d: expected %#v, got %#v",
   425  				i, tc.result, actual)
   426  		}
   427  	}
   428  }
   429  
   430  func TestStructToMapHookFuncTabled(t *testing.T) {
   431  	var f DecodeHookFunc = RecursiveStructToMapHookFunc()
   432  
   433  	type b struct {
   434  		TestKey string
   435  	}
   436  
   437  	type a struct {
   438  		Sub b
   439  	}
   440  
   441  	testStruct := a{
   442  		Sub: b{
   443  			TestKey: "testval",
   444  		},
   445  	}
   446  
   447  	testMap := map[string]interface{}{
   448  		"Sub": map[string]interface{}{
   449  			"TestKey": "testval",
   450  		},
   451  	}
   452  
   453  	cases := []struct {
   454  		name     string
   455  		receiver interface{}
   456  		input    interface{}
   457  		expected interface{}
   458  		err      bool
   459  	}{
   460  		{
   461  			"map receiver",
   462  			func() interface{} {
   463  				var res map[string]interface{}
   464  				return &res
   465  			}(),
   466  			testStruct,
   467  			&testMap,
   468  			false,
   469  		},
   470  		{
   471  			"interface receiver",
   472  			func() interface{} {
   473  				var res interface{}
   474  				return &res
   475  			}(),
   476  			testStruct,
   477  			func() interface{} {
   478  				var exp interface{} = testMap
   479  				return &exp
   480  			}(),
   481  			false,
   482  		},
   483  		{
   484  			"slice receiver errors",
   485  			func() interface{} {
   486  				var res []string
   487  				return &res
   488  			}(),
   489  			testStruct,
   490  			new([]string),
   491  			true,
   492  		},
   493  		{
   494  			"slice to slice - no change",
   495  			func() interface{} {
   496  				var res []string
   497  				return &res
   498  			}(),
   499  			[]string{"a", "b"},
   500  			&[]string{"a", "b"},
   501  			false,
   502  		},
   503  		{
   504  			"string to string - no change",
   505  			func() interface{} {
   506  				var res string
   507  				return &res
   508  			}(),
   509  			"test",
   510  			func() *string {
   511  				s := "test"
   512  				return &s
   513  			}(),
   514  			false,
   515  		},
   516  	}
   517  
   518  	for _, tc := range cases {
   519  		t.Run(tc.name, func(t *testing.T) {
   520  			cfg := &DecoderConfig{
   521  				DecodeHook: f,
   522  				Result:     tc.receiver,
   523  			}
   524  
   525  			d, err := NewDecoder(cfg)
   526  			if err != nil {
   527  				t.Fatalf("unexpected err %#v", err)
   528  			}
   529  
   530  			err = d.Decode(tc.input)
   531  			if tc.err != (err != nil) {
   532  				t.Fatalf("expected err %#v", err)
   533  			}
   534  
   535  			if !reflect.DeepEqual(tc.expected, tc.receiver) {
   536  				t.Fatalf("expected %#v, got %#v",
   537  					tc.expected, tc.receiver)
   538  			}
   539  		})
   540  
   541  	}
   542  }
   543  
   544  func TestTextUnmarshallerHookFunc(t *testing.T) {
   545  	cases := []struct {
   546  		f, t   reflect.Value
   547  		result interface{}
   548  		err    bool
   549  	}{
   550  		{reflect.ValueOf("42"), reflect.ValueOf(big.Int{}), big.NewInt(42), false},
   551  		{reflect.ValueOf("invalid"), reflect.ValueOf(big.Int{}), nil, true},
   552  		{reflect.ValueOf("5"), reflect.ValueOf("5"), "5", false},
   553  	}
   554  
   555  	for i, tc := range cases {
   556  		f := TextUnmarshallerHookFunc()
   557  		actual, err := DecodeHookExec(f, tc.f, tc.t)
   558  		if tc.err != (err != nil) {
   559  			t.Fatalf("case %d: expected err %#v", i, tc.err)
   560  		}
   561  		if !reflect.DeepEqual(actual, tc.result) {
   562  			t.Fatalf(
   563  				"case %d: expected %#v, got %#v",
   564  				i, tc.result, actual)
   565  		}
   566  	}
   567  }
   568  

View as plain text