1
16
17 package dynamic
18
19 import (
20 "bytes"
21 "context"
22 "fmt"
23 "io"
24 "net/http"
25 "net/http/httptest"
26 "reflect"
27 "strings"
28 "testing"
29
30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
32 "k8s.io/apimachinery/pkg/runtime"
33 "k8s.io/apimachinery/pkg/runtime/schema"
34 "k8s.io/apimachinery/pkg/runtime/serializer/streaming"
35 "k8s.io/apimachinery/pkg/types"
36 "k8s.io/apimachinery/pkg/watch"
37 restclient "k8s.io/client-go/rest"
38 restclientwatch "k8s.io/client-go/rest/watch"
39 )
40
41 func getJSON(version, kind, name string) []byte {
42 return []byte(fmt.Sprintf(`{"apiVersion": %q, "kind": %q, "metadata": {"name": %q}}`, version, kind, name))
43 }
44
45 func getListJSON(version, kind string, items ...[]byte) []byte {
46 json := fmt.Sprintf(`{"apiVersion": %q, "kind": %q, "items": [%s]}`,
47 version, kind, bytes.Join(items, []byte(",")))
48 return []byte(json)
49 }
50
51 func getObject(version, kind, name string) *unstructured.Unstructured {
52 return &unstructured.Unstructured{
53 Object: map[string]interface{}{
54 "apiVersion": version,
55 "kind": kind,
56 "metadata": map[string]interface{}{
57 "name": name,
58 },
59 },
60 }
61 }
62
63 func getClientServer(h func(http.ResponseWriter, *http.Request)) (Interface, *httptest.Server, error) {
64 srv := httptest.NewServer(http.HandlerFunc(h))
65 cl, err := NewForConfig(&restclient.Config{
66 Host: srv.URL,
67 })
68 if err != nil {
69 srv.Close()
70 return nil, nil, err
71 }
72 return cl, srv, nil
73 }
74
75 func TestList(t *testing.T) {
76 tcs := []struct {
77 name string
78 namespace string
79 path string
80 resp []byte
81 want *unstructured.UnstructuredList
82 }{
83 {
84 name: "normal_list",
85 path: "/apis/gtest/vtest/rtest",
86 resp: getListJSON("vTest", "rTestList",
87 getJSON("vTest", "rTest", "item1"),
88 getJSON("vTest", "rTest", "item2")),
89 want: &unstructured.UnstructuredList{
90 Object: map[string]interface{}{
91 "apiVersion": "vTest",
92 "kind": "rTestList",
93 },
94 Items: []unstructured.Unstructured{
95 *getObject("vTest", "rTest", "item1"),
96 *getObject("vTest", "rTest", "item2"),
97 },
98 },
99 },
100 {
101 name: "namespaced_list",
102 namespace: "nstest",
103 path: "/apis/gtest/vtest/namespaces/nstest/rtest",
104 resp: getListJSON("vTest", "rTestList",
105 getJSON("vTest", "rTest", "item1"),
106 getJSON("vTest", "rTest", "item2")),
107 want: &unstructured.UnstructuredList{
108 Object: map[string]interface{}{
109 "apiVersion": "vTest",
110 "kind": "rTestList",
111 },
112 Items: []unstructured.Unstructured{
113 *getObject("vTest", "rTest", "item1"),
114 *getObject("vTest", "rTest", "item2"),
115 },
116 },
117 },
118 }
119 for _, tc := range tcs {
120 resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: "rtest"}
121 cl, srv, err := getClientServer(func(w http.ResponseWriter, r *http.Request) {
122 if r.Method != "GET" {
123 t.Errorf("List(%q) got HTTP method %s. wanted GET", tc.name, r.Method)
124 }
125
126 if r.URL.Path != tc.path {
127 t.Errorf("List(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path)
128 }
129
130 w.Header().Set("Content-Type", runtime.ContentTypeJSON)
131 w.Write(tc.resp)
132 })
133 if err != nil {
134 t.Errorf("unexpected error when creating client: %v", err)
135 continue
136 }
137 defer srv.Close()
138
139 got, err := cl.Resource(resource).Namespace(tc.namespace).List(context.TODO(), metav1.ListOptions{})
140 if err != nil {
141 t.Errorf("unexpected error when listing %q: %v", tc.name, err)
142 continue
143 }
144
145 if !reflect.DeepEqual(got, tc.want) {
146 t.Errorf("List(%q) want: %v\ngot: %v", tc.name, tc.want, got)
147 }
148 }
149 }
150
151 func TestGet(t *testing.T) {
152 tcs := []struct {
153 resource string
154 subresource []string
155 namespace string
156 name string
157 path string
158 resp []byte
159 want *unstructured.Unstructured
160 }{
161 {
162 resource: "rtest",
163 name: "normal_get",
164 path: "/apis/gtest/vtest/rtest/normal_get",
165 resp: getJSON("vTest", "rTest", "normal_get"),
166 want: getObject("vTest", "rTest", "normal_get"),
167 },
168 {
169 resource: "rtest",
170 namespace: "nstest",
171 name: "namespaced_get",
172 path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_get",
173 resp: getJSON("vTest", "rTest", "namespaced_get"),
174 want: getObject("vTest", "rTest", "namespaced_get"),
175 },
176 {
177 resource: "rtest",
178 subresource: []string{"srtest"},
179 name: "normal_subresource_get",
180 path: "/apis/gtest/vtest/rtest/normal_subresource_get/srtest",
181 resp: getJSON("vTest", "srTest", "normal_subresource_get"),
182 want: getObject("vTest", "srTest", "normal_subresource_get"),
183 },
184 {
185 resource: "rtest",
186 subresource: []string{"srtest"},
187 namespace: "nstest",
188 name: "namespaced_subresource_get",
189 path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_get/srtest",
190 resp: getJSON("vTest", "srTest", "namespaced_subresource_get"),
191 want: getObject("vTest", "srTest", "namespaced_subresource_get"),
192 },
193 }
194 for _, tc := range tcs {
195 resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: tc.resource}
196 cl, srv, err := getClientServer(func(w http.ResponseWriter, r *http.Request) {
197 if r.Method != "GET" {
198 t.Errorf("Get(%q) got HTTP method %s. wanted GET", tc.name, r.Method)
199 }
200
201 if r.URL.Path != tc.path {
202 t.Errorf("Get(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path)
203 }
204
205 w.Header().Set("Content-Type", runtime.ContentTypeJSON)
206 w.Write(tc.resp)
207 })
208 if err != nil {
209 t.Errorf("unexpected error when creating client: %v", err)
210 continue
211 }
212 defer srv.Close()
213
214 got, err := cl.Resource(resource).Namespace(tc.namespace).Get(context.TODO(), tc.name, metav1.GetOptions{}, tc.subresource...)
215 if err != nil {
216 t.Errorf("unexpected error when getting %q: %v", tc.name, err)
217 continue
218 }
219
220 if !reflect.DeepEqual(got, tc.want) {
221 t.Errorf("Get(%q) want: %v\ngot: %v", tc.name, tc.want, got)
222 }
223 }
224 }
225
226 func TestDelete(t *testing.T) {
227 background := metav1.DeletePropagationBackground
228 uid := types.UID("uid")
229
230 statusOK := &metav1.Status{
231 TypeMeta: metav1.TypeMeta{Kind: "Status"},
232 Status: metav1.StatusSuccess,
233 }
234 tcs := []struct {
235 subresource []string
236 namespace string
237 name string
238 path string
239 deleteOptions metav1.DeleteOptions
240 }{
241 {
242 name: "normal_delete",
243 path: "/apis/gtest/vtest/rtest/normal_delete",
244 },
245 {
246 namespace: "nstest",
247 name: "namespaced_delete",
248 path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_delete",
249 },
250 {
251 subresource: []string{"srtest"},
252 name: "normal_delete",
253 path: "/apis/gtest/vtest/rtest/normal_delete/srtest",
254 },
255 {
256 subresource: []string{"srtest"},
257 namespace: "nstest",
258 name: "namespaced_delete",
259 path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_delete/srtest",
260 },
261 {
262 namespace: "nstest",
263 name: "namespaced_delete_with_options",
264 path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_delete_with_options",
265 deleteOptions: metav1.DeleteOptions{Preconditions: &metav1.Preconditions{UID: &uid}, PropagationPolicy: &background},
266 },
267 }
268 for _, tc := range tcs {
269 resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: "rtest"}
270 cl, srv, err := getClientServer(func(w http.ResponseWriter, r *http.Request) {
271 if r.Method != "DELETE" {
272 t.Errorf("Delete(%q) got HTTP method %s. wanted DELETE", tc.name, r.Method)
273 }
274
275 if r.URL.Path != tc.path {
276 t.Errorf("Delete(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path)
277 }
278
279 content := r.Header.Get("Content-Type")
280 if content != runtime.ContentTypeJSON {
281 t.Errorf("Delete(%q) got Content-Type %s. wanted %s", tc.name, content, runtime.ContentTypeJSON)
282 }
283
284 w.Header().Set("Content-Type", runtime.ContentTypeJSON)
285 unstructured.UnstructuredJSONScheme.Encode(statusOK, w)
286 })
287 if err != nil {
288 t.Errorf("unexpected error when creating client: %v", err)
289 continue
290 }
291 defer srv.Close()
292
293 err = cl.Resource(resource).Namespace(tc.namespace).Delete(context.TODO(), tc.name, tc.deleteOptions, tc.subresource...)
294 if err != nil {
295 t.Errorf("unexpected error when deleting %q: %v", tc.name, err)
296 continue
297 }
298 }
299 }
300
301 func TestDeleteCollection(t *testing.T) {
302 statusOK := &metav1.Status{
303 TypeMeta: metav1.TypeMeta{Kind: "Status"},
304 Status: metav1.StatusSuccess,
305 }
306 tcs := []struct {
307 namespace string
308 name string
309 path string
310 }{
311 {
312 name: "normal_delete_collection",
313 path: "/apis/gtest/vtest/rtest",
314 },
315 {
316 namespace: "nstest",
317 name: "namespaced_delete_collection",
318 path: "/apis/gtest/vtest/namespaces/nstest/rtest",
319 },
320 }
321 for _, tc := range tcs {
322 resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: "rtest"}
323 cl, srv, err := getClientServer(func(w http.ResponseWriter, r *http.Request) {
324 if r.Method != "DELETE" {
325 t.Errorf("DeleteCollection(%q) got HTTP method %s. wanted DELETE", tc.name, r.Method)
326 }
327
328 if r.URL.Path != tc.path {
329 t.Errorf("DeleteCollection(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path)
330 }
331
332 content := r.Header.Get("Content-Type")
333 if content != runtime.ContentTypeJSON {
334 t.Errorf("DeleteCollection(%q) got Content-Type %s. wanted %s", tc.name, content, runtime.ContentTypeJSON)
335 }
336
337 w.Header().Set("Content-Type", runtime.ContentTypeJSON)
338 unstructured.UnstructuredJSONScheme.Encode(statusOK, w)
339 })
340 if err != nil {
341 t.Errorf("unexpected error when creating client: %v", err)
342 continue
343 }
344 defer srv.Close()
345
346 err = cl.Resource(resource).Namespace(tc.namespace).DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{})
347 if err != nil {
348 t.Errorf("unexpected error when deleting collection %q: %v", tc.name, err)
349 continue
350 }
351 }
352 }
353
354 func TestCreate(t *testing.T) {
355 tcs := []struct {
356 resource string
357 subresource []string
358 name string
359 namespace string
360 obj *unstructured.Unstructured
361 path string
362 }{
363 {
364 resource: "rtest",
365 name: "normal_create",
366 path: "/apis/gtest/vtest/rtest",
367 obj: getObject("gtest/vTest", "rTest", "normal_create"),
368 },
369 {
370 resource: "rtest",
371 name: "namespaced_create",
372 namespace: "nstest",
373 path: "/apis/gtest/vtest/namespaces/nstest/rtest",
374 obj: getObject("gtest/vTest", "rTest", "namespaced_create"),
375 },
376 {
377 resource: "rtest",
378 subresource: []string{"srtest"},
379 name: "normal_subresource_create",
380 path: "/apis/gtest/vtest/rtest/normal_subresource_create/srtest",
381 obj: getObject("vTest", "srTest", "normal_subresource_create"),
382 },
383 {
384 resource: "rtest/",
385 subresource: []string{"srtest"},
386 name: "namespaced_subresource_create",
387 namespace: "nstest",
388 path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_create/srtest",
389 obj: getObject("vTest", "srTest", "namespaced_subresource_create"),
390 },
391 }
392 for _, tc := range tcs {
393 resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: tc.resource}
394 cl, srv, err := getClientServer(func(w http.ResponseWriter, r *http.Request) {
395 if r.Method != "POST" {
396 t.Errorf("Create(%q) got HTTP method %s. wanted POST", tc.name, r.Method)
397 }
398
399 if r.URL.Path != tc.path {
400 t.Errorf("Create(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path)
401 }
402
403 content := r.Header.Get("Content-Type")
404 if content != runtime.ContentTypeJSON {
405 t.Errorf("Create(%q) got Content-Type %s. wanted %s", tc.name, content, runtime.ContentTypeJSON)
406 }
407
408 w.Header().Set("Content-Type", runtime.ContentTypeJSON)
409 data, err := io.ReadAll(r.Body)
410 if err != nil {
411 t.Errorf("Create(%q) unexpected error reading body: %v", tc.name, err)
412 w.WriteHeader(http.StatusInternalServerError)
413 return
414 }
415
416 w.Write(data)
417 })
418 if err != nil {
419 t.Errorf("unexpected error when creating client: %v", err)
420 continue
421 }
422 defer srv.Close()
423
424 got, err := cl.Resource(resource).Namespace(tc.namespace).Create(context.TODO(), tc.obj, metav1.CreateOptions{}, tc.subresource...)
425 if err != nil {
426 t.Errorf("unexpected error when creating %q: %v", tc.name, err)
427 continue
428 }
429
430 if !reflect.DeepEqual(got, tc.obj) {
431 t.Errorf("Create(%q) want: %v\ngot: %v", tc.name, tc.obj, got)
432 }
433 }
434 }
435
436 func TestUpdate(t *testing.T) {
437 tcs := []struct {
438 resource string
439 subresource []string
440 name string
441 namespace string
442 obj *unstructured.Unstructured
443 path string
444 }{
445 {
446 resource: "rtest",
447 name: "normal_update",
448 path: "/apis/gtest/vtest/rtest/normal_update",
449 obj: getObject("gtest/vTest", "rTest", "normal_update"),
450 },
451 {
452 resource: "rtest",
453 name: "namespaced_update",
454 namespace: "nstest",
455 path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_update",
456 obj: getObject("gtest/vTest", "rTest", "namespaced_update"),
457 },
458 {
459 resource: "rtest",
460 subresource: []string{"srtest"},
461 name: "normal_subresource_update",
462 path: "/apis/gtest/vtest/rtest/normal_update/srtest",
463 obj: getObject("gtest/vTest", "srTest", "normal_update"),
464 },
465 {
466 resource: "rtest",
467 subresource: []string{"srtest"},
468 name: "namespaced_subresource_update",
469 namespace: "nstest",
470 path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_update/srtest",
471 obj: getObject("gtest/vTest", "srTest", "namespaced_update"),
472 },
473 }
474 for _, tc := range tcs {
475 resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: tc.resource}
476 cl, srv, err := getClientServer(func(w http.ResponseWriter, r *http.Request) {
477 if r.Method != "PUT" {
478 t.Errorf("Update(%q) got HTTP method %s. wanted PUT", tc.name, r.Method)
479 }
480
481 if r.URL.Path != tc.path {
482 t.Errorf("Update(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path)
483 }
484
485 content := r.Header.Get("Content-Type")
486 if content != runtime.ContentTypeJSON {
487 t.Errorf("Uppdate(%q) got Content-Type %s. wanted %s", tc.name, content, runtime.ContentTypeJSON)
488 }
489
490 w.Header().Set("Content-Type", runtime.ContentTypeJSON)
491 data, err := io.ReadAll(r.Body)
492 if err != nil {
493 t.Errorf("Update(%q) unexpected error reading body: %v", tc.name, err)
494 w.WriteHeader(http.StatusInternalServerError)
495 return
496 }
497
498 w.Write(data)
499 })
500 if err != nil {
501 t.Errorf("unexpected error when creating client: %v", err)
502 continue
503 }
504 defer srv.Close()
505
506 got, err := cl.Resource(resource).Namespace(tc.namespace).Update(context.TODO(), tc.obj, metav1.UpdateOptions{}, tc.subresource...)
507 if err != nil {
508 t.Errorf("unexpected error when updating %q: %v", tc.name, err)
509 continue
510 }
511
512 if !reflect.DeepEqual(got, tc.obj) {
513 t.Errorf("Update(%q) want: %v\ngot: %v", tc.name, tc.obj, got)
514 }
515 }
516 }
517
518 func TestWatch(t *testing.T) {
519 tcs := []struct {
520 name string
521 namespace string
522 events []watch.Event
523 path string
524 query string
525 }{
526 {
527 name: "normal_watch",
528 path: "/apis/gtest/vtest/rtest",
529 query: "watch=true",
530 events: []watch.Event{
531 {Type: watch.Added, Object: getObject("gtest/vTest", "rTest", "normal_watch")},
532 {Type: watch.Modified, Object: getObject("gtest/vTest", "rTest", "normal_watch")},
533 {Type: watch.Deleted, Object: getObject("gtest/vTest", "rTest", "normal_watch")},
534 },
535 },
536 {
537 name: "namespaced_watch",
538 namespace: "nstest",
539 path: "/apis/gtest/vtest/namespaces/nstest/rtest",
540 query: "watch=true",
541 events: []watch.Event{
542 {Type: watch.Added, Object: getObject("gtest/vTest", "rTest", "namespaced_watch")},
543 {Type: watch.Modified, Object: getObject("gtest/vTest", "rTest", "namespaced_watch")},
544 {Type: watch.Deleted, Object: getObject("gtest/vTest", "rTest", "namespaced_watch")},
545 },
546 },
547 }
548 for _, tc := range tcs {
549 resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: "rtest"}
550 cl, srv, err := getClientServer(func(w http.ResponseWriter, r *http.Request) {
551 if r.Method != "GET" {
552 t.Errorf("Watch(%q) got HTTP method %s. wanted GET", tc.name, r.Method)
553 }
554
555 if r.URL.Path != tc.path {
556 t.Errorf("Watch(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path)
557 }
558 if r.URL.RawQuery != tc.query {
559 t.Errorf("Watch(%q) got query %s. wanted %s", tc.name, r.URL.RawQuery, tc.query)
560 }
561
562 w.Header().Set("Content-Type", "application/json")
563
564 enc := restclientwatch.NewEncoder(streaming.NewEncoder(w, unstructured.UnstructuredJSONScheme), unstructured.UnstructuredJSONScheme)
565 for _, e := range tc.events {
566 enc.Encode(&e)
567 }
568 })
569 if err != nil {
570 t.Errorf("unexpected error when creating client: %v", err)
571 continue
572 }
573 defer srv.Close()
574
575 watcher, err := cl.Resource(resource).Namespace(tc.namespace).Watch(context.TODO(), metav1.ListOptions{})
576 if err != nil {
577 t.Errorf("unexpected error when watching %q: %v", tc.name, err)
578 continue
579 }
580
581 for _, want := range tc.events {
582 got := <-watcher.ResultChan()
583 if !reflect.DeepEqual(got, want) {
584 t.Errorf("Watch(%q) want: %v\ngot: %v", tc.name, want, got)
585 }
586 }
587 }
588 }
589
590 func TestPatch(t *testing.T) {
591 tcs := []struct {
592 resource string
593 subresource []string
594 name string
595 namespace string
596 patch []byte
597 want *unstructured.Unstructured
598 path string
599 }{
600 {
601 resource: "rtest",
602 name: "normal_patch",
603 path: "/apis/gtest/vtest/rtest/normal_patch",
604 patch: getJSON("gtest/vTest", "rTest", "normal_patch"),
605 want: getObject("gtest/vTest", "rTest", "normal_patch"),
606 },
607 {
608 resource: "rtest",
609 name: "namespaced_patch",
610 namespace: "nstest",
611 path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_patch",
612 patch: getJSON("gtest/vTest", "rTest", "namespaced_patch"),
613 want: getObject("gtest/vTest", "rTest", "namespaced_patch"),
614 },
615 {
616 resource: "rtest",
617 subresource: []string{"srtest"},
618 name: "normal_subresource_patch",
619 path: "/apis/gtest/vtest/rtest/normal_subresource_patch/srtest",
620 patch: getJSON("gtest/vTest", "srTest", "normal_subresource_patch"),
621 want: getObject("gtest/vTest", "srTest", "normal_subresource_patch"),
622 },
623 {
624 resource: "rtest",
625 subresource: []string{"srtest"},
626 name: "namespaced_subresource_patch",
627 namespace: "nstest",
628 path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_patch/srtest",
629 patch: getJSON("gtest/vTest", "srTest", "namespaced_subresource_patch"),
630 want: getObject("gtest/vTest", "srTest", "namespaced_subresource_patch"),
631 },
632 }
633 for _, tc := range tcs {
634 resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: tc.resource}
635 cl, srv, err := getClientServer(func(w http.ResponseWriter, r *http.Request) {
636 if r.Method != "PATCH" {
637 t.Errorf("Patch(%q) got HTTP method %s. wanted PATCH", tc.name, r.Method)
638 }
639
640 if r.URL.Path != tc.path {
641 t.Errorf("Patch(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path)
642 }
643
644 content := r.Header.Get("Content-Type")
645 if content != string(types.StrategicMergePatchType) {
646 t.Errorf("Patch(%q) got Content-Type %s. wanted %s", tc.name, content, types.StrategicMergePatchType)
647 }
648
649 data, err := io.ReadAll(r.Body)
650 if err != nil {
651 t.Errorf("Patch(%q) unexpected error reading body: %v", tc.name, err)
652 w.WriteHeader(http.StatusInternalServerError)
653 return
654 }
655
656 w.Header().Set("Content-Type", "application/json")
657 w.Write(data)
658 })
659 if err != nil {
660 t.Errorf("unexpected error when creating client: %v", err)
661 continue
662 }
663 defer srv.Close()
664
665 got, err := cl.Resource(resource).Namespace(tc.namespace).Patch(context.TODO(), tc.name, types.StrategicMergePatchType, tc.patch, metav1.PatchOptions{}, tc.subresource...)
666 if err != nil {
667 t.Errorf("unexpected error when patching %q: %v", tc.name, err)
668 continue
669 }
670
671 if !reflect.DeepEqual(got, tc.want) {
672 t.Errorf("Patch(%q) want: %v\ngot: %v", tc.name, tc.want, got)
673 }
674 }
675 }
676
677 func TestInvalidSegments(t *testing.T) {
678 name := "bad/name"
679 namespace := "bad/namespace"
680 resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: "rtest"}
681 obj := &unstructured.Unstructured{
682 Object: map[string]interface{}{
683 "apiVersion": "vtest",
684 "kind": "vkind",
685 "metadata": map[string]interface{}{
686 "name": name,
687 },
688 },
689 }
690 cl, err := NewForConfig(&restclient.Config{
691 Host: "127.0.0.1",
692 })
693 if err != nil {
694 t.Fatalf("Failed to create config: %v", err)
695 }
696
697 _, err = cl.Resource(resource).Namespace(namespace).Create(context.TODO(), obj, metav1.CreateOptions{})
698 if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
699 t.Fatalf("Expected `invalid namespace` error, got: %v", err)
700 }
701
702 _, err = cl.Resource(resource).Update(context.TODO(), obj, metav1.UpdateOptions{})
703 if err == nil || !strings.Contains(err.Error(), "invalid resource name") {
704 t.Fatalf("Expected `invalid resource name` error, got: %v", err)
705 }
706 _, err = cl.Resource(resource).Namespace(namespace).Update(context.TODO(), obj, metav1.UpdateOptions{})
707 if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
708 t.Fatalf("Expected `invalid namespace` error, got: %v", err)
709 }
710
711 _, err = cl.Resource(resource).UpdateStatus(context.TODO(), obj, metav1.UpdateOptions{})
712 if err == nil || !strings.Contains(err.Error(), "invalid resource name") {
713 t.Fatalf("Expected `invalid resource name` error, got: %v", err)
714 }
715 _, err = cl.Resource(resource).Namespace(namespace).UpdateStatus(context.TODO(), obj, metav1.UpdateOptions{})
716 if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
717 t.Fatalf("Expected `invalid namespace` error, got: %v", err)
718 }
719
720 err = cl.Resource(resource).Delete(context.TODO(), name, metav1.DeleteOptions{})
721 if err == nil || !strings.Contains(err.Error(), "invalid resource name") {
722 t.Fatalf("Expected `invalid resource name` error, got: %v", err)
723 }
724 err = cl.Resource(resource).Namespace(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
725 if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
726 t.Fatalf("Expected `invalid namespace` error, got: %v", err)
727 }
728
729 err = cl.Resource(resource).Namespace(namespace).DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{})
730 if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
731 t.Fatalf("Expected `invalid namespace` error, got: %v", err)
732 }
733
734 _, err = cl.Resource(resource).Get(context.TODO(), name, metav1.GetOptions{})
735 if err == nil || !strings.Contains(err.Error(), "invalid resource name") {
736 t.Fatalf("Expected `invalid resource name` error, got: %v", err)
737 }
738 _, err = cl.Resource(resource).Namespace(namespace).Get(context.TODO(), name, metav1.GetOptions{})
739 if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
740 t.Fatalf("Expected `invalid namespace` error, got: %v", err)
741 }
742
743 _, err = cl.Resource(resource).Namespace(namespace).List(context.TODO(), metav1.ListOptions{})
744 if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
745 t.Fatalf("Expected `invalid namespace` error, got: %v", err)
746 }
747
748 _, err = cl.Resource(resource).Namespace(namespace).Watch(context.TODO(), metav1.ListOptions{})
749 if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
750 t.Fatalf("Expected `invalid namespace` error, got: %v", err)
751 }
752
753 _, err = cl.Resource(resource).Patch(context.TODO(), name, types.StrategicMergePatchType, []byte("{}"), metav1.PatchOptions{})
754 if err == nil || !strings.Contains(err.Error(), "invalid resource name") {
755 t.Fatalf("Expected `invalid resource name` error, got: %v", err)
756 }
757 _, err = cl.Resource(resource).Namespace(namespace).Patch(context.TODO(), name, types.StrategicMergePatchType, []byte("{}"), metav1.PatchOptions{})
758 if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
759 t.Fatalf("Expected `invalid namespace` error, got: %v", err)
760 }
761
762 _, err = cl.Resource(resource).Apply(context.TODO(), name, obj, metav1.ApplyOptions{})
763 if err == nil || !strings.Contains(err.Error(), "invalid resource name") {
764 t.Fatalf("Expected `invalid resource name` error, got: %v", err)
765 }
766 _, err = cl.Resource(resource).Namespace(namespace).Apply(context.TODO(), name, obj, metav1.ApplyOptions{})
767 if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
768 t.Fatalf("Expected `invalid namespace` error, got: %v", err)
769 }
770
771 _, err = cl.Resource(resource).ApplyStatus(context.TODO(), name, obj, metav1.ApplyOptions{})
772 if err == nil || !strings.Contains(err.Error(), "invalid resource name") {
773 t.Fatalf("Expected `invalid resource name` error, got: %v", err)
774 }
775 _, err = cl.Resource(resource).Namespace(namespace).ApplyStatus(context.TODO(), name, obj, metav1.ApplyOptions{})
776 if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
777 t.Fatalf("Expected `invalid namespace` error, got: %v", err)
778 }
779 }
780
View as plain text