1package mapstructure
2
3import (
4	"errors"
5	"net"
6	"reflect"
7	"testing"
8	"time"
9)
10
11func TestComposeDecodeHookFunc(t *testing.T) {
12	f1 := func(
13		f reflect.Kind,
14		t reflect.Kind,
15		data interface{}) (interface{}, error) {
16		return data.(string) + "foo", nil
17	}
18
19	f2 := func(
20		f reflect.Kind,
21		t reflect.Kind,
22		data interface{}) (interface{}, error) {
23		return data.(string) + "bar", nil
24	}
25
26	f := ComposeDecodeHookFunc(f1, f2)
27
28	result, err := DecodeHookExec(
29		f, reflect.TypeOf(""), reflect.TypeOf([]byte("")), "")
30	if err != nil {
31		t.Fatalf("bad: %s", err)
32	}
33	if result.(string) != "foobar" {
34		t.Fatalf("bad: %#v", result)
35	}
36}
37
38func TestComposeDecodeHookFunc_err(t *testing.T) {
39	f1 := func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) {
40		return nil, errors.New("foo")
41	}
42
43	f2 := func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) {
44		panic("NOPE")
45	}
46
47	f := ComposeDecodeHookFunc(f1, f2)
48
49	_, err := DecodeHookExec(
50		f, reflect.TypeOf(""), reflect.TypeOf([]byte("")), 42)
51	if err.Error() != "foo" {
52		t.Fatalf("bad: %s", err)
53	}
54}
55
56func TestComposeDecodeHookFunc_kinds(t *testing.T) {
57	var f2From reflect.Kind
58
59	f1 := func(
60		f reflect.Kind,
61		t reflect.Kind,
62		data interface{}) (interface{}, error) {
63		return int(42), nil
64	}
65
66	f2 := func(
67		f reflect.Kind,
68		t reflect.Kind,
69		data interface{}) (interface{}, error) {
70		f2From = f
71		return data, nil
72	}
73
74	f := ComposeDecodeHookFunc(f1, f2)
75
76	_, err := DecodeHookExec(
77		f, reflect.TypeOf(""), reflect.TypeOf([]byte("")), "")
78	if err != nil {
79		t.Fatalf("bad: %s", err)
80	}
81	if f2From != reflect.Int {
82		t.Fatalf("bad: %#v", f2From)
83	}
84}
85
86func TestStringToSliceHookFunc(t *testing.T) {
87	f := StringToSliceHookFunc(",")
88
89	strType := reflect.TypeOf("")
90	sliceType := reflect.TypeOf([]byte(""))
91	cases := []struct {
92		f, t   reflect.Type
93		data   interface{}
94		result interface{}
95		err    bool
96	}{
97		{sliceType, sliceType, 42, 42, false},
98		{strType, strType, 42, 42, false},
99		{
100			strType,
101			sliceType,
102			"foo,bar,baz",
103			[]string{"foo", "bar", "baz"},
104			false,
105		},
106		{
107			strType,
108			sliceType,
109			"",
110			[]string{},
111			false,
112		},
113	}
114
115	for i, tc := range cases {
116		actual, err := DecodeHookExec(f, tc.f, tc.t, tc.data)
117		if tc.err != (err != nil) {
118			t.Fatalf("case %d: expected err %#v", i, tc.err)
119		}
120		if !reflect.DeepEqual(actual, tc.result) {
121			t.Fatalf(
122				"case %d: expected %#v, got %#v",
123				i, tc.result, actual)
124		}
125	}
126}
127
128func TestStringToTimeDurationHookFunc(t *testing.T) {
129	f := StringToTimeDurationHookFunc()
130
131	strType := reflect.TypeOf("")
132	timeType := reflect.TypeOf(time.Duration(5))
133	cases := []struct {
134		f, t   reflect.Type
135		data   interface{}
136		result interface{}
137		err    bool
138	}{
139		{strType, timeType, "5s", 5 * time.Second, false},
140		{strType, timeType, "5", time.Duration(0), true},
141		{strType, strType, "5", "5", false},
142	}
143
144	for i, tc := range cases {
145		actual, err := DecodeHookExec(f, tc.f, tc.t, tc.data)
146		if tc.err != (err != nil) {
147			t.Fatalf("case %d: expected err %#v", i, tc.err)
148		}
149		if !reflect.DeepEqual(actual, tc.result) {
150			t.Fatalf(
151				"case %d: expected %#v, got %#v",
152				i, tc.result, actual)
153		}
154	}
155}
156
157func TestStringToTimeHookFunc(t *testing.T) {
158	strType := reflect.TypeOf("")
159	timeType := reflect.TypeOf(time.Time{})
160	cases := []struct {
161		f, t   reflect.Type
162		layout string
163		data   interface{}
164		result interface{}
165		err    bool
166	}{
167		{strType, timeType, time.RFC3339, "2006-01-02T15:04:05Z",
168			time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC), false},
169		{strType, timeType, time.RFC3339, "5", time.Time{}, true},
170		{strType, strType, time.RFC3339, "5", "5", false},
171	}
172
173	for i, tc := range cases {
174		f := StringToTimeHookFunc(tc.layout)
175		actual, err := DecodeHookExec(f, tc.f, tc.t, tc.data)
176		if tc.err != (err != nil) {
177			t.Fatalf("case %d: expected err %#v", i, tc.err)
178		}
179		if !reflect.DeepEqual(actual, tc.result) {
180			t.Fatalf(
181				"case %d: expected %#v, got %#v",
182				i, tc.result, actual)
183		}
184	}
185}
186
187func TestStringToIPHookFunc(t *testing.T) {
188	strType := reflect.TypeOf("")
189	ipType := reflect.TypeOf(net.IP{})
190	cases := []struct {
191		f, t   reflect.Type
192		data   interface{}
193		result interface{}
194		err    bool
195	}{
196		{strType, ipType, "1.2.3.4",
197			net.IPv4(0x01, 0x02, 0x03, 0x04), false},
198		{strType, ipType, "5", net.IP{}, true},
199		{strType, strType, "5", "5", false},
200	}
201
202	for i, tc := range cases {
203		f := StringToIPHookFunc()
204		actual, err := DecodeHookExec(f, tc.f, tc.t, tc.data)
205		if tc.err != (err != nil) {
206			t.Fatalf("case %d: expected err %#v", i, tc.err)
207		}
208		if !reflect.DeepEqual(actual, tc.result) {
209			t.Fatalf(
210				"case %d: expected %#v, got %#v",
211				i, tc.result, actual)
212		}
213	}
214}
215
216func TestStringToIPNetHookFunc(t *testing.T) {
217	strType := reflect.TypeOf("")
218	ipNetType := reflect.TypeOf(net.IPNet{})
219	var nilNet *net.IPNet = nil
220
221	cases := []struct {
222		f, t   reflect.Type
223		data   interface{}
224		result interface{}
225		err    bool
226	}{
227		{strType, ipNetType, "1.2.3.4/24",
228			&net.IPNet{
229				IP:   net.IP{0x01, 0x02, 0x03, 0x00},
230				Mask: net.IPv4Mask(0xff, 0xff, 0xff, 0x00),
231			}, false},
232		{strType, ipNetType, "5", nilNet, true},
233		{strType, strType, "5", "5", false},
234	}
235
236	for i, tc := range cases {
237		f := StringToIPNetHookFunc()
238		actual, err := DecodeHookExec(f, tc.f, tc.t, tc.data)
239		if tc.err != (err != nil) {
240			t.Fatalf("case %d: expected err %#v", i, tc.err)
241		}
242		if !reflect.DeepEqual(actual, tc.result) {
243			t.Fatalf(
244				"case %d: expected %#v, got %#v",
245				i, tc.result, actual)
246		}
247	}
248}
249
250func TestWeaklyTypedHook(t *testing.T) {
251	var f DecodeHookFunc = WeaklyTypedHook
252
253	boolType := reflect.TypeOf(true)
254	strType := reflect.TypeOf("")
255	sliceType := reflect.TypeOf([]byte(""))
256	cases := []struct {
257		f, t   reflect.Type
258		data   interface{}
259		result interface{}
260		err    bool
261	}{
262		// TO STRING
263		{
264			boolType,
265			strType,
266			false,
267			"0",
268			false,
269		},
270
271		{
272			boolType,
273			strType,
274			true,
275			"1",
276			false,
277		},
278
279		{
280			reflect.TypeOf(float32(1)),
281			strType,
282			float32(7),
283			"7",
284			false,
285		},
286
287		{
288			reflect.TypeOf(int(1)),
289			strType,
290			int(7),
291			"7",
292			false,
293		},
294
295		{
296			sliceType,
297			strType,
298			[]uint8("foo"),
299			"foo",
300			false,
301		},
302
303		{
304			reflect.TypeOf(uint(1)),
305			strType,
306			uint(7),
307			"7",
308			false,
309		},
310	}
311
312	for i, tc := range cases {
313		actual, err := DecodeHookExec(f, tc.f, tc.t, tc.data)
314		if tc.err != (err != nil) {
315			t.Fatalf("case %d: expected err %#v", i, tc.err)
316		}
317		if !reflect.DeepEqual(actual, tc.result) {
318			t.Fatalf(
319				"case %d: expected %#v, got %#v",
320				i, tc.result, actual)
321		}
322	}
323}
324