1
16
17
18
19
20
21 package runtime_test
22
23 import (
24 encodingjson "encoding/json"
25 "fmt"
26 "reflect"
27 "regexp"
28 "strconv"
29 "strings"
30 "testing"
31 "time"
32
33 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
34 "k8s.io/apimachinery/pkg/conversion"
35 "k8s.io/apimachinery/pkg/runtime"
36 "k8s.io/apimachinery/pkg/util/json"
37
38 "github.com/google/go-cmp/cmp"
39 fuzz "github.com/google/gofuzz"
40 "github.com/stretchr/testify/assert"
41 "github.com/stretchr/testify/require"
42 )
43
44 var simpleEquality = conversion.EqualitiesOrDie(
45 func(a, b time.Time) bool {
46 return a.UTC() == b.UTC()
47 },
48 )
49
50
51 type A struct {
52 A int `json:"aa,omitempty"`
53 B string `json:"ab,omitempty"`
54 C bool `json:"ac,omitempty"`
55 }
56
57 type B struct {
58 A A `json:"ba"`
59 B string `json:"bb"`
60 C map[string]string `json:"bc"`
61 D []string `json:"bd"`
62 }
63
64 type C struct {
65 A []A `json:"ca"`
66 B `json:",inline"`
67 C string `json:"cc"`
68 D *int64 `json:"cd"`
69 E map[string]int `json:"ce"`
70 F []bool `json:"cf"`
71 G []int `json:"cg"`
72 H float32 `json:"ch"`
73 I []interface{} `json:"ci"`
74 }
75
76 type D struct {
77 A []interface{} `json:"da"`
78 }
79
80 type E struct {
81 A interface{} `json:"ea"`
82 }
83
84 type F struct {
85 A string `json:"fa"`
86 B map[string]string `json:"fb"`
87 C []A `json:"fc"`
88 D int `json:"fd"`
89 E float32 `json:"fe"`
90 F []string `json:"ff"`
91 G []int `json:"fg"`
92 H []bool `json:"fh"`
93 I []float32 `json:"fi"`
94 J []byte `json:"fj"`
95 }
96
97 type G struct {
98 CustomValue1 CustomValue `json:"customValue1"`
99 CustomValue2 *CustomValue `json:"customValue2"`
100 CustomPointer1 CustomPointer `json:"customPointer1"`
101 CustomPointer2 *CustomPointer `json:"customPointer2"`
102 }
103
104 type H struct {
105 A A `json:"ha"`
106 C `json:",inline"`
107 }
108
109 type I struct {
110 A A `json:"ia"`
111 H `json:",inline"`
112
113 UL1 UnknownLevel1 `json:"ul1"`
114 }
115
116 type UnknownLevel1 struct {
117 A int64 `json:"a"`
118 InlinedAA `json:",inline"`
119 InlinedAAA `json:",inline"`
120 }
121 type InlinedAA struct {
122 AA int64 `json:"aa"`
123 }
124 type InlinedAAA struct {
125 AAA int64 `json:"aaa"`
126 Child UnknownLevel2 `json:"child"`
127 }
128
129 type UnknownLevel2 struct {
130 B int64 `json:"b"`
131 InlinedBB `json:",inline"`
132 InlinedBBB `json:",inline"`
133 }
134 type InlinedBB struct {
135 BB int64 `json:"bb"`
136 }
137 type InlinedBBB struct {
138 BBB int64 `json:"bbb"`
139 Child UnknownLevel3 `json:"child"`
140 }
141
142 type UnknownLevel3 struct {
143 C int64 `json:"c"`
144 InlinedCC `json:",inline"`
145 InlinedCCC `json:",inline"`
146 }
147 type InlinedCC struct {
148 CC int64 `json:"cc"`
149 }
150 type InlinedCCC struct {
151 CCC int64 `json:"ccc"`
152 }
153
154 type CustomValue struct {
155 data []byte
156 }
157
158
159 func (c CustomValue) MarshalJSON() ([]byte, error) {
160 return c.data, nil
161 }
162
163 type CustomPointer struct {
164 data []byte
165 }
166
167
168 func (c *CustomPointer) MarshalJSON() ([]byte, error) {
169 return c.data, nil
170 }
171
172 func doRoundTrip(t *testing.T, item interface{}) {
173 data, err := json.Marshal(item)
174 if err != nil {
175 t.Errorf("Error when marshaling object: %v", err)
176 return
177 }
178
179 unstr := make(map[string]interface{})
180 err = json.Unmarshal(data, &unstr)
181 if err != nil {
182 t.Errorf("Error when unmarshaling to unstructured: %v", err)
183 return
184 }
185
186 data, err = json.Marshal(unstr)
187 if err != nil {
188 t.Errorf("Error when marshaling unstructured: %v", err)
189 return
190 }
191 unmarshalledObj := reflect.New(reflect.TypeOf(item).Elem()).Interface()
192 err = json.Unmarshal(data, unmarshalledObj)
193 if err != nil {
194 t.Errorf("Error when unmarshaling to object: %v", err)
195 return
196 }
197 if !reflect.DeepEqual(item, unmarshalledObj) {
198 t.Errorf("Object changed during JSON operations, diff: %v", cmp.Diff(item, unmarshalledObj))
199 return
200 }
201
202
203 newUnstr, err := runtime.DefaultUnstructuredConverter.ToUnstructured(item)
204 if err != nil {
205 t.Errorf("ToUnstructured failed: %v", err)
206 return
207 }
208
209 newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface()
210 err = runtime.NewTestUnstructuredConverter(simpleEquality).FromUnstructured(newUnstr, newObj)
211 if err != nil {
212 t.Errorf("FromUnstructured failed: %v", err)
213 return
214 }
215
216 if !reflect.DeepEqual(item, newObj) {
217 t.Errorf("Object changed, diff: %v", cmp.Diff(item, newObj))
218 }
219 }
220
221 func TestRoundTrip(t *testing.T) {
222 intVal := int64(42)
223 testCases := []struct {
224 obj interface{}
225 }{
226 {
227 obj: &unstructured.UnstructuredList{
228 Object: map[string]interface{}{
229 "kind": "List",
230 },
231
232
233
234 Items: []unstructured.Unstructured{},
235 },
236 },
237 {
238 obj: &unstructured.UnstructuredList{
239 Object: map[string]interface{}{
240 "kind": "List",
241 },
242 Items: []unstructured.Unstructured{
243 {
244 Object: map[string]interface{}{
245 "kind": "Pod",
246 },
247 },
248 },
249 },
250 },
251 {
252 obj: &unstructured.Unstructured{
253 Object: map[string]interface{}{
254 "kind": "Pod",
255 },
256 },
257 },
258 {
259 obj: &unstructured.Unstructured{
260 Object: map[string]interface{}{
261 "apiVersion": "v1",
262 "kind": "Foo",
263 "metadata": map[string]interface{}{
264 "name": "foo1",
265 },
266 },
267 },
268 },
269 {
270
271 obj: &C{
272 C: "ccc",
273 },
274 },
275 {
276
277 obj: &C{
278 A: []A{},
279 C: "ccc",
280 E: map[string]int{},
281 I: []interface{}{},
282 },
283 },
284 {
285 obj: &C{
286 A: []A{
287 {
288 A: 1,
289 B: "11",
290 C: true,
291 },
292 {
293 A: 2,
294 B: "22",
295 C: false,
296 },
297 },
298 B: B{
299 A: A{
300 A: 3,
301 B: "33",
302 },
303 B: "bbb",
304 C: map[string]string{
305 "k1": "v1",
306 "k2": "v2",
307 },
308 D: []string{"s1", "s2"},
309 },
310 C: "ccc",
311 D: &intVal,
312 E: map[string]int{
313 "k1": 1,
314 "k2": 2,
315 },
316 F: []bool{true, false, false},
317 G: []int{1, 2, 5},
318 H: 3.3,
319 I: []interface{}{nil, nil, nil},
320 },
321 },
322 {
323
324 obj: &D{
325 A: []interface{}{[]interface{}{}, []interface{}{}},
326 },
327 },
328 {
329
330 obj: &D{
331 A: []interface{}{float64(3.5), int64(4), "3.0", nil},
332 },
333 },
334 }
335
336 for i := range testCases {
337 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
338 doRoundTrip(t, testCases[i].obj)
339 })
340 }
341 }
342
343
344
345
346 func TestUnknownFields(t *testing.T) {
347
348
349 var simplesData = `{
350 "ca": [
351 {
352 "aa": 1,
353 "ab": "ab",
354 "ac": true,
355 "unknown1": 24
356 }
357 ],
358 "cc": "ccstring",
359 "unknown2": "foo"
360 }`
361
362 var simplesErrs = []string{
363 `unknown field "ca[0].unknown1"`,
364 `unknown field "unknown2"`,
365 }
366
367
368
369
370
371
372
373
374
375
376
377
378
379 var sameNameDiffLevelData = `
380 {
381 "cc": "foo",
382 "aaa": 1,
383 "ul1": {
384 "aa": 1,
385 "aaa": 1,
386 "cc": 1
387
388 }
389 }`
390 var sameNameDiffLevelErrs = []string{
391 `unknown field "aaa"`,
392 `unknown field "ul1.cc"`,
393 }
394
395
396
397
398 var inlinedInlinedData = `{
399 "bb": "foo",
400 "bc": {
401 "foo": "bar"
402 },
403 "bd": ["d1", "d2"],
404 "aa": 1
405 }`
406
407 var inlinedInlinedErrs = []string{
408 `unknown field "aa"`,
409 }
410
411
412 var combinedData = `
413 {
414 "ia": {
415 "aa": 1,
416 "ab": "ab",
417 "unknownI": "foo"
418 },
419 "ha": {
420 "aa": 2,
421 "ab": "ab2",
422 "unknownH": "foo"
423 },
424 "ca":[
425 {
426 "aa":1,
427 "ab":"11",
428 "ac":true
429 },
430 {
431 "aa":2,
432 "ab":"22",
433 "unknown1": "foo"
434 },
435 {
436 "aa":3,
437 "ab":"33",
438 "unknown2": "foo"
439 }
440 ],
441 "ba":{
442 "aa":3,
443 "ab":"33",
444 "ac": true,
445 "unknown3": 26,
446 "unknown4": "foo"
447 },
448 "unknown5": "foo",
449 "bb":"bbb",
450 "bc":{
451 "k1":"v1",
452 "k2":"v2"
453 },
454 "bd":[
455 "s1",
456 "s2"
457 ],
458 "cc":"ccc",
459 "cd":42,
460 "ce":{
461 "k1":1,
462 "k2":2
463 },
464 "cf":[
465 true,
466 false,
467 false
468 ],
469 "cg":
470 [
471 1,
472 2,
473 5
474 ],
475 "ch":3.3,
476 "ci":[
477 null,
478 null,
479 null
480 ],
481 "ul1": {
482 "a": 1,
483 "aa": 1,
484 "aaa": 1,
485 "b": 1,
486 "bb": 1,
487 "bbb": 1,
488 "c": 1,
489 "cc": 1,
490 "ccc": 1,
491 "child": {
492 "a": 1,
493 "aa": 1,
494 "aaa": 1,
495 "b": 1,
496 "bb": 1,
497 "bbb": 1,
498 "c": 1,
499 "cc": 1,
500 "ccc": 1,
501 "child": {
502 "a": 1,
503 "aa": 1,
504 "aaa": 1,
505 "b": 1,
506 "bb": 1,
507 "bbb": 1,
508 "c": 1,
509 "cc": 1,
510 "ccc": 1
511 }
512 }
513 }
514 }`
515
516 var combinedErrs = []string{
517 `unknown field "ca[1].unknown1"`,
518 `unknown field "ca[2].unknown2"`,
519 `unknown field "ba.unknown3"`,
520 `unknown field "ba.unknown4"`,
521 `unknown field "unknown5"`,
522 `unknown field "ha.unknownH"`,
523 `unknown field "ia.unknownI"`,
524
525 `unknown field "ul1.b"`,
526 `unknown field "ul1.bb"`,
527 `unknown field "ul1.bbb"`,
528 `unknown field "ul1.c"`,
529 `unknown field "ul1.cc"`,
530 `unknown field "ul1.ccc"`,
531
532 `unknown field "ul1.child.a"`,
533 `unknown field "ul1.child.aa"`,
534 `unknown field "ul1.child.aaa"`,
535 `unknown field "ul1.child.c"`,
536 `unknown field "ul1.child.cc"`,
537 `unknown field "ul1.child.ccc"`,
538
539 `unknown field "ul1.child.child.a"`,
540 `unknown field "ul1.child.child.aa"`,
541 `unknown field "ul1.child.child.aaa"`,
542 `unknown field "ul1.child.child.b"`,
543 `unknown field "ul1.child.child.bb"`,
544 `unknown field "ul1.child.child.bbb"`,
545 }
546
547 testCases := []struct {
548 jsonData string
549 obj interface{}
550 returnUnknownFields bool
551 expectedErrs []string
552 }{
553 {
554 jsonData: simplesData,
555 obj: &C{},
556 returnUnknownFields: true,
557 expectedErrs: simplesErrs,
558 },
559 {
560 jsonData: simplesData,
561 obj: &C{},
562 returnUnknownFields: false,
563 },
564 {
565 jsonData: sameNameDiffLevelData,
566 obj: &I{},
567 returnUnknownFields: true,
568 expectedErrs: sameNameDiffLevelErrs,
569 },
570 {
571 jsonData: sameNameDiffLevelData,
572 obj: &I{},
573 returnUnknownFields: false,
574 },
575 {
576 jsonData: inlinedInlinedData,
577 obj: &I{},
578 returnUnknownFields: true,
579 expectedErrs: inlinedInlinedErrs,
580 },
581 {
582 jsonData: inlinedInlinedData,
583 obj: &I{},
584 returnUnknownFields: false,
585 },
586 {
587 jsonData: combinedData,
588 obj: &I{},
589 returnUnknownFields: true,
590 expectedErrs: combinedErrs,
591 },
592 {
593 jsonData: combinedData,
594 obj: &I{},
595 returnUnknownFields: false,
596 },
597 }
598
599 for i, tc := range testCases {
600 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
601 unstr := make(map[string]interface{})
602 err := json.Unmarshal([]byte(tc.jsonData), &unstr)
603 if err != nil {
604 t.Errorf("Error when unmarshaling to unstructured: %v", err)
605 return
606 }
607 err = runtime.NewTestUnstructuredConverterWithValidation(simpleEquality).FromUnstructuredWithValidation(unstr, tc.obj, tc.returnUnknownFields)
608 if len(tc.expectedErrs) == 0 && err != nil {
609 t.Errorf("unexpected err: %v", err)
610 }
611 var errString string
612 if err != nil {
613 errString = err.Error()
614 }
615 missedErrs := []string{}
616 failed := false
617 for _, expected := range tc.expectedErrs {
618 if !strings.Contains(errString, expected) {
619 failed = true
620 missedErrs = append(missedErrs, expected)
621 } else {
622 errString = strings.Replace(errString, expected, "", 1)
623 }
624 }
625 if failed {
626 for _, e := range missedErrs {
627 t.Errorf("missing err: %v\n", e)
628 }
629 }
630 leftoverErrors := strings.TrimSpace(strings.TrimPrefix(strings.ReplaceAll(errString, ",", ""), "strict decoding error:"))
631 if leftoverErrors != "" {
632 t.Errorf("found unexpected errors: %s", leftoverErrors)
633 }
634 })
635 }
636 }
637
638
639
640
641 func BenchmarkFromUnstructuredWithValidation(b *testing.B) {
642 re := regexp.MustCompile("^I$")
643 f := fuzz.NewWithSeed(1).NilChance(0.1).SkipFieldsWithPattern(re)
644 iObj := &I{}
645 f.Fuzz(&iObj)
646
647 unstr, err := runtime.DefaultUnstructuredConverter.ToUnstructured(iObj)
648 if err != nil {
649 b.Fatalf("ToUnstructured failed: %v", err)
650 return
651 }
652 for _, shouldReturn := range []bool{false, true} {
653 b.Run(fmt.Sprintf("shouldReturn=%t", shouldReturn), func(b *testing.B) {
654 newObj := reflect.New(reflect.TypeOf(iObj).Elem()).Interface()
655 b.ReportAllocs()
656 for i := 0; i < b.N; i++ {
657 if err = runtime.NewTestUnstructuredConverterWithValidation(simpleEquality).FromUnstructuredWithValidation(unstr, newObj, shouldReturn); err != nil {
658 b.Fatalf("FromUnstructured failed: %v", err)
659 return
660 }
661 }
662 })
663 }
664 }
665
666
667
668
669
670 func doUnrecognized(t *testing.T, jsonData string, item interface{}, expectedErr error) {
671 unmarshalledObj := reflect.New(reflect.TypeOf(item).Elem()).Interface()
672 err := json.Unmarshal([]byte(jsonData), unmarshalledObj)
673 if (err != nil) != (expectedErr != nil) {
674 t.Errorf("Unexpected error when unmarshaling to object: %v, expected: %v", err, expectedErr)
675 return
676 }
677
678 unstr := make(map[string]interface{})
679 err = json.Unmarshal([]byte(jsonData), &unstr)
680 if err != nil {
681 t.Errorf("Error when unmarshaling to unstructured: %v", err)
682 return
683 }
684 newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface()
685 err = runtime.NewTestUnstructuredConverter(simpleEquality).FromUnstructured(unstr, newObj)
686 if (err != nil) != (expectedErr != nil) {
687 t.Errorf("Unexpected error in FromUnstructured: %v, expected: %v", err, expectedErr)
688 }
689
690 if expectedErr == nil && !reflect.DeepEqual(unmarshalledObj, newObj) {
691 t.Errorf("Object changed, diff: %v", cmp.Diff(unmarshalledObj, newObj))
692 }
693 }
694
695 func TestUnrecognized(t *testing.T) {
696 testCases := []struct {
697 data string
698 obj interface{}
699 err error
700 }{
701 {
702 data: "{\"da\":[3.5,4,\"3.0\",null]}",
703 obj: &D{},
704 },
705 {
706 data: "{\"ea\":[3.5,4,\"3.0\",null]}",
707 obj: &E{},
708 },
709 {
710 data: "{\"ea\":[null,null,null]}",
711 obj: &E{},
712 },
713 {
714 data: "{\"ea\":[[],[null]]}",
715 obj: &E{},
716 },
717 {
718 data: "{\"ea\":{\"a\":[],\"b\":null}}",
719 obj: &E{},
720 },
721 {
722 data: "{\"fa\":\"fa\",\"fb\":{\"a\":\"a\"}}",
723 obj: &F{},
724 },
725 {
726 data: "{\"fa\":\"fa\",\"fb\":{\"a\":null}}",
727 obj: &F{},
728 },
729 {
730 data: "{\"fc\":[null]}",
731 obj: &F{},
732 },
733 {
734 data: "{\"fc\":[{\"aa\":123,\"ab\":\"bbb\"}]}",
735 obj: &F{},
736 },
737 {
738
739 data: "{\"fx\":[{\"aa\":123,\"ab\":\"bbb\"}],\"fz\":123}",
740 obj: &F{},
741 },
742 {
743 data: "{\"fc\":[{\"aa\":\"aaa\",\"ab\":\"bbb\"}]}",
744 obj: &F{},
745 err: fmt.Errorf("json: cannot unmarshal string into Go value of type int"),
746 },
747 {
748 data: "{\"fd\":123,\"fe\":3.5}",
749 obj: &F{},
750 },
751 {
752 data: "{\"ff\":[\"abc\"],\"fg\":[123],\"fh\":[true,false]}",
753 obj: &F{},
754 },
755 {
756 data: "{\"fj\":\"\"}",
757 obj: &F{},
758 },
759 {
760
761 data: "{\"fa\":123}",
762 obj: &F{},
763 err: fmt.Errorf("json: cannot unmarshal number into Go value of type string"),
764 },
765 {
766
767 data: "{\"fa\":13.5}",
768 obj: &F{},
769 err: fmt.Errorf("json: cannot unmarshal number into Go value of type string"),
770 },
771 {
772
773 data: "{\"fa\":true}",
774 obj: &F{},
775 err: fmt.Errorf("json: cannot unmarshal bool into Go value of type string"),
776 },
777 {
778
779 data: "{\"ff\":123}",
780 obj: &F{},
781 err: fmt.Errorf("json: cannot unmarshal number into Go value of type []string"),
782 },
783 {
784
785 data: "{\"ff\":3.5}",
786 obj: &F{},
787 err: fmt.Errorf("json: cannot unmarshal number into Go value of type []string"),
788 },
789 {
790
791 data: "{\"ff\":[123,345]}",
792 obj: &F{},
793 err: fmt.Errorf("json: cannot unmarshal number into Go value of type string"),
794 },
795 {
796
797 data: "{\"fg\":123}",
798 obj: &F{},
799 err: fmt.Errorf("json: cannot unmarshal number into Go value of type []int"),
800 },
801 {
802
803 data: "{\"fg\":\"abc\"}",
804 obj: &F{},
805 err: fmt.Errorf("json: cannot unmarshal string into Go value of type []int"),
806 },
807 {
808
809 data: "{\"fg\":[\"abc\"]}",
810 obj: &F{},
811 err: fmt.Errorf("json: cannot unmarshal string into Go value of type int"),
812 },
813 {
814
815 data: "{\"fg\":[3.5]}",
816 obj: &F{},
817 err: fmt.Errorf("json: cannot unmarshal number 3.5 into Go value of type int"),
818 },
819 {
820
821 data: "{\"fg\":[true,false]}",
822 obj: &F{},
823 err: fmt.Errorf("json: cannot unmarshal number 3.5 into Go value of type int"),
824 },
825 {
826
827 data: "{\"fh\":123}",
828 obj: &F{},
829 err: fmt.Errorf("json: cannot unmarshal number into Go value of type []bool"),
830 },
831 {
832
833 data: "{\"fh\":\"abc\"}",
834 obj: &F{},
835 err: fmt.Errorf("json: cannot unmarshal string into Go value of type []bool"),
836 },
837 {
838
839 data: "{\"fh\":[\"abc\"]}",
840 obj: &F{},
841 err: fmt.Errorf("json: cannot unmarshal string into Go value of type bool"),
842 },
843 {
844
845 data: "{\"fh\":[3.5]}",
846 obj: &F{},
847 err: fmt.Errorf("json: cannot unmarshal number into Go value of type bool"),
848 },
849 {
850
851 data: "{\"fh\":[123]}",
852 obj: &F{},
853 err: fmt.Errorf("json: cannot unmarshal number into Go value of type bool"),
854 },
855 {
856
857 data: "{\"fi\":123}",
858 obj: &F{},
859 err: fmt.Errorf("json: cannot unmarshal number into Go value of type []float32"),
860 },
861 {
862
863 data: "{\"fi\":\"abc\"}",
864 obj: &F{},
865 err: fmt.Errorf("json: cannot unmarshal string into Go value of type []float32"),
866 },
867 {
868
869 data: "{\"fi\":[\"abc\"]}",
870 obj: &F{},
871 err: fmt.Errorf("json: cannot unmarshal string into Go value of type float32"),
872 },
873 {
874
875 data: "{\"fi\":[true]}",
876 obj: &F{},
877 err: fmt.Errorf("json: cannot unmarshal bool into Go value of type float32"),
878 },
879 }
880
881 for _, tc := range testCases {
882 t.Run(tc.data, func(t *testing.T) {
883 doUnrecognized(t, tc.data, tc.obj, tc.err)
884 })
885 }
886 }
887
888 func TestDeepCopyJSON(t *testing.T) {
889 src := map[string]interface{}{
890 "a": nil,
891 "b": int64(123),
892 "c": map[string]interface{}{
893 "a": "b",
894 },
895 "d": []interface{}{
896 int64(1), int64(2),
897 },
898 "e": "estr",
899 "f": true,
900 "g": encodingjson.Number("123"),
901 }
902 deepCopy := runtime.DeepCopyJSON(src)
903 assert.Equal(t, src, deepCopy)
904 }
905
906 func TestFloatIntConversion(t *testing.T) {
907 unstr := map[string]interface{}{"fd": float64(3)}
908
909 var obj F
910 if err := runtime.NewTestUnstructuredConverter(simpleEquality).FromUnstructured(unstr, &obj); err != nil {
911 t.Errorf("Unexpected error in FromUnstructured: %v", err)
912 }
913
914 data, err := json.Marshal(unstr)
915 if err != nil {
916 t.Fatalf("Error when marshaling unstructured: %v", err)
917 }
918 var unmarshalled F
919 if err := json.Unmarshal(data, &unmarshalled); err != nil {
920 t.Fatalf("Error when unmarshaling to object: %v", err)
921 }
922
923 if !reflect.DeepEqual(obj, unmarshalled) {
924 t.Errorf("Incorrect conversion, diff: %v", cmp.Diff(obj, unmarshalled))
925 }
926 }
927
928 func TestIntFloatConversion(t *testing.T) {
929 unstr := map[string]interface{}{"ch": int64(3)}
930
931 var obj C
932 if err := runtime.NewTestUnstructuredConverter(simpleEquality).FromUnstructured(unstr, &obj); err != nil {
933 t.Errorf("Unexpected error in FromUnstructured: %v", err)
934 }
935
936 data, err := json.Marshal(unstr)
937 if err != nil {
938 t.Fatalf("Error when marshaling unstructured: %v", err)
939 }
940 var unmarshalled C
941 if err := json.Unmarshal(data, &unmarshalled); err != nil {
942 t.Fatalf("Error when unmarshaling to object: %v", err)
943 }
944
945 if !reflect.DeepEqual(obj, unmarshalled) {
946 t.Errorf("Incorrect conversion, diff: %v", cmp.Diff(obj, unmarshalled))
947 }
948 }
949
950 func TestCustomToUnstructured(t *testing.T) {
951 testcases := []struct {
952 Data string
953 Expected interface{}
954 }{
955 {Data: `null`, Expected: nil},
956 {Data: `true`, Expected: true},
957 {Data: `false`, Expected: false},
958 {Data: `[]`, Expected: []interface{}{}},
959 {Data: `[1]`, Expected: []interface{}{int64(1)}},
960 {Data: `{}`, Expected: map[string]interface{}{}},
961 {Data: `{"a":1}`, Expected: map[string]interface{}{"a": int64(1)}},
962 {Data: `0`, Expected: int64(0)},
963 {Data: `0.0`, Expected: float64(0)},
964 }
965
966 for _, tc := range testcases {
967 tc := tc
968 t.Run(tc.Data, func(t *testing.T) {
969 t.Parallel()
970 result, err := runtime.NewTestUnstructuredConverter(simpleEquality).ToUnstructured(&G{
971 CustomValue1: CustomValue{data: []byte(tc.Data)},
972 CustomValue2: &CustomValue{data: []byte(tc.Data)},
973 CustomPointer1: CustomPointer{data: []byte(tc.Data)},
974 CustomPointer2: &CustomPointer{data: []byte(tc.Data)},
975 })
976 require.NoError(t, err)
977 for field, fieldResult := range result {
978 assert.Equal(t, tc.Expected, fieldResult, field)
979 }
980 })
981 }
982 }
983
984 func TestCustomToUnstructuredTopLevel(t *testing.T) {
985
986 topLevelCases := []interface{}{
987 &CustomValue{data: []byte(`{"a":1}`)},
988 &CustomPointer{data: []byte(`{"a":1}`)},
989 }
990 expected := map[string]interface{}{"a": int64(1)}
991 for i, obj := range topLevelCases {
992 obj := obj
993 t.Run(strconv.Itoa(i), func(t *testing.T) {
994 t.Parallel()
995 result, err := runtime.NewTestUnstructuredConverter(simpleEquality).ToUnstructured(obj)
996 require.NoError(t, err)
997 assert.Equal(t, expected, result)
998 })
999 }
1000 }
1001
View as plain text