...

Source file src/k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes/decode_test.go

Documentation: k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes

     1  /*
     2  Copyright 2024 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  package modes_test
    18  
    19  import (
    20  	"encoding/hex"
    21  	"fmt"
    22  	"reflect"
    23  	"testing"
    24  
    25  	"k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes"
    26  
    27  	"github.com/fxamacker/cbor/v2"
    28  	"github.com/google/go-cmp/cmp"
    29  )
    30  
    31  func TestDecode(t *testing.T) {
    32  	hex := func(h string) []byte {
    33  		b, err := hex.DecodeString(h)
    34  		if err != nil {
    35  			t.Fatal(err)
    36  		}
    37  		return b
    38  	}
    39  
    40  	for _, tc := range []struct {
    41  		name          string
    42  		modes         []cbor.DecMode
    43  		in            []byte
    44  		into          interface{} // prototype for concrete destination type. if nil, decode into empty interface value.
    45  		want          interface{}
    46  		assertOnError func(t *testing.T, e error)
    47  	}{
    48  		{
    49  			name:          "reject duplicate negative int keys into struct",
    50  			modes:         []cbor.DecMode{modes.DecodeLax},
    51  			in:            hex("a220012002"), // {-1: 1, -1: 2}
    52  			into:          struct{}{},
    53  			assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: int64(-1), Index: 1}),
    54  		},
    55  		{
    56  			name:          "reject duplicate negative int keys into map",
    57  			in:            hex("a220012002"), // {-1: 1, -1: 2}
    58  			into:          map[int64]interface{}{},
    59  			assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: int64(-1), Index: 1}),
    60  		},
    61  		{
    62  			name:          "reject duplicate positive int keys into struct",
    63  			modes:         []cbor.DecMode{modes.DecodeLax},
    64  			in:            hex("a201010102"), // {1: 1, 1: 2}
    65  			into:          struct{}{},
    66  			assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: int64(1), Index: 1}),
    67  		},
    68  		{
    69  			name:          "reject duplicate positive int keys into map",
    70  			in:            hex("a201010102"), // {1: 1, 1: 2}
    71  			into:          map[int64]interface{}{},
    72  			assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: int64(1), Index: 1}),
    73  		},
    74  		{
    75  			name: "reject duplicate text string keys into struct",
    76  			in:   hex("a2614101614102"), // {"A": 1, "A": 2}
    77  			into: struct {
    78  				A int `json:"A"`
    79  			}{},
    80  			assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("A"), Index: 1}),
    81  		},
    82  		{
    83  			name:          "reject duplicate text string keys into map",
    84  			in:            hex("a2614101614102"), // {"A": 1, "A": 2}
    85  			into:          map[string]interface{}{},
    86  			assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("A"), Index: 1}),
    87  		},
    88  		{
    89  			name:          "reject duplicate byte string keys into map",
    90  			in:            hex("a2414101414102"), // {'A': 1, 'A': 2}
    91  			into:          map[string]interface{}{},
    92  			assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("A"), Index: 1}),
    93  		},
    94  		{
    95  			name: "reject duplicate byte string keys into struct",
    96  			in:   hex("a2414101414102"), // {'A': 1, 'A': 2}
    97  			into: struct {
    98  				A int `json:"A"`
    99  			}{},
   100  			assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("A"), Index: 1}),
   101  		},
   102  		{
   103  			name:          "reject duplicate byte string and text string keys into map",
   104  			in:            hex("a2414101614102"), // {'A': 1, "A": 2}
   105  			into:          map[string]interface{}{},
   106  			assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("A"), Index: 1}),
   107  		},
   108  		{
   109  			name: "reject duplicate byte string and text string keys into struct",
   110  			in:   hex("a2414101614102"), // {'A': 1, "A": 2}
   111  			into: struct {
   112  				A int `json:"A"`
   113  			}{},
   114  			assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("A"), Index: 1}),
   115  		},
   116  		{
   117  			name: "reject two identical indefinite-length byte string keys split into chunks differently into struct",
   118  			in:   hex("a25f426865436c6c6fff015f416844656c6c6fff02"), // {(_ 'he', 'llo'): 1, (_ 'h', 'ello'): 2}
   119  			into: struct {
   120  				Hello int `json:"hello"`
   121  			}{},
   122  			assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("hello"), Index: 1}),
   123  		},
   124  		{
   125  			name:          "reject two identical indefinite-length byte string keys split into chunks differently into map",
   126  			in:            hex("a25f426865436c6c6fff015f416844656c6c6fff02"), // {(_ 'he', 'llo'): 1, (_ 'h', 'ello'): 2}
   127  			into:          map[string]interface{}{},
   128  			assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("hello"), Index: 1}),
   129  		},
   130  		{
   131  			name: "reject two identical indefinite-length text string keys split into chunks differently into struct",
   132  			in:   hex("a27f626865636c6c6fff017f616864656c6c6fff02"), // {(_ "he", "llo"): 1, (_ "h", "ello"): 2}
   133  			into: struct {
   134  				Hello int `json:"hello"`
   135  			}{},
   136  			assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("hello"), Index: 1}),
   137  		},
   138  		{
   139  			name:          "reject two identical indefinite-length text string keys split into chunks differently into map",
   140  			modes:         []cbor.DecMode{modes.DecodeLax},
   141  			in:            hex("a27f626865636c6c6fff017f616864656c6c6fff02"), // {(_ "he", "llo"): 1, (_ "h", "ello"): 2}
   142  			into:          map[string]interface{}{},
   143  			assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("hello"), Index: 1}),
   144  		},
   145  		{
   146  			name:  "case-insensitive match treated as unknown field",
   147  			modes: []cbor.DecMode{modes.Decode},
   148  			in:    hex("a1614101"), // {"A": 1}
   149  			into: struct {
   150  				A int `json:"a"`
   151  			}{},
   152  			assertOnError: assertIdenticalError(&cbor.UnknownFieldError{Index: 0}),
   153  		},
   154  		{
   155  			name:  "case-insensitive match ignored in lax mode",
   156  			modes: []cbor.DecMode{modes.DecodeLax},
   157  			in:    hex("a1614101"), // {"A": 1}
   158  			into: struct {
   159  				A int `json:"a"`
   160  			}{},
   161  			want: struct {
   162  				A int `json:"a"`
   163  			}{
   164  				A: 0,
   165  			},
   166  			assertOnError: assertNilError,
   167  		},
   168  		{
   169  			name:  "case-insensitive match after exact match treated as unknown field",
   170  			modes: []cbor.DecMode{modes.Decode},
   171  			in:    hex("a2616101614102"), // {"a": 1, "A": 2}
   172  			into: struct {
   173  				A int `json:"a"`
   174  			}{},
   175  			want: struct {
   176  				A int `json:"a"`
   177  			}{
   178  				A: 1,
   179  			},
   180  			assertOnError: assertIdenticalError(&cbor.UnknownFieldError{Index: 1}),
   181  		},
   182  		{
   183  			name:  "case-insensitive match after exact match ignored in lax mode",
   184  			modes: []cbor.DecMode{modes.DecodeLax},
   185  			in:    hex("a2616101614102"), // {"a": 1, "A": 2}
   186  			into: struct {
   187  				A int `json:"a"`
   188  			}{},
   189  			want: struct {
   190  				A int `json:"a"`
   191  			}{
   192  				A: 1,
   193  			},
   194  			assertOnError: assertNilError,
   195  		},
   196  		{
   197  			name:  "case-insensitive match before exact match treated as unknown field",
   198  			modes: []cbor.DecMode{modes.Decode},
   199  			in:    hex("a2614101616102"), // {"A": 1, "a": 2}
   200  			into: struct {
   201  				A int `json:"a"`
   202  			}{},
   203  			assertOnError: assertIdenticalError(&cbor.UnknownFieldError{Index: 0}),
   204  		},
   205  		{
   206  			name:  "case-insensitive match before exact match ignored in lax mode",
   207  			modes: []cbor.DecMode{modes.DecodeLax},
   208  			in:    hex("a2614101616102"), // {"A": 1, "a": 2}
   209  			into: struct {
   210  				A int `json:"a"`
   211  			}{},
   212  			want: struct {
   213  				A int `json:"a"`
   214  			}{
   215  				A: 2,
   216  			},
   217  			assertOnError: assertNilError,
   218  		},
   219  		{
   220  			name: "reject text string containing invalid utf-8 sequence",
   221  			in:   hex("6180"), // text string beginning with continuation byte 0x80
   222  			assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.SemanticError) {
   223  				const expected = "cbor: invalid UTF-8 string"
   224  				if msg := e.Error(); msg != expected {
   225  					t.Errorf("expected %v, got %v", expected, msg)
   226  				}
   227  			}),
   228  		},
   229  		{
   230  			name:          "unsigned integer decodes to interface{} as int64",
   231  			in:            hex("0a"), // 10
   232  			want:          int64(10),
   233  			assertOnError: assertNilError,
   234  		},
   235  		{
   236  			name:  "unknown field error",
   237  			modes: []cbor.DecMode{modes.Decode},
   238  			in:    hex("a1616101"), // {"a": 1}
   239  			into:  struct{}{},
   240  			assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnknownFieldError) {
   241  				if e.Index != 0 {
   242  					t.Errorf("expected %#v, got %#v", &cbor.UnknownFieldError{Index: 0}, e)
   243  				}
   244  			}),
   245  		},
   246  		{
   247  			name:          "no unknown field error in lax mode",
   248  			modes:         []cbor.DecMode{modes.DecodeLax},
   249  			in:            hex("a1616101"), // {"a": 1}
   250  			into:          struct{}{},
   251  			want:          struct{}{},
   252  			assertOnError: assertNilError,
   253  		},
   254  		{
   255  			name:          "indefinite-length text string",
   256  			in:            hex("7f616161626163ff"), // (_ "a", "b", "c")
   257  			want:          "abc",
   258  			assertOnError: assertNilError,
   259  		},
   260  		{
   261  			name: "nested indefinite-length array",
   262  			in:   hex("9f9f8080ff9f8080ffff"), // [_ [_ [] []] [_ [][]]]
   263  			want: []interface{}{
   264  				[]interface{}{[]interface{}{}, []interface{}{}},
   265  				[]interface{}{[]interface{}{}, []interface{}{}},
   266  			},
   267  			assertOnError: assertNilError,
   268  		},
   269  		{
   270  			name: "nested indefinite-length map",
   271  			in:   hex("bf6141bf616101616202ff6142bf616901616a02ffff"), // {_ "A": {_ "a": 1, "b": 2}, "B": {_ "i": 1, "j": 2}}
   272  			want: map[string]interface{}{
   273  				"A": map[string]interface{}{"a": int64(1), "b": int64(2)},
   274  				"B": map[string]interface{}{"i": int64(1), "j": int64(2)},
   275  			},
   276  			assertOnError: assertNilError,
   277  		},
   278  	} {
   279  		decModes := tc.modes
   280  		if len(decModes) == 0 {
   281  			decModes = allDecModes
   282  		}
   283  
   284  		for _, decMode := range decModes {
   285  			modeName, ok := decModeNames[decMode]
   286  			if !ok {
   287  				t.Fatal("test case configured to run against unrecognized mode")
   288  			}
   289  
   290  			t.Run(fmt.Sprintf("mode=%s/%s", modeName, tc.name), func(t *testing.T) {
   291  				var dst reflect.Value
   292  				if tc.into == nil {
   293  					var i interface{}
   294  					dst = reflect.ValueOf(&i)
   295  				} else {
   296  					dst = reflect.New(reflect.TypeOf(tc.into))
   297  				}
   298  				err := decMode.Unmarshal(tc.in, dst.Interface())
   299  				tc.assertOnError(t, err)
   300  				if tc.want != nil {
   301  					if diff := cmp.Diff(tc.want, dst.Elem().Interface()); diff != "" {
   302  						t.Errorf("unexpected output:\n%s", diff)
   303  					}
   304  				}
   305  			})
   306  		}
   307  	}
   308  }
   309  

View as plain text