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