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
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