1 package mapstructure
2
3 import (
4 "reflect"
5 "testing"
6 "time"
7 )
8
9
10 func TestDecode_NilValue(t *testing.T) {
11 t.Parallel()
12
13 tests := []struct {
14 name string
15 in interface{}
16 target interface{}
17 out interface{}
18 metaKeys []string
19 metaUnused []string
20 }{
21 {
22 "all nil",
23 &map[string]interface{}{
24 "vfoo": nil,
25 "vother": nil,
26 },
27 &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
28 &Map{Vfoo: "", Vother: nil},
29 []string{"Vfoo", "Vother"},
30 []string{},
31 },
32 {
33 "partial nil",
34 &map[string]interface{}{
35 "vfoo": "baz",
36 "vother": nil,
37 },
38 &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
39 &Map{Vfoo: "baz", Vother: nil},
40 []string{"Vfoo", "Vother"},
41 []string{},
42 },
43 {
44 "partial decode",
45 &map[string]interface{}{
46 "vother": nil,
47 },
48 &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
49 &Map{Vfoo: "foo", Vother: nil},
50 []string{"Vother"},
51 []string{},
52 },
53 {
54 "unused values",
55 &map[string]interface{}{
56 "vbar": "bar",
57 "vfoo": nil,
58 "vother": nil,
59 },
60 &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
61 &Map{Vfoo: "", Vother: nil},
62 []string{"Vfoo", "Vother"},
63 []string{"vbar"},
64 },
65 {
66 "map interface all nil",
67 &map[interface{}]interface{}{
68 "vfoo": nil,
69 "vother": nil,
70 },
71 &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
72 &Map{Vfoo: "", Vother: nil},
73 []string{"Vfoo", "Vother"},
74 []string{},
75 },
76 {
77 "map interface partial nil",
78 &map[interface{}]interface{}{
79 "vfoo": "baz",
80 "vother": nil,
81 },
82 &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
83 &Map{Vfoo: "baz", Vother: nil},
84 []string{"Vfoo", "Vother"},
85 []string{},
86 },
87 {
88 "map interface partial decode",
89 &map[interface{}]interface{}{
90 "vother": nil,
91 },
92 &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
93 &Map{Vfoo: "foo", Vother: nil},
94 []string{"Vother"},
95 []string{},
96 },
97 {
98 "map interface unused values",
99 &map[interface{}]interface{}{
100 "vbar": "bar",
101 "vfoo": nil,
102 "vother": nil,
103 },
104 &Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
105 &Map{Vfoo: "", Vother: nil},
106 []string{"Vfoo", "Vother"},
107 []string{"vbar"},
108 },
109 }
110
111 for _, tc := range tests {
112 t.Run(tc.name, func(t *testing.T) {
113 config := &DecoderConfig{
114 Metadata: new(Metadata),
115 Result: tc.target,
116 ZeroFields: true,
117 }
118
119 decoder, err := NewDecoder(config)
120 if err != nil {
121 t.Fatalf("should not error: %s", err)
122 }
123
124 err = decoder.Decode(tc.in)
125 if err != nil {
126 t.Fatalf("should not error: %s", err)
127 }
128
129 if !reflect.DeepEqual(tc.out, tc.target) {
130 t.Fatalf("%q: TestDecode_NilValue() expected: %#v, got: %#v", tc.name, tc.out, tc.target)
131 }
132
133 if !reflect.DeepEqual(tc.metaKeys, config.Metadata.Keys) {
134 t.Fatalf("%q: Metadata.Keys mismatch expected: %#v, got: %#v", tc.name, tc.metaKeys, config.Metadata.Keys)
135 }
136
137 if !reflect.DeepEqual(tc.metaUnused, config.Metadata.Unused) {
138 t.Fatalf("%q: Metadata.Unused mismatch expected: %#v, got: %#v", tc.name, tc.metaUnused, config.Metadata.Unused)
139 }
140 })
141 }
142 }
143
144
145 func TestNestedTypePointerWithDefaults(t *testing.T) {
146 t.Parallel()
147
148 input := map[string]interface{}{
149 "vfoo": "foo",
150 "vbar": map[string]interface{}{
151 "vstring": "foo",
152 "vint": 42,
153 "vbool": true,
154 },
155 }
156
157 result := NestedPointer{
158 Vbar: &Basic{
159 Vuint: 42,
160 },
161 }
162 err := Decode(input, &result)
163 if err != nil {
164 t.Fatalf("got an err: %s", err.Error())
165 }
166
167 if result.Vfoo != "foo" {
168 t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
169 }
170
171 if result.Vbar.Vstring != "foo" {
172 t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring)
173 }
174
175 if result.Vbar.Vint != 42 {
176 t.Errorf("vint value should be 42: %#v", result.Vbar.Vint)
177 }
178
179 if result.Vbar.Vbool != true {
180 t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool)
181 }
182
183 if result.Vbar.Vextra != "" {
184 t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra)
185 }
186
187
188 if result.Vbar.Vuint != 42 {
189 t.Errorf("vuint value should be 42: %#v", result.Vbar.Vuint)
190 }
191
192 }
193
194 type NestedSlice struct {
195 Vfoo string
196 Vbars []Basic
197 Vempty []Basic
198 }
199
200
201 func TestNestedTypeSliceWithDefaults(t *testing.T) {
202 t.Parallel()
203
204 input := map[string]interface{}{
205 "vfoo": "foo",
206 "vbars": []map[string]interface{}{
207 {"vstring": "foo", "vint": 42, "vbool": true},
208 {"vint": 42, "vbool": true},
209 },
210 "vempty": []map[string]interface{}{
211 {"vstring": "foo", "vint": 42, "vbool": true},
212 {"vint": 42, "vbool": true},
213 },
214 }
215
216 result := NestedSlice{
217 Vbars: []Basic{
218 {Vuint: 42},
219 {Vstring: "foo"},
220 },
221 }
222 err := Decode(input, &result)
223 if err != nil {
224 t.Fatalf("got an err: %s", err.Error())
225 }
226
227 if result.Vfoo != "foo" {
228 t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
229 }
230
231 if result.Vbars[0].Vstring != "foo" {
232 t.Errorf("vstring value should be 'foo': %#v", result.Vbars[0].Vstring)
233 }
234
235 if result.Vbars[0].Vuint != 42 {
236 t.Errorf("vuint value should be 42: %#v", result.Vbars[0].Vuint)
237 }
238 }
239
240
241 func TestNestedTypeWithDefaults(t *testing.T) {
242 t.Parallel()
243
244 input := map[string]interface{}{
245 "vfoo": "foo",
246 "vbar": map[string]interface{}{
247 "vstring": "foo",
248 "vint": 42,
249 "vbool": true,
250 },
251 }
252
253 result := Nested{
254 Vbar: Basic{
255 Vuint: 42,
256 },
257 }
258 err := Decode(input, &result)
259 if err != nil {
260 t.Fatalf("got an err: %s", err.Error())
261 }
262
263 if result.Vfoo != "foo" {
264 t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
265 }
266
267 if result.Vbar.Vstring != "foo" {
268 t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring)
269 }
270
271 if result.Vbar.Vint != 42 {
272 t.Errorf("vint value should be 42: %#v", result.Vbar.Vint)
273 }
274
275 if result.Vbar.Vbool != true {
276 t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool)
277 }
278
279 if result.Vbar.Vextra != "" {
280 t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra)
281 }
282
283
284 if result.Vbar.Vuint != 42 {
285 t.Errorf("vuint value should be 42: %#v", result.Vbar.Vuint)
286 }
287
288 }
289
290
291 func TestDecodeSliceToEmptySliceWOZeroing(t *testing.T) {
292 t.Parallel()
293
294 type TestStruct struct {
295 Vfoo []string
296 }
297
298 decode := func(m interface{}, rawVal interface{}) error {
299 config := &DecoderConfig{
300 Metadata: nil,
301 Result: rawVal,
302 ZeroFields: false,
303 }
304
305 decoder, err := NewDecoder(config)
306 if err != nil {
307 return err
308 }
309
310 return decoder.Decode(m)
311 }
312
313 {
314 input := map[string]interface{}{
315 "vfoo": []string{"1"},
316 }
317
318 result := &TestStruct{}
319
320 err := decode(input, &result)
321 if err != nil {
322 t.Fatalf("got an err: %s", err.Error())
323 }
324 }
325
326 {
327 input := map[string]interface{}{
328 "vfoo": []string{"1"},
329 }
330
331 result := &TestStruct{
332 Vfoo: []string{},
333 }
334
335 err := decode(input, &result)
336 if err != nil {
337 t.Fatalf("got an err: %s", err.Error())
338 }
339 }
340
341 {
342 input := map[string]interface{}{
343 "vfoo": []string{"2", "3"},
344 }
345
346 result := &TestStruct{
347 Vfoo: []string{"1"},
348 }
349
350 err := decode(input, &result)
351 if err != nil {
352 t.Fatalf("got an err: %s", err.Error())
353 }
354 }
355 }
356
357
358 func TestNextSquashMapstructure(t *testing.T) {
359 data := &struct {
360 Level1 struct {
361 Level2 struct {
362 Foo string
363 } `mapstructure:",squash"`
364 } `mapstructure:",squash"`
365 }{}
366 err := Decode(map[interface{}]interface{}{"foo": "baz"}, &data)
367 if err != nil {
368 t.Fatalf("should not error: %s", err)
369 }
370 if data.Level1.Level2.Foo != "baz" {
371 t.Fatal("value should be baz")
372 }
373 }
374
375 type ImplementsInterfacePointerReceiver struct {
376 Name string
377 }
378
379 func (i *ImplementsInterfacePointerReceiver) DoStuff() {}
380
381 type ImplementsInterfaceValueReceiver string
382
383 func (i ImplementsInterfaceValueReceiver) DoStuff() {}
384
385
386 func TestDecode_DecodeHookInterface(t *testing.T) {
387 t.Parallel()
388
389 type Interface interface {
390 DoStuff()
391 }
392 type DecodeIntoInterface struct {
393 Test Interface
394 }
395
396 testData := map[string]string{"test": "test"}
397
398 stringToPointerInterfaceDecodeHook := func(from, to reflect.Type, data interface{}) (interface{}, error) {
399 if from.Kind() != reflect.String {
400 return data, nil
401 }
402
403 if to != reflect.TypeOf((*Interface)(nil)).Elem() {
404 return data, nil
405 }
406
407 var impl Interface = &ImplementsInterfacePointerReceiver{data.(string)}
408 return impl, nil
409 }
410
411 stringToValueInterfaceDecodeHook := func(from, to reflect.Type, data interface{}) (interface{}, error) {
412 if from.Kind() != reflect.String {
413 return data, nil
414 }
415
416 if to != reflect.TypeOf((*Interface)(nil)).Elem() {
417 return data, nil
418 }
419
420 var impl Interface = ImplementsInterfaceValueReceiver(data.(string))
421 return impl, nil
422 }
423
424 {
425 decodeInto := new(DecodeIntoInterface)
426
427 decoder, _ := NewDecoder(&DecoderConfig{
428 DecodeHook: stringToPointerInterfaceDecodeHook,
429 Result: decodeInto,
430 })
431
432 err := decoder.Decode(testData)
433 if err != nil {
434 t.Fatalf("Decode returned error: %s", err)
435 }
436
437 expected := &ImplementsInterfacePointerReceiver{"test"}
438 if !reflect.DeepEqual(decodeInto.Test, expected) {
439 t.Fatalf("expected: %#v (%T), got: %#v (%T)", decodeInto.Test, decodeInto.Test, expected, expected)
440 }
441 }
442
443 {
444 decodeInto := new(DecodeIntoInterface)
445
446 decoder, _ := NewDecoder(&DecoderConfig{
447 DecodeHook: stringToValueInterfaceDecodeHook,
448 Result: decodeInto,
449 })
450
451 err := decoder.Decode(testData)
452 if err != nil {
453 t.Fatalf("Decode returned error: %s", err)
454 }
455
456 expected := ImplementsInterfaceValueReceiver("test")
457 if !reflect.DeepEqual(decodeInto.Test, expected) {
458 t.Fatalf("expected: %#v (%T), got: %#v (%T)", decodeInto.Test, decodeInto.Test, expected, expected)
459 }
460 }
461 }
462
463
464
465 func TestDecodeBadDataTypeInSlice(t *testing.T) {
466 t.Parallel()
467
468 input := map[string]interface{}{
469 "Toto": "titi",
470 }
471 result := []struct {
472 Toto string
473 }{}
474
475 if err := Decode(input, &result); err == nil {
476 t.Error("An error was expected, got nil")
477 }
478 }
479
480
481
482 func TestDecodeIntermediateMapsSettable(t *testing.T) {
483 type Timestamp struct {
484 Seconds int64
485 Nanos int32
486 }
487
488 type TsWrapper struct {
489 Timestamp *Timestamp
490 }
491
492 type TimeWrapper struct {
493 Timestamp time.Time
494 }
495
496 input := TimeWrapper{
497 Timestamp: time.Unix(123456789, 987654),
498 }
499
500 expected := TsWrapper{
501 Timestamp: &Timestamp{
502 Seconds: 123456789,
503 Nanos: 987654,
504 },
505 }
506
507 timePtrType := reflect.TypeOf((*time.Time)(nil))
508 mapStrInfType := reflect.TypeOf((map[string]interface{})(nil))
509
510 var actual TsWrapper
511 decoder, err := NewDecoder(&DecoderConfig{
512 Result: &actual,
513 DecodeHook: func(from, to reflect.Type, data interface{}) (interface{}, error) {
514 if from == timePtrType && to == mapStrInfType {
515 ts := data.(*time.Time)
516 nanos := ts.UnixNano()
517
518 seconds := nanos / 1000000000
519 nanos = nanos % 1000000000
520
521 return &map[string]interface{}{
522 "Seconds": seconds,
523 "Nanos": int32(nanos),
524 }, nil
525 }
526 return data, nil
527 },
528 })
529
530 if err != nil {
531 t.Fatalf("failed to create decoder: %v", err)
532 }
533
534 if err := decoder.Decode(&input); err != nil {
535 t.Fatalf("failed to decode input: %v", err)
536 }
537
538 if !reflect.DeepEqual(expected, actual) {
539 t.Fatalf("expected: %#[1]v (%[1]T), got: %#[2]v (%[2]T)", expected, actual)
540 }
541 }
542
543
544 func TestDecode_weakEmptyStringToInt(t *testing.T) {
545 input := map[string]interface{}{
546 "StringToInt": "",
547 "StringToUint": "",
548 "StringToBool": "",
549 "StringToFloat": "",
550 }
551
552 expectedResultWeak := TypeConversionResult{
553 StringToInt: 0,
554 StringToUint: 0,
555 StringToBool: false,
556 StringToFloat: 0,
557 }
558
559
560 var resultWeak TypeConversionResult
561 err := WeakDecode(input, &resultWeak)
562 if err != nil {
563 t.Fatalf("got an err: %s", err)
564 }
565
566 if !reflect.DeepEqual(resultWeak, expectedResultWeak) {
567 t.Errorf("expected \n%#v, got: \n%#v", expectedResultWeak, resultWeak)
568 }
569 }
570
571
572 func TestMapSquash(t *testing.T) {
573 type AA struct {
574 T *time.Time
575 }
576 type A struct {
577 AA
578 }
579
580 v := time.Now()
581 in := &AA{
582 T: &v,
583 }
584 out := &A{}
585 d, err := NewDecoder(&DecoderConfig{
586 Squash: true,
587 Result: out,
588 })
589 if err != nil {
590 t.Fatalf("err: %s", err)
591 }
592 if err := d.Decode(in); err != nil {
593 t.Fatalf("err: %s", err)
594 }
595
596
597 if !v.Equal(*out.T) {
598 t.Fatal("expected equal")
599 }
600 if out.T.IsZero() {
601 t.Fatal("expected false")
602 }
603 }
604
605
606 func TestMapOmitEmptyWithEmptyFieldnameInTag(t *testing.T) {
607 type Struct struct {
608 Username string `mapstructure:",omitempty"`
609 Age int `mapstructure:",omitempty"`
610 }
611
612 s := Struct{
613 Username: "Joe",
614 }
615 var m map[string]interface{}
616
617 if err := Decode(s, &m); err != nil {
618 t.Fatal(err)
619 }
620
621 if len(m) != 1 {
622 t.Fatalf("fail: %#v", m)
623 }
624 if m["Username"] != "Joe" {
625 t.Fatalf("fail: %#v", m)
626 }
627 }
628
View as plain text