1
16
17
18
19
20
21 package cbor
22
23 import (
24 "bytes"
25 "encoding/hex"
26 "errors"
27 "io"
28 "reflect"
29 "testing"
30
31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
33 "k8s.io/apimachinery/pkg/runtime"
34 "k8s.io/apimachinery/pkg/runtime/schema"
35 "k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes"
36
37 "github.com/google/go-cmp/cmp"
38 )
39
40 func TestRecognizesData(t *testing.T) {
41 for _, tc := range []struct {
42 in []byte
43 recognizes bool
44 }{
45 {
46 in: nil,
47 recognizes: false,
48 },
49 {
50 in: []byte{},
51 recognizes: false,
52 },
53 {
54 in: []byte{0xd9},
55 recognizes: false,
56 },
57 {
58 in: []byte{0xd9, 0xd9},
59 recognizes: false,
60 },
61 {
62 in: []byte{0xd9, 0xd9, 0xf7},
63 recognizes: true,
64 },
65 {
66 in: []byte{0xff, 0xff, 0xff},
67 recognizes: false,
68 },
69 {
70 in: []byte{0xd9, 0xd9, 0xf7, 0x01, 0x02, 0x03},
71 recognizes: true,
72 },
73 {
74 in: []byte{0xff, 0xff, 0xff, 0x01, 0x02, 0x03},
75 recognizes: false,
76 },
77 } {
78 t.Run(hex.EncodeToString(tc.in), func(t *testing.T) {
79 s := NewSerializer(nil, nil)
80 recognizes, unknown, err := s.RecognizesData(tc.in)
81 if recognizes != tc.recognizes {
82 t.Errorf("expected recognized to be %t, got %t", tc.recognizes, recognizes)
83 }
84 if unknown {
85 t.Error("expected unknown to be false, got true")
86 }
87 if err != nil {
88 t.Errorf("expected nil error, got: %v", err)
89 }
90 })
91 }
92 }
93
94 type stubWriter struct {
95 n int
96 err error
97 }
98
99 func (w stubWriter) Write([]byte) (int, error) {
100 return w.n, w.err
101 }
102
103
104 type anyObject struct {
105 Value interface{}
106 }
107
108 func (p anyObject) GetObjectKind() schema.ObjectKind {
109 return schema.EmptyObjectKind
110 }
111
112 func (anyObject) DeepCopyObject() runtime.Object {
113 panic("unimplemented")
114 }
115
116 func (p anyObject) MarshalCBOR() ([]byte, error) {
117 return modes.Encode.Marshal(p.Value)
118 }
119
120 func (p *anyObject) UnmarshalCBOR(in []byte) error {
121 return modes.Decode.Unmarshal(in, &p.Value)
122 }
123
124 func TestEncode(t *testing.T) {
125 for _, tc := range []struct {
126 name string
127 in runtime.Object
128 assertOnWriter func() (io.Writer, func(*testing.T))
129 assertOnError func(*testing.T, error)
130 }{
131 {
132 name: "io error writing self described cbor tag",
133 assertOnWriter: func() (io.Writer, func(*testing.T)) {
134 return stubWriter{err: io.ErrShortWrite}, func(*testing.T) {}
135 },
136 assertOnError: func(t *testing.T, err error) {
137 if !errors.Is(err, io.ErrShortWrite) {
138 t.Errorf("expected io.ErrShortWrite, got: %v", err)
139 }
140 },
141 },
142 {
143 name: "output enclosed by self-described CBOR tag",
144 in: anyObject{},
145 assertOnWriter: func() (io.Writer, func(*testing.T)) {
146 var b bytes.Buffer
147 return &b, func(t *testing.T) {
148 if !bytes.HasPrefix(b.Bytes(), []byte{0xd9, 0xd9, 0xf7}) {
149 t.Errorf("expected output to have prefix 0xd9d9f7: 0x%x", b.Bytes())
150 }
151 }
152 },
153 assertOnError: func(t *testing.T, err error) {
154 if err != nil {
155 t.Errorf("expected nil error, got: %v", err)
156 }
157 },
158 },
159 } {
160 t.Run(tc.name, func(t *testing.T) {
161 s := NewSerializer(nil, nil)
162 w, assertOnWriter := tc.assertOnWriter()
163 err := s.Encode(tc.in, w)
164 tc.assertOnError(t, err)
165 assertOnWriter(t)
166 })
167 }
168 }
169
170 func TestDecode(t *testing.T) {
171 for _, tc := range []struct {
172 name string
173 options []Option
174 data []byte
175 gvk *schema.GroupVersionKind
176 metaFactory metaFactory
177 typer runtime.ObjectTyper
178 creater runtime.ObjectCreater
179 into runtime.Object
180 expectedObj runtime.Object
181 expectedGVK *schema.GroupVersionKind
182 assertOnError func(*testing.T, error)
183 }{
184 {
185 name: "error determining gvk",
186 metaFactory: stubMetaFactory{err: errors.New("test")},
187 assertOnError: func(t *testing.T, err error) {
188 if err == nil || err.Error() != "test" {
189 t.Errorf("expected error \"test\", got: %v", err)
190 }
191 },
192 },
193 {
194 name: "typer does not recognize into",
195 gvk: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
196 metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
197 typer: notRegisteredTyper{},
198 into: &anyObject{},
199 expectedObj: nil,
200 expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
201 assertOnError: func(t *testing.T, err error) {
202 if !runtime.IsNotRegisteredError(err) {
203 t.Errorf("expected NotRegisteredError, got: %v", err)
204 }
205 },
206 },
207 {
208 name: "gvk from type of into",
209 data: []byte{0xf6},
210 gvk: &schema.GroupVersionKind{},
211 metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
212 typer: stubTyper{gvks: []schema.GroupVersionKind{{Group: "x", Version: "y", Kind: "z"}}},
213 into: &anyObject{},
214 expectedObj: &anyObject{},
215 expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
216 assertOnError: func(t *testing.T, err error) {
217 if err != nil {
218 t.Errorf("expected nil error, got: %v", err)
219 }
220 },
221 },
222 {
223 name: "strict mode strict error",
224 options: []Option{Strict(true)},
225 data: []byte{0xa1, 0x61, 'z', 0x01},
226 gvk: &schema.GroupVersionKind{},
227 metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
228 typer: stubTyper{gvks: []schema.GroupVersionKind{{Group: "x", Version: "y", Kind: "z"}}},
229 into: &metav1.PartialObjectMetadata{},
230 expectedObj: &metav1.PartialObjectMetadata{},
231 expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
232 assertOnError: func(t *testing.T, err error) {
233 if !runtime.IsStrictDecodingError(err) {
234 t.Errorf("expected StrictDecodingError, got: %v", err)
235 }
236 },
237 },
238 {
239 name: "no strict mode no strict error",
240 data: []byte{0xa1, 0x61, 'z', 0x01},
241 gvk: &schema.GroupVersionKind{},
242 metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
243 typer: stubTyper{gvks: []schema.GroupVersionKind{{Group: "x", Version: "y", Kind: "z"}}},
244 into: &metav1.PartialObjectMetadata{},
245 expectedObj: &metav1.PartialObjectMetadata{},
246 expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
247 assertOnError: func(t *testing.T, err error) {
248 if err != nil {
249 t.Errorf("expected nil error, got: %v", err)
250 }
251 },
252 },
253 {
254 name: "unknown error from typer on into",
255 gvk: &schema.GroupVersionKind{},
256 metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
257 typer: stubTyper{err: errors.New("test")},
258 into: &anyObject{},
259 expectedObj: nil,
260 expectedGVK: &schema.GroupVersionKind{},
261 assertOnError: func(t *testing.T, err error) {
262 if err == nil || err.Error() != "test" {
263 t.Errorf("expected error \"test\", got: %v", err)
264 }
265 },
266 },
267 {
268 name: "missing kind",
269 gvk: &schema.GroupVersionKind{Version: "v"},
270 metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
271 expectedObj: nil,
272 expectedGVK: &schema.GroupVersionKind{Version: "v"},
273 assertOnError: func(t *testing.T, err error) {
274 if !runtime.IsMissingKind(err) {
275 t.Errorf("expected MissingKind, got: %v", err)
276 }
277 },
278 },
279 {
280 name: "missing version",
281 gvk: &schema.GroupVersionKind{Kind: "k"},
282 metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
283 expectedObj: nil,
284 expectedGVK: &schema.GroupVersionKind{Kind: "k"},
285 assertOnError: func(t *testing.T, err error) {
286 if !runtime.IsMissingVersion(err) {
287 t.Errorf("expected MissingVersion, got: %v", err)
288 }
289 },
290 },
291 {
292 name: "creater error",
293 gvk: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
294 metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
295 creater: stubCreater{err: errors.New("test")},
296 expectedObj: nil,
297 expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
298 assertOnError: func(t *testing.T, err error) {
299 if err == nil || err.Error() != "test" {
300 t.Errorf("expected error \"test\", got: %v", err)
301 }
302 },
303 },
304 {
305 name: "unmarshal error",
306 data: nil,
307 gvk: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
308 metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
309 creater: stubCreater{obj: &anyObject{}},
310 expectedObj: nil,
311 expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
312 assertOnError: func(t *testing.T, err error) {
313 if !errors.Is(err, io.EOF) {
314 t.Errorf("expected EOF, got: %v", err)
315 }
316 },
317 },
318 {
319 name: "strict mode unmarshal error",
320 options: []Option{Strict(true)},
321 data: nil,
322 gvk: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
323 metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
324 creater: stubCreater{obj: &anyObject{}},
325 expectedObj: nil,
326 expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
327 assertOnError: func(t *testing.T, err error) {
328 if !errors.Is(err, io.EOF) {
329 t.Errorf("expected EOF, got: %v", err)
330 }
331 },
332 },
333 {
334 name: "into unstructured unmarshal error",
335 data: nil,
336 gvk: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
337 metaFactory: stubMetaFactory{gvk: &schema.GroupVersionKind{}},
338 into: &unstructured.Unstructured{},
339 expectedObj: nil,
340 expectedGVK: &schema.GroupVersionKind{Group: "x", Version: "y", Kind: "z"},
341 assertOnError: func(t *testing.T, err error) {
342 if !errors.Is(err, io.EOF) {
343 t.Errorf("expected EOF, got: %v", err)
344 }
345 },
346 },
347 {
348 name: "into unstructured missing kind",
349 data: []byte("\xa1\x6aapiVersion\x61v"),
350 into: &unstructured.Unstructured{},
351 expectedObj: nil,
352 expectedGVK: &schema.GroupVersionKind{Version: "v"},
353 assertOnError: func(t *testing.T, err error) {
354 if !runtime.IsMissingKind(err) {
355 t.Errorf("expected MissingKind, got: %v", err)
356 }
357 },
358 },
359 {
360 name: "into unstructured missing version",
361 data: []byte("\xa1\x64kind\x61k"),
362 into: &unstructured.Unstructured{},
363 expectedObj: nil,
364 expectedGVK: &schema.GroupVersionKind{Kind: "k"},
365 assertOnError: func(t *testing.T, err error) {
366 if !runtime.IsMissingVersion(err) {
367 t.Errorf("expected MissingVersion, got: %v", err)
368 }
369 },
370 },
371 {
372 name: "into unstructured",
373 data: []byte("\xa2\x6aapiVersion\x61v\x64kind\x61k"),
374 into: &unstructured.Unstructured{},
375 expectedObj: &unstructured.Unstructured{Object: map[string]interface{}{
376 "apiVersion": "v",
377 "kind": "k",
378 }},
379 expectedGVK: &schema.GroupVersionKind{Version: "v", Kind: "k"},
380 assertOnError: func(t *testing.T, err error) {
381 if err != nil {
382 t.Errorf("expected nil error, got: %v", err)
383 }
384 },
385 },
386 {
387 name: "using unstructured creater",
388 data: []byte("\xa2\x6aapiVersion\x61v\x64kind\x61k"),
389 metaFactory: &defaultMetaFactory{},
390 creater: stubCreater{obj: &unstructured.Unstructured{}},
391 expectedObj: &unstructured.Unstructured{Object: map[string]interface{}{
392 "apiVersion": "v",
393 "kind": "k",
394 }},
395 expectedGVK: &schema.GroupVersionKind{Version: "v", Kind: "k"},
396 assertOnError: func(t *testing.T, err error) {
397 if err != nil {
398 t.Errorf("expected nil error, got: %v", err)
399 }
400 },
401 },
402 } {
403 t.Run(tc.name, func(t *testing.T) {
404 s := newSerializer(tc.metaFactory, tc.creater, tc.typer, tc.options...)
405
406 actualObj, actualGVK, err := s.Decode(tc.data, tc.gvk, tc.into)
407 tc.assertOnError(t, err)
408
409 if !reflect.DeepEqual(tc.expectedObj, actualObj) {
410 t.Error(cmp.Diff(tc.expectedObj, actualObj))
411 }
412
413 if diff := cmp.Diff(tc.expectedGVK, actualGVK); diff != "" {
414 t.Error(diff)
415 }
416 })
417 }
418 }
419
420 func TestMetaFactoryInterpret(t *testing.T) {
421 mf := &defaultMetaFactory{}
422 _, err := mf.Interpret(nil)
423 if err == nil {
424 t.Error("expected non-nil error")
425 }
426 gvk, err := mf.Interpret([]byte("\xa2\x6aapiVersion\x63a/b\x64kind\x61c"))
427 if err != nil {
428 t.Fatalf("unexpected error: %v", err)
429 }
430 if diff := cmp.Diff(&schema.GroupVersionKind{Group: "a", Version: "b", Kind: "c"}, gvk); diff != "" {
431 t.Error(diff)
432 }
433 }
434
435 type stubTyper struct {
436 gvks []schema.GroupVersionKind
437 unversioned bool
438 err error
439 }
440
441 func (t stubTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
442 return t.gvks, t.unversioned, t.err
443 }
444
445 func (stubTyper) Recognizes(schema.GroupVersionKind) bool {
446 return false
447 }
448
449 type stubCreater struct {
450 obj runtime.Object
451 err error
452 }
453
454 func (c stubCreater) New(gvk schema.GroupVersionKind) (runtime.Object, error) {
455 return c.obj, c.err
456 }
457
458 type notRegisteredTyper struct{}
459
460 func (notRegisteredTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
461 return nil, false, runtime.NewNotRegisteredErrForType("test", reflect.TypeOf(obj))
462 }
463
464 func (notRegisteredTyper) Recognizes(schema.GroupVersionKind) bool {
465 return false
466 }
467
468 type stubMetaFactory struct {
469 gvk *schema.GroupVersionKind
470 err error
471 }
472
473 func (mf stubMetaFactory) Interpret([]byte) (*schema.GroupVersionKind, error) {
474 return mf.gvk, mf.err
475 }
476
View as plain text