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  }