1
16
17 package json_test
18
19 import (
20 "fmt"
21 "reflect"
22 "strings"
23 "testing"
24
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
27 "k8s.io/apimachinery/pkg/runtime"
28 "k8s.io/apimachinery/pkg/runtime/schema"
29 "k8s.io/apimachinery/pkg/runtime/serializer/json"
30 runtimetesting "k8s.io/apimachinery/pkg/runtime/testing"
31 "k8s.io/apimachinery/pkg/util/diff"
32 )
33
34 type testDecodable struct {
35 metav1.TypeMeta `json:",inline"`
36
37 Other string
38 Value int `json:"value"`
39 Spec DecodableSpec `json:"spec"`
40 Interface interface{} `json:"interface"`
41 }
42
43
44 type DecodableSpec struct {
45 A int `json:"A"`
46 B int `json:"B"`
47 C int `json:"C"`
48 D int `json:"D"`
49 E int `json:"E"`
50 F int `json:"F"`
51 G int `json:"G"`
52 H int `json:"h"`
53 I int `json:"i"`
54 J int `json:"j"`
55 K int `json:"k"`
56 L int `json:"l"`
57 M int `json:"m"`
58 N int `json:"n"`
59 O int `json:"o"`
60 }
61
62 func (d *testDecodable) DeepCopyObject() runtime.Object {
63 if d == nil {
64 return nil
65 }
66 out := new(testDecodable)
67 d.DeepCopyInto(out)
68 return out
69 }
70 func (d *testDecodable) DeepCopyInto(out *testDecodable) {
71 *out = *d
72 out.Other = d.Other
73 out.Value = d.Value
74 out.Spec = d.Spec
75 out.Interface = d.Interface
76 return
77 }
78
79 type testDecodeCoercion struct {
80 metav1.TypeMeta `json:",inline"`
81
82 Bool bool `json:"bool"`
83
84 Int int `json:"int"`
85 Int32 int `json:"int32"`
86 Int64 int `json:"int64"`
87
88 Float32 float32 `json:"float32"`
89 Float64 float64 `json:"float64"`
90
91 String string `json:"string"`
92
93 Struct testDecodable `json:"struct"`
94
95 Array []string `json:"array"`
96 Map map[string]string `json:"map"`
97 }
98
99 func (d *testDecodeCoercion) DeepCopyObject() runtime.Object {
100 if d == nil {
101 return nil
102 }
103 out := new(testDecodeCoercion)
104 d.DeepCopyInto(out)
105 return out
106 }
107 func (d *testDecodeCoercion) DeepCopyInto(out *testDecodeCoercion) {
108 *out = *d
109 return
110 }
111
112 func TestDecode(t *testing.T) {
113 type testCase struct {
114 creater runtime.ObjectCreater
115 typer runtime.ObjectTyper
116 yaml bool
117 pretty bool
118 strict bool
119
120 data []byte
121 defaultGVK *schema.GroupVersionKind
122 into runtime.Object
123
124 errFn func(error) bool
125 expectedObject runtime.Object
126 expectedGVK *schema.GroupVersionKind
127 }
128
129 testCases := []testCase{
130
131 {
132 data: []byte("{}"),
133
134 expectedGVK: &schema.GroupVersionKind{},
135 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
136 },
137 {
138 data: []byte("{}"),
139
140 expectedGVK: &schema.GroupVersionKind{},
141 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
142 strict: true,
143 },
144
145 {
146 data: []byte(`{"kind":"Foo"}`),
147
148 expectedGVK: &schema.GroupVersionKind{Kind: "Foo"},
149 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'apiVersion' is missing in") },
150 },
151 {
152 data: []byte(`{"kind":"Foo"}`),
153
154 expectedGVK: &schema.GroupVersionKind{Kind: "Foo"},
155 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'apiVersion' is missing in") },
156 strict: true,
157 },
158
159 {
160 data: []byte(`{"apiVersion":"foo/v1"}`),
161
162 expectedGVK: &schema.GroupVersionKind{Group: "foo", Version: "v1"},
163 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
164 },
165 {
166 data: []byte(`{"apiVersion":"foo/v1"}`),
167
168 expectedGVK: &schema.GroupVersionKind{Group: "foo", Version: "v1"},
169 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
170 strict: true,
171 },
172
173 {
174 data: []byte(`{"apiVersion":"/v1","kind":"Foo"}`),
175 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
176 creater: &mockCreater{obj: &testDecodable{}},
177
178 expectedObject: &testDecodable{TypeMeta: metav1.TypeMeta{APIVersion: "/v1", Kind: "Foo"}},
179 expectedGVK: &schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Foo"},
180 },
181 {
182 data: []byte(`{"apiVersion":"/v1","kind":"Foo"}`),
183 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
184 creater: &mockCreater{obj: &testDecodable{}},
185
186 expectedObject: &testDecodable{TypeMeta: metav1.TypeMeta{APIVersion: "/v1", Kind: "Foo"}},
187 expectedGVK: &schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Foo"},
188 strict: true,
189 },
190
191
192 {
193 data: []byte("{}"),
194 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
195 into: &unstructured.Unstructured{},
196
197 expectedGVK: &schema.GroupVersionKind{},
198 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
199 },
200 {
201 data: []byte("{}"),
202 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
203 into: &unstructured.Unstructured{},
204
205 expectedGVK: &schema.GroupVersionKind{},
206 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
207 strict: true,
208 },
209
210 {
211 data: []byte(`{"kind":"Foo"}`),
212 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
213 into: &unstructured.Unstructured{},
214
215 expectedGVK: &schema.GroupVersionKind{Kind: "Foo"},
216 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"kind": "Foo"}},
217
218 },
219 {
220 data: []byte(`{"kind":"Foo"}`),
221 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
222 into: &unstructured.Unstructured{},
223
224 expectedGVK: &schema.GroupVersionKind{Kind: "Foo"},
225 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"kind": "Foo"}},
226 strict: true,
227
228 },
229
230 {
231 data: []byte(`{"apiVersion":"foo/v1"}`),
232 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
233 into: &unstructured.Unstructured{},
234
235 expectedGVK: &schema.GroupVersionKind{Group: "foo", Version: "v1"},
236 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
237 },
238 {
239 data: []byte(`{"apiVersion":"foo/v1"}`),
240 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
241 into: &unstructured.Unstructured{},
242
243 expectedGVK: &schema.GroupVersionKind{Group: "foo", Version: "v1"},
244 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
245 strict: true,
246 },
247
248 {
249 data: []byte(`{"apiVersion":"/v1","kind":"Foo"}`),
250 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
251 into: &unstructured.Unstructured{},
252
253 expectedGVK: &schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Foo"},
254 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "/v1", "kind": "Foo"}},
255 },
256 {
257 data: []byte(`{"apiVersion":"/v1","kind":"Foo"}`),
258 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
259 into: &unstructured.Unstructured{},
260
261 expectedGVK: &schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Foo"},
262 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "/v1", "kind": "Foo"}},
263 strict: true,
264 },
265
266
267 {
268 data: []byte("{}"),
269 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
270 into: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "into/v1", "kind": "Into"}},
271
272 expectedGVK: &schema.GroupVersionKind{},
273 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
274 },
275 {
276 data: []byte("{}"),
277 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
278 into: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "into/v1", "kind": "Into"}},
279
280 expectedGVK: &schema.GroupVersionKind{},
281 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
282 strict: true,
283 },
284
285 {
286 data: []byte(`{"kind":"Foo"}`),
287 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
288 into: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "into/v1", "kind": "Into"}},
289
290 expectedGVK: &schema.GroupVersionKind{Kind: "Foo"},
291 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"kind": "Foo"}},
292
293 },
294 {
295 data: []byte(`{"kind":"Foo"}`),
296 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
297 into: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "into/v1", "kind": "Into"}},
298
299 expectedGVK: &schema.GroupVersionKind{Kind: "Foo"},
300 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"kind": "Foo"}},
301 strict: true,
302
303 },
304
305 {
306 data: []byte(`{"apiVersion":"foo/v1"}`),
307 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
308 into: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "into/v1", "kind": "Into"}},
309
310 expectedGVK: &schema.GroupVersionKind{Group: "foo", Version: "v1"},
311 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
312 },
313 {
314 data: []byte(`{"apiVersion":"foo/v1"}`),
315 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
316 into: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "into/v1", "kind": "Into"}},
317
318 expectedGVK: &schema.GroupVersionKind{Group: "foo", Version: "v1"},
319 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
320 strict: true,
321 },
322
323 {
324 data: []byte(`{"apiVersion":"/v1","kind":"Foo"}`),
325 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
326 into: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "into/v1", "kind": "Into"}},
327
328 expectedGVK: &schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Foo"},
329 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "/v1", "kind": "Foo"}},
330 },
331 {
332 data: []byte(`{"apiVersion":"/v1","kind":"Foo"}`),
333 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
334 into: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "into/v1", "kind": "Into"}},
335
336 expectedGVK: &schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Foo"},
337 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "/v1", "kind": "Foo"}},
338 strict: true,
339 },
340
341
342 {
343 data: []byte("{}"),
344 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
345 creater: &mockCreater{obj: &unstructured.Unstructured{}},
346
347 expectedGVK: &schema.GroupVersionKind{},
348 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
349 },
350 {
351 data: []byte("{}"),
352 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
353 creater: &mockCreater{obj: &unstructured.Unstructured{}},
354
355 expectedGVK: &schema.GroupVersionKind{},
356 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
357 strict: true,
358 },
359
360 {
361 data: []byte(`{"kind":"Foo"}`),
362 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
363 creater: &mockCreater{obj: &unstructured.Unstructured{}},
364
365 expectedGVK: &schema.GroupVersionKind{Kind: "Foo"},
366 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'apiVersion' is missing in") },
367 },
368 {
369 data: []byte(`{"kind":"Foo"}`),
370 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
371 creater: &mockCreater{obj: &unstructured.Unstructured{}},
372
373 expectedGVK: &schema.GroupVersionKind{Kind: "Foo"},
374 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'apiVersion' is missing in") },
375 strict: true,
376 },
377
378 {
379 data: []byte(`{"apiVersion":"foo/v1"}`),
380 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
381 creater: &mockCreater{obj: &unstructured.Unstructured{}},
382
383 expectedGVK: &schema.GroupVersionKind{Group: "foo", Version: "v1"},
384 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
385 },
386 {
387 data: []byte(`{"apiVersion":"foo/v1"}`),
388 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
389 creater: &mockCreater{obj: &unstructured.Unstructured{}},
390
391 expectedGVK: &schema.GroupVersionKind{Group: "foo", Version: "v1"},
392 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
393 strict: true,
394 },
395
396 {
397 data: []byte(`{"apiVersion":"/v1","kind":"Foo"}`),
398 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
399 creater: &mockCreater{obj: &unstructured.Unstructured{}},
400
401 expectedGVK: &schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Foo"},
402 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "/v1", "kind": "Foo"}},
403 },
404 {
405 data: []byte(`{"apiVersion":"/v1","kind":"Foo"}`),
406 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
407 creater: &mockCreater{obj: &unstructured.Unstructured{}},
408
409 expectedGVK: &schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Foo"},
410 expectedObject: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "/v1", "kind": "Foo"}},
411 strict: true,
412 },
413
414
415 {
416 data: []byte("{}"),
417 defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
418 creater: &mockCreater{err: fmt.Errorf("fake error")},
419
420 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
421 errFn: func(err error) bool { return err.Error() == "fake error" },
422 },
423 {
424 data: []byte("{}"),
425 defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
426 creater: &mockCreater{err: fmt.Errorf("fake error")},
427
428 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
429 errFn: func(err error) bool { return err.Error() == "fake error" },
430 },
431
432 {
433 data: []byte("{}"),
434 defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
435 creater: &mockCreater{obj: &testDecodable{}},
436 expectedObject: &testDecodable{},
437 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
438 },
439 {
440 data: []byte("{}"),
441 defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
442 creater: &mockCreater{obj: &testDecodable{}},
443 expectedObject: &testDecodable{},
444 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
445 strict: true,
446 },
447
448
449 {
450 data: []byte(`{"apiVersion":"blah"}`),
451 defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
452 creater: &mockCreater{obj: &testDecodable{}},
453 expectedObject: &testDecodable{TypeMeta: metav1.TypeMeta{APIVersion: "blah"}},
454 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "", Version: "blah"},
455 },
456
457 {
458 data: []byte(`{"apiVersion":"other/"}`),
459 defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
460 creater: &mockCreater{obj: &testDecodable{}},
461 expectedObject: &testDecodable{TypeMeta: metav1.TypeMeta{APIVersion: "other/"}},
462 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
463 },
464
465 {
466 data: []byte(`{"apiVersion":"other1/blah1"}`),
467 defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
468 creater: &mockCreater{obj: &testDecodable{}},
469 expectedObject: &testDecodable{TypeMeta: metav1.TypeMeta{APIVersion: "other1/blah1"}},
470 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other1", Version: "blah1"},
471 },
472
473 {
474 data: []byte(`{"kind":"Test","apiVersion":"other/blah"}`),
475 defaultGVK: &schema.GroupVersionKind{Kind: "Test1", Group: "other1", Version: "blah1"},
476 creater: &mockCreater{obj: &testDecodable{}},
477 expectedObject: &testDecodable{TypeMeta: metav1.TypeMeta{APIVersion: "other/blah", Kind: "Test"}},
478 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
479 },
480
481 {
482 data: []byte(`{"apiVersion":"b1/c1"}`),
483 into: &testDecodable{TypeMeta: metav1.TypeMeta{Kind: "a3", APIVersion: "b1/c1"}},
484 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "a3", Group: "b1", Version: "c1"}},
485 defaultGVK: nil,
486 creater: &mockCreater{obj: &testDecodable{}},
487 expectedObject: &testDecodable{TypeMeta: metav1.TypeMeta{Kind: "a3", APIVersion: "b1/c1"}},
488 expectedGVK: &schema.GroupVersionKind{Kind: "a3", Group: "b1", Version: "c1"},
489 },
490
491
492 {
493 data: []byte(`{}`),
494 into: &runtime.Unknown{},
495
496 expectedGVK: &schema.GroupVersionKind{},
497 expectedObject: &runtime.Unknown{
498 Raw: []byte(`{}`),
499 ContentType: runtime.ContentTypeJSON,
500 },
501 },
502 {
503 data: []byte(`{"test":"object"}`),
504 into: &runtime.Unknown{},
505
506 expectedGVK: &schema.GroupVersionKind{},
507 expectedObject: &runtime.Unknown{
508 Raw: []byte(`{"test":"object"}`),
509 ContentType: runtime.ContentTypeJSON,
510 },
511 },
512 {
513 data: []byte(`{"test":"object"}`),
514 into: &runtime.Unknown{},
515 defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
516 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
517 expectedObject: &runtime.Unknown{
518 TypeMeta: runtime.TypeMeta{APIVersion: "other/blah", Kind: "Test"},
519 Raw: []byte(`{"test":"object"}`),
520 ContentType: runtime.ContentTypeJSON,
521 },
522 },
523
524
525 {
526 data: []byte(`{"kind":"Test","apiVersion":"other/blah","value":1,"Other":"test"}`),
527 into: &testDecodable{},
528 typer: &mockTyper{err: runtime.NewNotRegisteredErrForKind("mock", schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"})},
529 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
530 expectedObject: &testDecodable{
531 TypeMeta: metav1.TypeMeta{APIVersion: "other/blah", Kind: "Test"},
532 Other: "test",
533 Value: 1,
534 },
535 },
536
537 {
538 data: []byte(`{"value":1,"Other":"test"}`),
539 into: &testDecodable{},
540 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
541 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
542 expectedObject: &testDecodable{
543 Other: "test",
544 Value: 1,
545 },
546 },
547
548 {
549 data: []byte(`{"value":1,"Other":"test"}`),
550 into: &testDecodable{},
551 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: ""}},
552 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: ""},
553 errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'apiVersion' is missing in") },
554 expectedObject: &testDecodable{
555 Other: "test",
556 Value: 1,
557 },
558 },
559
560 {
561 data: []byte(`{"kind":"Test","apiVersion":"other/blah","interface":1e1000}`),
562 creater: &mockCreater{obj: &testDecodable{}},
563 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
564 errFn: func(err error) bool {
565 return strings.Contains(err.Error(), `json: cannot unmarshal number 1e1000 into Go struct field testDecodable.interface of type float64`)
566 },
567 },
568
569 {
570
571 data: []byte(`{"kind":"Test","apiVersion":"other/blah","VaLue":1,"Other":"test"}`),
572 into: &testDecodable{},
573 typer: &mockTyper{err: runtime.NewNotRegisteredErrForKind("mock", schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"})},
574 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
575 expectedObject: &testDecodable{
576 TypeMeta: metav1.TypeMeta{APIVersion: "other/blah", Kind: "Test"},
577 Other: "test",
578 },
579 },
580
581 {
582
583 data: []byte(`{"kind":"Test","apiVersion":"other/blah","spec": {"A": 1, "b": 2, "h": 3, "I": 4}}`),
584 into: &testDecodable{},
585 typer: &mockTyper{err: runtime.NewNotRegisteredErrForKind("mock", schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"})},
586 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
587 expectedObject: &testDecodable{
588 TypeMeta: metav1.TypeMeta{APIVersion: "other/blah", Kind: "Test"},
589 Spec: DecodableSpec{A: 1, H: 3},
590 },
591 },
592
593 {
594 data: []byte(`{"unknown": 1}`),
595 into: &testDecodable{},
596 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
597 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
598 errFn: func(err error) bool {
599 return strings.Contains(err.Error(), `unknown field "unknown"`)
600 },
601 strict: true,
602 },
603
604 {
605 data: []byte("unknown: 1\n"),
606 into: &testDecodable{},
607 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
608 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
609 errFn: func(err error) bool {
610 return strings.Contains(err.Error(), `unknown field "unknown"`)
611 },
612 yaml: true,
613 strict: true,
614 },
615
616 {
617 data: []byte(`{"value":1,"value":1}`),
618 into: &testDecodable{},
619 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
620 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
621 errFn: func(err error) bool {
622 return strings.Contains(err.Error(), `duplicate field "value"`)
623 },
624 strict: true,
625 },
626
627 {
628 data: []byte("value: 1\n" +
629 "value: 1\n"),
630 into: &testDecodable{},
631 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
632 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
633 errFn: func(err error) bool {
634 return strings.Contains(err.Error(), `"value" already set in map`)
635 },
636 yaml: true,
637 strict: true,
638 },
639
640 {
641 data: []byte(`{"kind":"Custom","value":1,"value":1}`),
642 into: &unstructured.Unstructured{},
643 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
644 expectedGVK: &schema.GroupVersionKind{Kind: "Custom"},
645 errFn: func(err error) bool {
646 return strings.Contains(err.Error(), `duplicate field "value"`)
647 },
648 strict: true,
649 },
650
651 {
652 data: []byte("kind: Custom\n" +
653 "value: 1\n" +
654 "value: 1\n"),
655 into: &unstructured.Unstructured{},
656 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
657 expectedGVK: &schema.GroupVersionKind{Kind: "Custom"},
658 errFn: func(err error) bool {
659 return strings.Contains(err.Error(), `"value" already set in map`)
660 },
661 yaml: true,
662 strict: true,
663 },
664
665 {
666 data: []byte(`{"kind":"Test","apiVersion":"other/blah","value":1,"Other":"test"}`),
667 into: &testDecodable{},
668 typer: &mockTyper{err: runtime.NewNotRegisteredErrForKind("mock", schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"})},
669 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
670 expectedObject: &testDecodable{
671 TypeMeta: metav1.TypeMeta{APIVersion: "other/blah", Kind: "Test"},
672 Other: "test",
673 Value: 1,
674 },
675 strict: true,
676 },
677
678 {
679 data: []byte("kind: Test\n" +
680 "apiVersion: other/blah\n" +
681 "value: 1\n" +
682 "Other: test\n"),
683 into: &testDecodable{},
684 typer: &mockTyper{err: runtime.NewNotRegisteredErrForKind("mock", schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"})},
685 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
686 expectedObject: &testDecodable{
687 TypeMeta: metav1.TypeMeta{APIVersion: "other/blah", Kind: "Test"},
688 Other: "test",
689 Value: 1,
690 },
691 yaml: true,
692 strict: true,
693 },
694
695 {
696 data: []byte(`{"value":1234}`),
697 into: &testDecodable{},
698 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
699 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
700 expectedObject: &testDecodable{
701 Value: 1234,
702 },
703 strict: true,
704 },
705
706 {
707 data: []byte("value: 1234\n"),
708 into: &testDecodable{},
709 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
710 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
711 expectedObject: &testDecodable{
712 Value: 1234,
713 },
714 yaml: true,
715 strict: true,
716 },
717
718 {
719 data: []byte("foo"),
720 into: &unstructured.Unstructured{},
721 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
722 errFn: func(err error) bool {
723 return strings.Contains(err.Error(), `json parse error: invalid character 'o'`)
724 },
725 strict: true,
726 },
727
728 {
729 data: []byte("{}"),
730 into: &unstructured.Unstructured{},
731 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
732 expectedGVK: &schema.GroupVersionKind{},
733 errFn: func(err error) bool {
734 return strings.Contains(err.Error(), `Object 'Kind' is missing`)
735 },
736 strict: true,
737 },
738
739 {
740 data: []byte(`{"bool":null,"int":null,"int32":null,"int64":null,"float32":null,"float64":null,"string":null,"array":null,"map":null,"struct":null}`),
741 into: &testDecodeCoercion{},
742 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
743 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
744 expectedObject: &testDecodeCoercion{},
745 strict: true,
746 },
747 {
748 data: []byte(`{"bool":null,"int":null,"int32":null,"int64":null,"float32":null,"float64":null,"string":null,"array":null,"map":null,"struct":null}`),
749 into: &testDecodeCoercion{},
750 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
751 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
752 expectedObject: &testDecodeCoercion{},
753 yaml: true,
754 strict: true,
755 },
756
757 {
758 data: []byte(`{"string":""}`),
759 into: &testDecodeCoercion{},
760 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
761 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
762 expectedObject: &testDecodeCoercion{},
763 strict: true,
764 },
765 {
766 data: []byte(`{"string":""}`),
767 into: &testDecodeCoercion{},
768 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
769 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
770 expectedObject: &testDecodeCoercion{},
771 yaml: true,
772 strict: true,
773 },
774
775 {
776 data: []byte(`{"array":[]}`),
777 into: &testDecodeCoercion{},
778 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
779 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
780 expectedObject: &testDecodeCoercion{Array: []string{}},
781 strict: true,
782 },
783 {
784 data: []byte(`{"array":[]}`),
785 into: &testDecodeCoercion{},
786 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
787 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
788 expectedObject: &testDecodeCoercion{Array: []string{}},
789 yaml: true,
790 strict: true,
791 },
792
793 {
794 data: []byte(`{"map":{},"struct":{}}`),
795 into: &testDecodeCoercion{},
796 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
797 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
798 expectedObject: &testDecodeCoercion{Map: map[string]string{}},
799 strict: true,
800 },
801 {
802 data: []byte(`{"map":{},"struct":{}}`),
803 into: &testDecodeCoercion{},
804 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
805 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
806 expectedObject: &testDecodeCoercion{Map: map[string]string{}},
807 yaml: true,
808 strict: true,
809 },
810
811 {
812 data: []byte(`{"int":1,"int32":1,"int64":1,"float32":1,"float64":1}`),
813 into: &testDecodeCoercion{},
814 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
815 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
816 expectedObject: &testDecodeCoercion{Int: 1, Int32: 1, Int64: 1, Float32: 1, Float64: 1},
817 strict: true,
818 },
819 {
820 data: []byte(`{"int":1,"int32":1,"int64":1,"float32":1,"float64":1}`),
821 into: &testDecodeCoercion{},
822 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
823 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
824 expectedObject: &testDecodeCoercion{Int: 1, Int32: 1, Int64: 1, Float32: 1, Float64: 1},
825 yaml: true,
826 strict: true,
827 },
828
829 {
830 data: []byte(`{"float32":1.0,"float64":1.0}`),
831 into: &testDecodeCoercion{},
832 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
833 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
834 expectedObject: &testDecodeCoercion{Float32: 1, Float64: 1},
835 strict: true,
836 },
837 {
838 data: []byte(`{"int":1.0,"int32":1.0,"int64":1.0,"float32":1.0,"float64":1.0}`),
839 into: &testDecodeCoercion{},
840 typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
841 expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
842 expectedObject: &testDecodeCoercion{Int: 1, Int32: 1, Int64: 1, Float32: 1, Float64: 1},
843 yaml: true,
844 strict: true,
845 },
846 }
847
848 logTestCase := func(t *testing.T, tc testCase) {
849 t.Logf("data=%s\n\tinto=%T, yaml=%v, strict=%v", string(tc.data), tc.into, tc.yaml, tc.strict)
850 }
851
852 for i, test := range testCases {
853 var s runtime.Serializer
854 if test.yaml {
855 s = json.NewSerializerWithOptions(json.DefaultMetaFactory, test.creater, test.typer, json.SerializerOptions{Yaml: test.yaml, Pretty: false, Strict: test.strict})
856 } else {
857 s = json.NewSerializerWithOptions(json.DefaultMetaFactory, test.creater, test.typer, json.SerializerOptions{Yaml: test.yaml, Pretty: test.pretty, Strict: test.strict})
858 }
859 obj, gvk, err := s.Decode([]byte(test.data), test.defaultGVK, test.into)
860
861 if !reflect.DeepEqual(test.expectedGVK, gvk) {
862 logTestCase(t, test)
863 t.Errorf("%d: unexpected GVK: %v", i, gvk)
864 }
865
866 switch {
867 case err == nil && test.errFn != nil:
868 logTestCase(t, test)
869 t.Errorf("%d: failed: not getting the expected error", i)
870 continue
871 case err != nil && test.errFn == nil:
872 logTestCase(t, test)
873 t.Errorf("%d: failed: %v", i, err)
874 continue
875 case err != nil:
876 if !test.errFn(err) {
877 logTestCase(t, test)
878 t.Errorf("%d: failed: %v", i, err)
879 }
880 if !runtime.IsStrictDecodingError(err) && obj != nil {
881 logTestCase(t, test)
882 t.Errorf("%d: should have returned nil object", i)
883 }
884 continue
885 }
886
887 if test.into != nil && test.into != obj {
888 logTestCase(t, test)
889 t.Errorf("%d: expected into to be returned: %v", i, obj)
890 continue
891 }
892
893 if !reflect.DeepEqual(test.expectedObject, obj) {
894 logTestCase(t, test)
895 t.Errorf("%d: unexpected object:\n%s", i, diff.ObjectGoPrintSideBySide(test.expectedObject, obj))
896 }
897 }
898 }
899
900 func TestCacheableObject(t *testing.T) {
901 gvk := schema.GroupVersionKind{Group: "group", Version: "version", Kind: "MockCacheableObject"}
902 creater := &mockCreater{obj: &runtimetesting.MockCacheableObject{}}
903 typer := &mockTyper{gvk: &gvk}
904 serializer := json.NewSerializer(json.DefaultMetaFactory, creater, typer, false)
905
906 runtimetesting.CacheableObjectTest(t, serializer)
907 }
908
909 type mockCreater struct {
910 apiVersion string
911 kind string
912 err error
913 obj runtime.Object
914 }
915
916 func (c *mockCreater) New(kind schema.GroupVersionKind) (runtime.Object, error) {
917 c.apiVersion, c.kind = kind.GroupVersion().String(), kind.Kind
918 return c.obj, c.err
919 }
920
921 type mockTyper struct {
922 gvk *schema.GroupVersionKind
923 err error
924 }
925
926 func (t *mockTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
927 if t.gvk == nil {
928 return nil, false, t.err
929 }
930 return []schema.GroupVersionKind{*t.gvk}, false, t.err
931 }
932
933 func (t *mockTyper) Recognizes(_ schema.GroupVersionKind) bool {
934 return false
935 }
936
View as plain text