...

Source file src/k8s.io/apimachinery/pkg/runtime/converter_test.go

Documentation: k8s.io/apimachinery/pkg/runtime

     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  	"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  // 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  	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  // MarshalJSON has a value receiver on this type.
   159  func (c CustomValue) MarshalJSON() ([]byte, error) {
   160  	return c.data, nil
   161  }
   162  
   163  type CustomPointer struct {
   164  	data []byte
   165  }
   166  
   167  // MarshalJSON has a pointer receiver on this type.
   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  	// TODO: should be using mismatch detection but fails due to another error
   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  				// Not testing a list with nil Items because items is a non-optional field and hence
   232  				// is always marshaled into an empty array which is not equal to nil when unmarshalled and will fail.
   233  				// That is expected.
   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  			// This (among others) tests nil map, slice and pointer.
   271  			obj: &C{
   272  				C: "ccc",
   273  			},
   274  		},
   275  		{
   276  			// This (among others) tests empty map and slice.
   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  			// Test slice of interface{} with empty slices.
   324  			obj: &D{
   325  				A: []interface{}{[]interface{}{}, []interface{}{}},
   326  			},
   327  		},
   328  		{
   329  			// Test slice of interface{} with different values.
   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  // TestUnknownFields checks for the collection of unknown
   344  // field errors from the various possible locations of
   345  // unknown fields (e.g. fields on struct, inlined struct, slice, etc)
   346  func TestUnknownFields(t *testing.T) {
   347  	// simples checks that basic unknown fields are found
   348  	// in fields, subfields and slices.
   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  	// same-name, different-levels checks that
   368  	// fields at a higher level in the json
   369  	// are not persisted to unrecognized fields
   370  	// at lower levels and vice-versa.
   371  	//
   372  	// In this case, the field "cc" exists at the root level
   373  	// but not in the nested field ul1. If we are
   374  	// improperly retaining matched keys, this not
   375  	// see an issue with "cc" existing inside "ul1"
   376  	//
   377  	// The opposite for "aaa", which exists at the
   378  	// nested level but not at the root.
   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  	// inlined-inlined confirms that we see
   396  	// fields that are doubly nested and don't recognize
   397  	// those that aren't
   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  	// combined tests everything together
   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  // BenchmarkFromUnstructuredWithValidation benchmarks
   639  // the time and memory required to perform FromUnstructured
   640  // with the various validation directives (Ignore, Warn, Strict)
   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  // Verifies that:
   667  // 1) serialized json -> object
   668  // 2) serialized json -> map[string]interface{} -> object
   669  // produces the same object.
   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  			// Only unknown fields
   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  			// Invalid string data
   761  			data: "{\"fa\":123}",
   762  			obj:  &F{},
   763  			err:  fmt.Errorf("json: cannot unmarshal number into Go value of type string"),
   764  		},
   765  		{
   766  			// Invalid string data
   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  			// Invalid string data
   773  			data: "{\"fa\":true}",
   774  			obj:  &F{},
   775  			err:  fmt.Errorf("json: cannot unmarshal bool into Go value of type string"),
   776  		},
   777  		{
   778  			// Invalid []string data
   779  			data: "{\"ff\":123}",
   780  			obj:  &F{},
   781  			err:  fmt.Errorf("json: cannot unmarshal number into Go value of type []string"),
   782  		},
   783  		{
   784  			// Invalid []string data
   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  			// Invalid []string data
   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  			// Invalid []int data
   797  			data: "{\"fg\":123}",
   798  			obj:  &F{},
   799  			err:  fmt.Errorf("json: cannot unmarshal number into Go value of type []int"),
   800  		},
   801  		{
   802  			// Invalid []int data
   803  			data: "{\"fg\":\"abc\"}",
   804  			obj:  &F{},
   805  			err:  fmt.Errorf("json: cannot unmarshal string into Go value of type []int"),
   806  		},
   807  		{
   808  			// Invalid []int data
   809  			data: "{\"fg\":[\"abc\"]}",
   810  			obj:  &F{},
   811  			err:  fmt.Errorf("json: cannot unmarshal string into Go value of type int"),
   812  		},
   813  		{
   814  			// Invalid []int data
   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  			// Invalid []int data
   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  			// Invalid []bool data
   827  			data: "{\"fh\":123}",
   828  			obj:  &F{},
   829  			err:  fmt.Errorf("json: cannot unmarshal number into Go value of type []bool"),
   830  		},
   831  		{
   832  			// Invalid []bool data
   833  			data: "{\"fh\":\"abc\"}",
   834  			obj:  &F{},
   835  			err:  fmt.Errorf("json: cannot unmarshal string into Go value of type []bool"),
   836  		},
   837  		{
   838  			// Invalid []bool data
   839  			data: "{\"fh\":[\"abc\"]}",
   840  			obj:  &F{},
   841  			err:  fmt.Errorf("json: cannot unmarshal string into Go value of type bool"),
   842  		},
   843  		{
   844  			// Invalid []bool data
   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  			// Invalid []bool data
   851  			data: "{\"fh\":[123]}",
   852  			obj:  &F{},
   853  			err:  fmt.Errorf("json: cannot unmarshal number into Go value of type bool"),
   854  		},
   855  		{
   856  			// Invalid []float data
   857  			data: "{\"fi\":123}",
   858  			obj:  &F{},
   859  			err:  fmt.Errorf("json: cannot unmarshal number into Go value of type []float32"),
   860  		},
   861  		{
   862  			// Invalid []float data
   863  			data: "{\"fi\":\"abc\"}",
   864  			obj:  &F{},
   865  			err:  fmt.Errorf("json: cannot unmarshal string into Go value of type []float32"),
   866  		},
   867  		{
   868  			// Invalid []float data
   869  			data: "{\"fi\":[\"abc\"]}",
   870  			obj:  &F{},
   871  			err:  fmt.Errorf("json: cannot unmarshal string into Go value of type float32"),
   872  		},
   873  		{
   874  			// Invalid []float data
   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  	// Only objects are supported at the top level
   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