1package mapstructure
2
3import (
4	"errors"
5	"math/big"
6	"net"
7	"reflect"
8	"testing"
9	"time"
10)
11
12func 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
39func 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
57func 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
87func TestStringToSliceHookFunc(t *testing.T) {
88	f := StringToSliceHookFunc(",")
89
90	strValue := reflect.ValueOf("42")
91	sliceValue := reflect.ValueOf([]byte("42"))
92	cases := []struct {
93		f, t   reflect.Value
94		result interface{}
95		err    bool
96	}{
97		{sliceValue, sliceValue, []byte("42"), false},
98		{strValue, strValue, "42", false},
99		{
100			reflect.ValueOf("foo,bar,baz"),
101			sliceValue,
102			[]string{"foo", "bar", "baz"},
103			false,
104		},
105		{
106			reflect.ValueOf(""),
107			sliceValue,
108			[]string{},
109			false,
110		},
111	}
112
113	for i, tc := range cases {
114		actual, err := DecodeHookExec(f, tc.f, tc.t)
115		if tc.err != (err != nil) {
116			t.Fatalf("case %d: expected err %#v", i, tc.err)
117		}
118		if !reflect.DeepEqual(actual, tc.result) {
119			t.Fatalf(
120				"case %d: expected %#v, got %#v",
121				i, tc.result, actual)
122		}
123	}
124}
125
126func TestStringToTimeDurationHookFunc(t *testing.T) {
127	f := StringToTimeDurationHookFunc()
128
129	timeValue := reflect.ValueOf(time.Duration(5))
130	strValue := reflect.ValueOf("")
131	cases := []struct {
132		f, t   reflect.Value
133		result interface{}
134		err    bool
135	}{
136		{reflect.ValueOf("5s"), timeValue, 5 * time.Second, false},
137		{reflect.ValueOf("5"), timeValue, time.Duration(0), true},
138		{reflect.ValueOf("5"), strValue, "5", false},
139	}
140
141	for i, tc := range cases {
142		actual, err := DecodeHookExec(f, tc.f, tc.t)
143		if tc.err != (err != nil) {
144			t.Fatalf("case %d: expected err %#v", i, tc.err)
145		}
146		if !reflect.DeepEqual(actual, tc.result) {
147			t.Fatalf(
148				"case %d: expected %#v, got %#v",
149				i, tc.result, actual)
150		}
151	}
152}
153
154func TestStringToTimeHookFunc(t *testing.T) {
155	strValue := reflect.ValueOf("5")
156	timeValue := reflect.ValueOf(time.Time{})
157	cases := []struct {
158		f, t   reflect.Value
159		layout string
160		result interface{}
161		err    bool
162	}{
163		{reflect.ValueOf("2006-01-02T15:04:05Z"), timeValue, time.RFC3339,
164			time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC), false},
165		{strValue, timeValue, time.RFC3339, time.Time{}, true},
166		{strValue, strValue, time.RFC3339, "5", false},
167	}
168
169	for i, tc := range cases {
170		f := StringToTimeHookFunc(tc.layout)
171		actual, err := DecodeHookExec(f, tc.f, tc.t)
172		if tc.err != (err != nil) {
173			t.Fatalf("case %d: expected err %#v", i, tc.err)
174		}
175		if !reflect.DeepEqual(actual, tc.result) {
176			t.Fatalf(
177				"case %d: expected %#v, got %#v",
178				i, tc.result, actual)
179		}
180	}
181}
182
183func TestStringToIPHookFunc(t *testing.T) {
184	strValue := reflect.ValueOf("5")
185	ipValue := reflect.ValueOf(net.IP{})
186	cases := []struct {
187		f, t   reflect.Value
188		result interface{}
189		err    bool
190	}{
191		{reflect.ValueOf("1.2.3.4"), ipValue,
192			net.IPv4(0x01, 0x02, 0x03, 0x04), false},
193		{strValue, ipValue, net.IP{}, true},
194		{strValue, strValue, "5", false},
195	}
196
197	for i, tc := range cases {
198		f := StringToIPHookFunc()
199		actual, err := DecodeHookExec(f, tc.f, tc.t)
200		if tc.err != (err != nil) {
201			t.Fatalf("case %d: expected err %#v", i, tc.err)
202		}
203		if !reflect.DeepEqual(actual, tc.result) {
204			t.Fatalf(
205				"case %d: expected %#v, got %#v",
206				i, tc.result, actual)
207		}
208	}
209}
210
211func TestStringToIPNetHookFunc(t *testing.T) {
212	strValue := reflect.ValueOf("5")
213	ipNetValue := reflect.ValueOf(net.IPNet{})
214	var nilNet *net.IPNet = nil
215
216	cases := []struct {
217		f, t   reflect.Value
218		result interface{}
219		err    bool
220	}{
221		{reflect.ValueOf("1.2.3.4/24"), ipNetValue,
222			&net.IPNet{
223				IP:   net.IP{0x01, 0x02, 0x03, 0x00},
224				Mask: net.IPv4Mask(0xff, 0xff, 0xff, 0x00),
225			}, false},
226		{strValue, ipNetValue, nilNet, true},
227		{strValue, strValue, "5", false},
228	}
229
230	for i, tc := range cases {
231		f := StringToIPNetHookFunc()
232		actual, err := DecodeHookExec(f, tc.f, tc.t)
233		if tc.err != (err != nil) {
234			t.Fatalf("case %d: expected err %#v", i, tc.err)
235		}
236		if !reflect.DeepEqual(actual, tc.result) {
237			t.Fatalf(
238				"case %d: expected %#v, got %#v",
239				i, tc.result, actual)
240		}
241	}
242}
243
244func TestWeaklyTypedHook(t *testing.T) {
245	var f DecodeHookFunc = WeaklyTypedHook
246
247	strValue := reflect.ValueOf("")
248	cases := []struct {
249		f, t   reflect.Value
250		result interface{}
251		err    bool
252	}{
253		// TO STRING
254		{
255			reflect.ValueOf(false),
256			strValue,
257			"0",
258			false,
259		},
260
261		{
262			reflect.ValueOf(true),
263			strValue,
264			"1",
265			false,
266		},
267
268		{
269			reflect.ValueOf(float32(7)),
270			strValue,
271			"7",
272			false,
273		},
274
275		{
276			reflect.ValueOf(int(7)),
277			strValue,
278			"7",
279			false,
280		},
281
282		{
283			reflect.ValueOf([]uint8("foo")),
284			strValue,
285			"foo",
286			false,
287		},
288
289		{
290			reflect.ValueOf(uint(7)),
291			strValue,
292			"7",
293			false,
294		},
295	}
296
297	for i, tc := range cases {
298		actual, err := DecodeHookExec(f, tc.f, tc.t)
299		if tc.err != (err != nil) {
300			t.Fatalf("case %d: expected err %#v", i, tc.err)
301		}
302		if !reflect.DeepEqual(actual, tc.result) {
303			t.Fatalf(
304				"case %d: expected %#v, got %#v",
305				i, tc.result, actual)
306		}
307	}
308}
309
310func TestStructToMapHookFuncTabled(t *testing.T) {
311	var f DecodeHookFunc = RecursiveStructToMapHookFunc()
312
313	type b struct {
314		TestKey string
315	}
316
317	type a struct {
318		Sub b
319	}
320
321	testStruct := a{
322		Sub: b{
323			TestKey: "testval",
324		},
325	}
326
327	testMap := map[string]interface{}{
328		"Sub": map[string]interface{}{
329			"TestKey": "testval",
330		},
331	}
332
333	cases := []struct {
334		name     string
335		receiver interface{}
336		input    interface{}
337		expected interface{}
338		err      bool
339	}{
340		{
341			"map receiver",
342			func() interface{} {
343				var res map[string]interface{}
344				return &res
345			}(),
346			testStruct,
347			&testMap,
348			false,
349		},
350		{
351			"interface receiver",
352			func() interface{} {
353				var res interface{}
354				return &res
355			}(),
356			testStruct,
357			func() interface{} {
358				var exp interface{} = testMap
359				return &exp
360			}(),
361			false,
362		},
363		{
364			"slice receiver errors",
365			func() interface{} {
366				var res []string
367				return &res
368			}(),
369			testStruct,
370			new([]string),
371			true,
372		},
373		{
374			"slice to slice - no change",
375			func() interface{} {
376				var res []string
377				return &res
378			}(),
379			[]string{"a", "b"},
380			&[]string{"a", "b"},
381			false,
382		},
383		{
384			"string to string - no change",
385			func() interface{} {
386				var res string
387				return &res
388			}(),
389			"test",
390			func() *string {
391				s := "test"
392				return &s
393			}(),
394			false,
395		},
396	}
397
398	for _, tc := range cases {
399		t.Run(tc.name, func(t *testing.T) {
400			cfg := &DecoderConfig{
401				DecodeHook: f,
402				Result:     tc.receiver,
403			}
404
405			d, err := NewDecoder(cfg)
406			if err != nil {
407				t.Fatalf("unexpected err %#v", err)
408			}
409
410			err = d.Decode(tc.input)
411			if tc.err != (err != nil) {
412				t.Fatalf("expected err %#v", err)
413			}
414
415			if !reflect.DeepEqual(tc.expected, tc.receiver) {
416				t.Fatalf("expected %#v, got %#v",
417					tc.expected, tc.receiver)
418			}
419		})
420
421	}
422}
423
424func TestTextUnmarshallerHookFunc(t *testing.T) {
425	cases := []struct {
426		f, t   reflect.Value
427		result interface{}
428		err    bool
429	}{
430		{reflect.ValueOf("42"), reflect.ValueOf(big.Int{}), big.NewInt(42), false},
431		{reflect.ValueOf("invalid"), reflect.ValueOf(big.Int{}), nil, true},
432		{reflect.ValueOf("5"), reflect.ValueOf("5"), "5", false},
433	}
434
435	for i, tc := range cases {
436		f := TextUnmarshallerHookFunc()
437		actual, err := DecodeHookExec(f, tc.f, tc.t)
438		if tc.err != (err != nil) {
439			t.Fatalf("case %d: expected err %#v", i, tc.err)
440		}
441		if !reflect.DeepEqual(actual, tc.result) {
442			t.Fatalf(
443				"case %d: expected %#v, got %#v",
444				i, tc.result, actual)
445		}
446	}
447}
448