1
2
3
4
5
6 package mergo_test
7
8 import (
9 "io/ioutil"
10 "reflect"
11 "strings"
12 "testing"
13 "time"
14
15 "dario.cat/mergo"
16 "gopkg.in/yaml.v3"
17 )
18
19 type simpleTest struct {
20 Value int
21 }
22
23 type complexTest struct {
24 ID string
25 St simpleTest
26 sz int
27 }
28
29 type mapTest struct {
30 M map[int]int
31 }
32
33 type ifcTest struct {
34 I interface{}
35 }
36
37 type moreComplextText struct {
38 Ct complexTest
39 St simpleTest
40 Nt simpleTest
41 }
42
43 type pointerTest struct {
44 C *simpleTest
45 }
46
47 type sliceTest struct {
48 S []int
49 }
50
51 func TestKb(t *testing.T) {
52 type testStruct struct {
53 KeyValue map[string]interface{}
54 Name string
55 }
56
57 akv := make(map[string]interface{})
58 akv["Key1"] = "not value 1"
59 akv["Key2"] = "value2"
60 a := testStruct{}
61 a.Name = "A"
62 a.KeyValue = akv
63
64 bkv := make(map[string]interface{})
65 bkv["Key1"] = "value1"
66 bkv["Key3"] = "value3"
67 b := testStruct{}
68 b.Name = "B"
69 b.KeyValue = bkv
70
71 ekv := make(map[string]interface{})
72 ekv["Key1"] = "value1"
73 ekv["Key2"] = "value2"
74 ekv["Key3"] = "value3"
75 expected := testStruct{}
76 expected.Name = "B"
77 expected.KeyValue = ekv
78
79 if err := mergo.Merge(&b, a); err != nil {
80 t.Error(err)
81 }
82
83 if !reflect.DeepEqual(b, expected) {
84 t.Errorf("Actual: %#v did not match \nExpected: %#v", b, expected)
85 }
86 }
87
88 func TestNil(t *testing.T) {
89 if err := mergo.Merge(nil, nil); err != mergo.ErrNilArguments {
90 t.Fail()
91 }
92 }
93
94 func TestDifferentTypes(t *testing.T) {
95 a := simpleTest{42}
96 b := 42
97 if err := mergo.Merge(&a, b); err != mergo.ErrDifferentArgumentsTypes {
98 t.Fail()
99 }
100 }
101
102 func TestSimpleStruct(t *testing.T) {
103 a := simpleTest{}
104 b := simpleTest{42}
105 if err := mergo.Merge(&a, b); err != nil {
106 t.FailNow()
107 }
108 if a.Value != 42 {
109 t.Errorf("b not merged in properly: a.Value(%d) != b.Value(%d)", a.Value, b.Value)
110 }
111 if !reflect.DeepEqual(a, b) {
112 t.FailNow()
113 }
114 }
115
116 func TestComplexStruct(t *testing.T) {
117 a := complexTest{}
118 a.ID = "athing"
119 b := complexTest{"bthing", simpleTest{42}, 1}
120 if err := mergo.Merge(&a, b); err != nil {
121 t.FailNow()
122 }
123 if a.St.Value != 42 {
124 t.Errorf("b not merged in properly: a.St.Value(%d) != b.St.Value(%d)", a.St.Value, b.St.Value)
125 }
126 if a.sz == 1 {
127 t.Errorf("a's private field sz not preserved from merge: a.sz(%d) == b.sz(%d)", a.sz, b.sz)
128 }
129 if a.ID == b.ID {
130 t.Errorf("a's field ID merged unexpectedly: a.ID(%s) == b.ID(%s)", a.ID, b.ID)
131 }
132 }
133
134 func TestComplexStructWithOverwrite(t *testing.T) {
135 a := complexTest{"do-not-overwrite-with-empty-value", simpleTest{1}, 1}
136 b := complexTest{"", simpleTest{42}, 2}
137
138 expect := complexTest{"do-not-overwrite-with-empty-value", simpleTest{42}, 1}
139 if err := mergo.MergeWithOverwrite(&a, b); err != nil {
140 t.FailNow()
141 }
142
143 if !reflect.DeepEqual(a, expect) {
144 t.Errorf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", a, expect)
145 }
146 }
147
148 func TestPointerStruct(t *testing.T) {
149 s1 := simpleTest{}
150 s2 := simpleTest{19}
151 a := pointerTest{&s1}
152 b := pointerTest{&s2}
153 if err := mergo.Merge(&a, b); err != nil {
154 t.FailNow()
155 }
156 if a.C.Value != b.C.Value {
157 t.Errorf("b not merged in properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value)
158 }
159 }
160
161 type embeddingStruct struct {
162 embeddedStruct
163 }
164
165 type embeddedStruct struct {
166 A string
167 }
168
169 func TestEmbeddedStruct(t *testing.T) {
170 tests := []struct {
171 src embeddingStruct
172 dst embeddingStruct
173 expected embeddingStruct
174 }{
175 {
176 src: embeddingStruct{
177 embeddedStruct{"foo"},
178 },
179 dst: embeddingStruct{
180 embeddedStruct{""},
181 },
182 expected: embeddingStruct{
183 embeddedStruct{"foo"},
184 },
185 },
186 {
187 src: embeddingStruct{
188 embeddedStruct{""},
189 },
190 dst: embeddingStruct{
191 embeddedStruct{"bar"},
192 },
193 expected: embeddingStruct{
194 embeddedStruct{"bar"},
195 },
196 },
197 {
198 src: embeddingStruct{
199 embeddedStruct{"foo"},
200 },
201 dst: embeddingStruct{
202 embeddedStruct{"bar"},
203 },
204 expected: embeddingStruct{
205 embeddedStruct{"bar"},
206 },
207 },
208 }
209
210 for _, test := range tests {
211 err := mergo.Merge(&test.dst, test.src)
212 if err != nil {
213 t.Errorf("unexpected error: %v", err)
214 continue
215 }
216 if !reflect.DeepEqual(test.dst, test.expected) {
217 t.Errorf("unexpected output\nexpected:\n%+v\nsaw:\n%+v\n", test.expected, test.dst)
218 }
219 }
220 }
221
222 func TestPointerStructNil(t *testing.T) {
223 a := pointerTest{nil}
224 b := pointerTest{&simpleTest{19}}
225 if err := mergo.Merge(&a, b); err != nil {
226 t.FailNow()
227 }
228 if a.C.Value != b.C.Value {
229 t.Errorf("b not merged in a properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value)
230 }
231 }
232
233 func testSlice(t *testing.T, a []int, b []int, e []int, opts ...func(*mergo.Config)) {
234 t.Helper()
235 bc := b
236
237 sa := sliceTest{a}
238 sb := sliceTest{b}
239 if err := mergo.Merge(&sa, sb, opts...); err != nil {
240 t.FailNow()
241 }
242 if !reflect.DeepEqual(sb.S, bc) {
243 t.Errorf("Source slice was modified %d != %d", sb.S, bc)
244 }
245 if !reflect.DeepEqual(sa.S, e) {
246 t.Errorf("b not merged in a proper way %d != %d", sa.S, e)
247 }
248
249 ma := map[string][]int{"S": a}
250 mb := map[string][]int{"S": b}
251 if err := mergo.Merge(&ma, mb, opts...); err != nil {
252 t.FailNow()
253 }
254 if !reflect.DeepEqual(mb["S"], bc) {
255 t.Errorf("map value: Source slice was modified %d != %d", mb["S"], bc)
256 }
257 if !reflect.DeepEqual(ma["S"], e) {
258 t.Errorf("map value: b not merged in a proper way %d != %d", ma["S"], e)
259 }
260
261 if a == nil {
262
263 ma := map[string][]int{}
264 mb := map[string][]int{"S": b}
265 if err := mergo.Merge(&ma, mb); err != nil {
266 t.FailNow()
267 }
268 if !reflect.DeepEqual(mb["S"], bc) {
269 t.Errorf("missing dst key: Source slice was modified %d != %d", mb["S"], bc)
270 }
271 if !reflect.DeepEqual(ma["S"], e) {
272 t.Errorf("missing dst key: b not merged in a proper way %d != %d", ma["S"], e)
273 }
274 }
275
276 if b == nil {
277
278 ma := map[string][]int{"S": a}
279 mb := map[string][]int{}
280 if err := mergo.Merge(&ma, mb); err != nil {
281 t.FailNow()
282 }
283 if !reflect.DeepEqual(mb["S"], bc) {
284 t.Errorf("missing src key: Source slice was modified %d != %d", mb["S"], bc)
285 }
286 if !reflect.DeepEqual(ma["S"], e) {
287 t.Errorf("missing src key: b not merged in a proper way %d != %d", ma["S"], e)
288 }
289 }
290 }
291
292 func TestSlice(t *testing.T) {
293 testSlice(t, nil, []int{1, 2, 3}, []int{1, 2, 3})
294 testSlice(t, []int{}, []int{1, 2, 3}, []int{1, 2, 3})
295 testSlice(t, []int{1}, []int{2, 3}, []int{1})
296 testSlice(t, []int{1}, []int{}, []int{1})
297 testSlice(t, []int{1}, nil, []int{1})
298 testSlice(t, nil, []int{1, 2, 3}, []int{1, 2, 3}, mergo.WithAppendSlice)
299 testSlice(t, []int{}, []int{1, 2, 3}, []int{1, 2, 3}, mergo.WithAppendSlice)
300 testSlice(t, []int{1}, []int{2, 3}, []int{1, 2, 3}, mergo.WithAppendSlice)
301 testSlice(t, []int{1}, []int{2, 3}, []int{1, 2, 3}, mergo.WithAppendSlice, mergo.WithOverride)
302 testSlice(t, []int{1}, []int{}, []int{1}, mergo.WithAppendSlice)
303 testSlice(t, []int{1}, nil, []int{1}, mergo.WithAppendSlice)
304 }
305
306 func TestEmptyMaps(t *testing.T) {
307 a := mapTest{}
308 b := mapTest{
309 map[int]int{},
310 }
311 if err := mergo.Merge(&a, b); err != nil {
312 t.Fail()
313 }
314 if !reflect.DeepEqual(a, b) {
315 t.FailNow()
316 }
317 }
318
319 func TestEmptyToEmptyMaps(t *testing.T) {
320 a := mapTest{}
321 b := mapTest{}
322 if err := mergo.Merge(&a, b); err != nil {
323 t.Fail()
324 }
325 if !reflect.DeepEqual(a, b) {
326 t.FailNow()
327 }
328 }
329
330 func TestEmptyToNotEmptyMaps(t *testing.T) {
331 a := mapTest{map[int]int{
332 1: 2,
333 3: 4,
334 }}
335 aa := mapTest{map[int]int{
336 1: 2,
337 3: 4,
338 }}
339 b := mapTest{
340 map[int]int{},
341 }
342 if err := mergo.Merge(&a, b); err != nil {
343 t.Fail()
344 }
345 if !reflect.DeepEqual(a, aa) {
346 t.FailNow()
347 }
348 }
349
350 func TestMapsWithOverwrite(t *testing.T) {
351 m := map[string]simpleTest{
352 "a": {},
353 "b": {42},
354 "c": {13},
355 "d": {61},
356 }
357 n := map[string]simpleTest{
358 "a": {16},
359 "b": {},
360 "c": {12},
361 "e": {14},
362 }
363 expect := map[string]simpleTest{
364 "a": {16},
365 "b": {},
366 "c": {12},
367 "d": {61},
368 "e": {14},
369 }
370
371 if err := mergo.MergeWithOverwrite(&m, n); err != nil {
372 t.Errorf(err.Error())
373 }
374
375 if !reflect.DeepEqual(m, expect) {
376 t.Errorf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect)
377 }
378 }
379
380 func TestMapWithEmbeddedStructPointer(t *testing.T) {
381 m := map[string]*simpleTest{
382 "a": {},
383 "b": {42},
384 "c": {13},
385 "d": {61},
386 }
387 n := map[string]*simpleTest{
388 "a": {16},
389 "b": {},
390 "c": {12},
391 "e": {14},
392 }
393 expect := map[string]*simpleTest{
394 "a": {16},
395 "b": {42},
396 "c": {12},
397 "d": {61},
398 "e": {14},
399 }
400
401 if err := mergo.Merge(&m, n, mergo.WithOverride); err != nil {
402 t.Errorf(err.Error())
403 }
404
405 if !reflect.DeepEqual(m, expect) {
406 t.Errorf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect)
407 }
408 }
409
410 func TestMergeUsingStructAndMap(t *testing.T) {
411 type multiPtr struct {
412 Text string
413 Number int
414 }
415 type final struct {
416 Msg1 string
417 Msg2 string
418 }
419 type params struct {
420 Multi *multiPtr
421 Final *final
422 Name string
423 }
424 type config struct {
425 Params *params
426 Foo string
427 Bar string
428 }
429
430 cases := []struct {
431 changes *config
432 target *config
433 output *config
434 name string
435 overwrite bool
436 }{
437 {
438 name: "Should overwrite values in target for non-nil values in source",
439 overwrite: true,
440 changes: &config{
441 Bar: "from changes",
442 Params: ¶ms{
443 Final: &final{
444 Msg1: "from changes",
445 Msg2: "from changes",
446 },
447 },
448 },
449 target: &config{
450 Foo: "from target",
451 Params: ¶ms{
452 Name: "from target",
453 Multi: &multiPtr{
454 Text: "from target",
455 Number: 5,
456 },
457 Final: &final{
458 Msg1: "from target",
459 Msg2: "",
460 },
461 },
462 },
463 output: &config{
464 Foo: "from target",
465 Bar: "from changes",
466 Params: ¶ms{
467 Name: "from target",
468 Multi: &multiPtr{
469 Text: "from target",
470 Number: 5,
471 },
472 Final: &final{
473 Msg1: "from changes",
474 Msg2: "from changes",
475 },
476 },
477 },
478 },
479 {
480 name: "Should not overwrite values in target for non-nil values in source",
481 overwrite: false,
482 changes: &config{
483 Bar: "from changes",
484 Params: ¶ms{
485 Final: &final{
486 Msg1: "from changes",
487 Msg2: "from changes",
488 },
489 },
490 },
491 target: &config{
492 Foo: "from target",
493 Params: ¶ms{
494 Name: "from target",
495 Multi: &multiPtr{
496 Text: "from target",
497 Number: 5,
498 },
499 Final: &final{
500 Msg1: "from target",
501 Msg2: "",
502 },
503 },
504 },
505 output: &config{
506 Foo: "from target",
507 Bar: "from changes",
508 Params: ¶ms{
509 Name: "from target",
510 Multi: &multiPtr{
511 Text: "from target",
512 Number: 5,
513 },
514 Final: &final{
515 Msg1: "from target",
516 Msg2: "from changes",
517 },
518 },
519 },
520 },
521 }
522
523 for _, tc := range cases {
524 t.Run(tc.name, func(t *testing.T) {
525 var err error
526 if tc.overwrite {
527 err = mergo.Merge(tc.target, *tc.changes, mergo.WithOverride)
528 } else {
529 err = mergo.Merge(tc.target, *tc.changes)
530 }
531 if err != nil {
532 t.Error(err)
533 }
534 if !reflect.DeepEqual(tc.target, tc.output) {
535 t.Errorf("Test failed:\ngot :\n%+v\n\nwant :\n%+v\n\n", tc.target.Params, tc.output.Params)
536 }
537 })
538 }
539 }
540 func TestMaps(t *testing.T) {
541 m := map[string]simpleTest{
542 "a": {},
543 "b": {42},
544 "c": {13},
545 "d": {61},
546 }
547 n := map[string]simpleTest{
548 "a": {16},
549 "b": {},
550 "c": {12},
551 "e": {14},
552 }
553 expect := map[string]simpleTest{
554 "a": {0},
555 "b": {42},
556 "c": {13},
557 "d": {61},
558 "e": {14},
559 }
560
561 if err := mergo.Merge(&m, n); err != nil {
562 t.Errorf(err.Error())
563 }
564
565 if !reflect.DeepEqual(m, expect) {
566 t.Errorf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect)
567 }
568 if m["a"].Value != 0 {
569 t.Errorf(`n merged in m because I solved non-addressable map values TODO: m["a"].Value(%d) != n["a"].Value(%d)`, m["a"].Value, n["a"].Value)
570 }
571 if m["b"].Value != 42 {
572 t.Errorf(`n wrongly merged in m: m["b"].Value(%d) != n["b"].Value(%d)`, m["b"].Value, n["b"].Value)
573 }
574 if m["c"].Value != 13 {
575 t.Errorf(`n overwritten in m: m["c"].Value(%d) != n["c"].Value(%d)`, m["c"].Value, n["c"].Value)
576 }
577 }
578
579 func TestMapsWithNilPointer(t *testing.T) {
580 m := map[string]*simpleTest{
581 "a": nil,
582 "b": nil,
583 }
584 n := map[string]*simpleTest{
585 "b": nil,
586 "c": nil,
587 }
588 expect := map[string]*simpleTest{
589 "a": nil,
590 "b": nil,
591 "c": nil,
592 }
593
594 if err := mergo.Merge(&m, n, mergo.WithOverride); err != nil {
595 t.Errorf(err.Error())
596 }
597
598 if !reflect.DeepEqual(m, expect) {
599 t.Errorf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect)
600 }
601 }
602
603 func TestYAMLMaps(t *testing.T) {
604 thing := loadYAML("testdata/thing.yml")
605 license := loadYAML("testdata/license.yml")
606 ft := thing["fields"].(map[string]interface{})
607 fl := license["fields"].(map[string]interface{})
608
609 expectedLength := len(ft) + len(fl) - 1
610 if err := mergo.Merge(&license, thing); err != nil {
611 t.Error(err.Error())
612 }
613 currentLength := len(license["fields"].(map[string]interface{}))
614 if currentLength != expectedLength {
615 t.Errorf(`thing not merged in license properly, license must have %d elements instead of %d`, expectedLength, currentLength)
616 }
617 fields := license["fields"].(map[string]interface{})
618 if _, ok := fields["id"]; !ok {
619 t.Errorf(`thing not merged in license properly, license must have a new id field from thing`)
620 }
621 }
622
623 func TestTwoPointerValues(t *testing.T) {
624 a := &simpleTest{}
625 b := &simpleTest{42}
626 if err := mergo.Merge(a, b); err != nil {
627 t.Errorf(`Boom. You crossed the streams: %s`, err)
628 }
629 }
630
631 func TestMap(t *testing.T) {
632 a := complexTest{}
633 a.ID = "athing"
634 c := moreComplextText{a, simpleTest{}, simpleTest{}}
635 b := map[string]interface{}{
636 "ct": map[string]interface{}{
637 "st": map[string]interface{}{
638 "value": 42,
639 },
640 "sz": 1,
641 "id": "bthing",
642 },
643 "st": &simpleTest{144},
644 "zt": simpleTest{299},
645 "nt": simpleTest{3},
646 }
647 if err := mergo.Map(&c, b); err != nil {
648 t.FailNow()
649 }
650 m := b["ct"].(map[string]interface{})
651 n := m["st"].(map[string]interface{})
652 o := b["st"].(*simpleTest)
653 p := b["nt"].(simpleTest)
654 if c.Ct.St.Value != 42 {
655 t.Errorf("b not merged in properly: c.Ct.St.Value(%d) != b.Ct.St.Value(%d)", c.Ct.St.Value, n["value"])
656 }
657 if c.St.Value != 144 {
658 t.Errorf("b not merged in properly: c.St.Value(%d) != b.St.Value(%d)", c.St.Value, o.Value)
659 }
660 if c.Nt.Value != 3 {
661 t.Errorf("b not merged in properly: c.Nt.Value(%d) != b.Nt.Value(%d)", c.St.Value, p.Value)
662 }
663 if c.Ct.sz == 1 {
664 t.Errorf("a's private field sz not preserved from merge: c.Ct.sz(%d) == b.Ct.sz(%d)", c.Ct.sz, m["sz"])
665 }
666 if c.Ct.ID == m["id"] {
667 t.Errorf("a's field ID merged unexpectedly: c.Ct.ID(%s) == b.Ct.ID(%s)", c.Ct.ID, m["id"])
668 }
669 }
670
671 func TestSimpleMap(t *testing.T) {
672 a := simpleTest{}
673 b := map[string]interface{}{
674 "value": 42,
675 }
676 if err := mergo.Map(&a, b); err != nil {
677 t.FailNow()
678 }
679 if a.Value != 42 {
680 t.Errorf("b not merged in properly: a.Value(%d) != b.Value(%v)", a.Value, b["value"])
681 }
682 }
683
684 func TestIfcMap(t *testing.T) {
685 a := ifcTest{}
686 b := ifcTest{42}
687 if err := mergo.Map(&a, b); err != nil {
688 t.FailNow()
689 }
690 if a.I != 42 {
691 t.Errorf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I)
692 }
693 if !reflect.DeepEqual(a, b) {
694 t.FailNow()
695 }
696 }
697
698 func TestIfcMapNoOverwrite(t *testing.T) {
699 a := ifcTest{13}
700 b := ifcTest{42}
701 if err := mergo.Map(&a, b); err != nil {
702 t.FailNow()
703 }
704 if a.I != 13 {
705 t.Errorf("a not left alone: a.I(%d) == b.I(%d)", a.I, b.I)
706 }
707 }
708
709 func TestIfcMapWithOverwrite(t *testing.T) {
710 a := ifcTest{13}
711 b := ifcTest{42}
712 if err := mergo.MapWithOverwrite(&a, b); err != nil {
713 t.FailNow()
714 }
715 if a.I != 42 {
716 t.Errorf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I)
717 }
718 if !reflect.DeepEqual(a, b) {
719 t.FailNow()
720 }
721 }
722
723 type pointerMapTest struct {
724 B *simpleTest
725 A int
726 hidden int
727 }
728
729 func TestBackAndForth(t *testing.T) {
730 pt := pointerMapTest{&simpleTest{66}, 42, 1}
731 m := make(map[string]interface{})
732 if err := mergo.Map(&m, pt); err != nil {
733 t.FailNow()
734 }
735 var (
736 v interface{}
737 ok bool
738 )
739 if v, ok = m["a"]; v.(int) != pt.A || !ok {
740 t.Errorf("pt not merged in properly: m[`a`](%d) != pt.A(%d)", v, pt.A)
741 }
742 if v, ok = m["b"]; !ok {
743 t.Errorf("pt not merged in properly: B is missing in m")
744 }
745 var st *simpleTest
746 if st = v.(*simpleTest); st.Value != 66 {
747 t.Errorf("something went wrong while mapping pt on m, B wasn't copied")
748 }
749 bpt := pointerMapTest{}
750 if err := mergo.Map(&bpt, m); err != nil {
751 t.Error(err)
752 }
753 if bpt.A != pt.A {
754 t.Errorf("pt not merged in properly: bpt.A(%d) != pt.A(%d)", bpt.A, pt.A)
755 }
756 if bpt.hidden == pt.hidden {
757 t.Errorf("pt unexpectedly merged: bpt.hidden(%d) == pt.hidden(%d)", bpt.hidden, pt.hidden)
758 }
759 if bpt.B.Value != pt.B.Value {
760 t.Errorf("pt not merged in properly: bpt.B.Value(%d) != pt.B.Value(%d)", bpt.B.Value, pt.B.Value)
761 }
762 }
763
764 func TestEmbeddedPointerUnpacking(t *testing.T) {
765 tests := []struct{ input pointerMapTest }{
766 {pointerMapTest{nil, 42, 1}},
767 {pointerMapTest{&simpleTest{66}, 42, 1}},
768 }
769 newValue := 77
770 m := map[string]interface{}{
771 "b": map[string]interface{}{
772 "value": newValue,
773 },
774 }
775 for _, test := range tests {
776 pt := test.input
777 if err := mergo.MapWithOverwrite(&pt, m); err != nil {
778 t.FailNow()
779 }
780 if pt.B.Value != newValue {
781 t.Errorf("pt not mapped properly: pt.A.Value(%d) != m[`b`][`value`](%d)", pt.B.Value, newValue)
782 }
783
784 }
785 }
786
787 type structWithTimePointer struct {
788 Birth *time.Time
789 }
790
791 func TestTime(t *testing.T) {
792 now := time.Now()
793 dataStruct := structWithTimePointer{
794 Birth: &now,
795 }
796 dataMap := map[string]interface{}{
797 "Birth": &now,
798 }
799 b := structWithTimePointer{}
800 if err := mergo.Merge(&b, dataStruct); err != nil {
801 t.FailNow()
802 }
803 if b.Birth.IsZero() {
804 t.Errorf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth)
805 }
806 if b.Birth != dataStruct.Birth {
807 t.Errorf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth)
808 }
809 b = structWithTimePointer{}
810 if err := mergo.Map(&b, dataMap); err != nil {
811 t.FailNow()
812 }
813 if b.Birth.IsZero() {
814 t.Errorf("time.Time not merged in properly: b.Birth(%v) != dataMap['Birth'](%v)", b.Birth, dataMap["Birth"])
815 }
816 }
817
818 type simpleNested struct {
819 A int
820 }
821
822 type structWithNestedPtrValueMap struct {
823 NestedPtrValue map[string]*simpleNested
824 }
825
826 func TestNestedPtrValueInMap(t *testing.T) {
827 src := &structWithNestedPtrValueMap{
828 NestedPtrValue: map[string]*simpleNested{
829 "x": {
830 A: 1,
831 },
832 },
833 }
834 dst := &structWithNestedPtrValueMap{
835 NestedPtrValue: map[string]*simpleNested{
836 "x": {},
837 },
838 }
839 if err := mergo.Map(dst, src); err != nil {
840 t.FailNow()
841 }
842 if dst.NestedPtrValue["x"].A == 0 {
843 t.Errorf("Nested Ptr value not merged in properly: dst.NestedPtrValue[\"x\"].A(%v) != src.NestedPtrValue[\"x\"].A(%v)", dst.NestedPtrValue["x"].A, src.NestedPtrValue["x"].A)
844 }
845 }
846
847 func loadYAML(path string) (m map[string]interface{}) {
848 m = make(map[string]interface{})
849 raw, _ := ioutil.ReadFile(path)
850 _ = yaml.Unmarshal(raw, &m)
851 return
852 }
853
854 type structWithMap struct {
855 m map[string]structWithUnexportedProperty
856 }
857
858 type structWithUnexportedProperty struct {
859 s string
860 }
861
862 func TestUnexportedProperty(t *testing.T) {
863 a := structWithMap{map[string]structWithUnexportedProperty{
864 "key": {"hello"},
865 }}
866 b := structWithMap{map[string]structWithUnexportedProperty{
867 "key": {"hi"},
868 }}
869 defer func() {
870 if r := recover(); r != nil {
871 t.Errorf("Should not have panicked")
872 }
873 }()
874 mergo.Merge(&a, b)
875 }
876
877 type structWithBoolPointer struct {
878 C *bool
879 }
880
881 func TestBooleanPointer(t *testing.T) {
882 bt, bf := true, false
883 src := structWithBoolPointer{
884 &bt,
885 }
886 dst := structWithBoolPointer{
887 &bf,
888 }
889 if err := mergo.Merge(&dst, src); err != nil {
890 t.FailNow()
891 }
892 if dst.C == src.C {
893 t.Errorf("dst.C should be a different pointer than src.C")
894 }
895 if *dst.C != *src.C {
896 t.Errorf("dst.C should be true")
897 }
898 }
899
900 func TestMergeMapWithInnerSliceOfDifferentType(t *testing.T) {
901 testCases := []struct {
902 name string
903 err string
904 options []func(*mergo.Config)
905 }{
906 {
907 "With override and append slice",
908 "cannot append two slices with different type",
909 []func(*mergo.Config){mergo.WithOverride, mergo.WithAppendSlice},
910 },
911 {
912 "With override and type check",
913 "cannot override two slices with different type",
914 []func(*mergo.Config){mergo.WithOverride, mergo.WithTypeCheck},
915 },
916 }
917 for _, tc := range testCases {
918 t.Run(tc.name, func(t *testing.T) {
919 src := map[string]interface{}{
920 "foo": []string{"a", "b"},
921 }
922 dst := map[string]interface{}{
923 "foo": []int{1, 2},
924 }
925
926 if err := mergo.Merge(&src, &dst, tc.options...); err == nil || !strings.Contains(err.Error(), tc.err) {
927 t.Errorf("expected %q, got %q", tc.err, err)
928 }
929 })
930 }
931 }
932
933 func TestMergeDifferentSlicesIsNotSupported(t *testing.T) {
934 src := []string{"a", "b"}
935 dst := []int{1, 2}
936
937 if err := mergo.Merge(&src, &dst, mergo.WithOverride, mergo.WithAppendSlice); err != mergo.ErrDifferentArgumentsTypes {
938 t.Errorf("expected %q, got %q", mergo.ErrNotSupported, err)
939 }
940 }
941
View as plain text