k8s.io/client-go@v0.31.1/metadata/fake/simple_test.go (about) 1 /* 2 Copyright 2018 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 fake 18 19 import ( 20 "context" 21 "fmt" 22 "testing" 23 24 "github.com/google/go-cmp/cmp" 25 "k8s.io/apimachinery/pkg/api/equality" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/runtime" 28 "k8s.io/apimachinery/pkg/runtime/schema" 29 "k8s.io/apimachinery/pkg/types" 30 ) 31 32 const ( 33 testGroup = "testgroup" 34 testVersion = "testversion" 35 testResource = "testkinds" 36 testNamespace = "testns" 37 testName = "testname" 38 testKind = "TestKind" 39 testAPIVersion = "testgroup/testversion" 40 ) 41 42 func newPartialObjectMetadata(apiVersion, kind, namespace, name string) *metav1.PartialObjectMetadata { 43 return &metav1.PartialObjectMetadata{ 44 TypeMeta: metav1.TypeMeta{ 45 APIVersion: apiVersion, 46 Kind: kind, 47 }, 48 ObjectMeta: metav1.ObjectMeta{ 49 Namespace: namespace, 50 Name: name, 51 }, 52 } 53 } 54 55 func newPartialObjectMetadataWithAnnotations(annotations map[string]string) *metav1.PartialObjectMetadata { 56 u := newPartialObjectMetadata(testAPIVersion, testKind, testNamespace, testName) 57 u.Annotations = annotations 58 return u 59 } 60 61 func TestList(t *testing.T) { 62 scheme := NewTestScheme() 63 metav1.AddMetaToScheme(scheme) 64 client := NewSimpleMetadataClient(scheme, 65 newPartialObjectMetadata("group/version", "TheKind", "ns-foo", "name-foo"), 66 newPartialObjectMetadata("group2/version", "TheKind", "ns-foo", "name2-foo"), 67 newPartialObjectMetadata("group/version", "TheKind", "ns-foo", "name-bar"), 68 newPartialObjectMetadata("group/version", "TheKind", "ns-foo", "name-baz"), 69 newPartialObjectMetadata("group2/version", "TheKind", "ns-foo", "name2-baz"), 70 ) 71 listFirst, err := client.Resource(schema.GroupVersionResource{Group: "group", Version: "version", Resource: "thekinds"}).List(context.TODO(), metav1.ListOptions{}) 72 if err != nil { 73 t.Fatal(err) 74 } 75 76 expected := []metav1.PartialObjectMetadata{ 77 *newPartialObjectMetadata("group/version", "TheKind", "ns-foo", "name-bar"), 78 *newPartialObjectMetadata("group/version", "TheKind", "ns-foo", "name-baz"), 79 *newPartialObjectMetadata("group/version", "TheKind", "ns-foo", "name-foo"), 80 } 81 if !equality.Semantic.DeepEqual(listFirst.Items, expected) { 82 t.Fatal(cmp.Diff(expected, listFirst.Items)) 83 } 84 } 85 86 type patchTestCase struct { 87 name string 88 object runtime.Object 89 patchType types.PatchType 90 patchBytes []byte 91 wantErrMsg string 92 expectedPatchedObject runtime.Object 93 } 94 95 func (tc *patchTestCase) runner(t *testing.T) { 96 scheme := NewTestScheme() 97 metav1.AddMetaToScheme(scheme) 98 client := NewSimpleMetadataClient(scheme, tc.object) 99 resourceInterface := client.Resource(schema.GroupVersionResource{Group: testGroup, Version: testVersion, Resource: testResource}).Namespace(testNamespace) 100 101 got, recErr := resourceInterface.Patch(context.TODO(), testName, tc.patchType, tc.patchBytes, metav1.PatchOptions{}) 102 103 if err := tc.verifyErr(recErr); err != nil { 104 t.Error(err) 105 } 106 107 if err := tc.verifyResult(got); err != nil { 108 t.Error(err) 109 } 110 111 } 112 113 // verifyErr verifies that the given error returned from Patch is the error 114 // expected by the test case. 115 func (tc *patchTestCase) verifyErr(err error) error { 116 if tc.wantErrMsg != "" && err == nil { 117 return fmt.Errorf("want error, got nil") 118 } 119 120 if tc.wantErrMsg == "" && err != nil { 121 return fmt.Errorf("want no error, got %v", err) 122 } 123 124 if err != nil { 125 if want, got := tc.wantErrMsg, err.Error(); want != got { 126 return fmt.Errorf("incorrect error: want: %q got: %q", want, got) 127 } 128 } 129 return nil 130 } 131 132 func (tc *patchTestCase) verifyResult(result *metav1.PartialObjectMetadata) error { 133 if tc.expectedPatchedObject == nil && result == nil { 134 return nil 135 } 136 if !equality.Semantic.DeepEqual(result, tc.expectedPatchedObject) { 137 return fmt.Errorf("unexpected diff in received object: %s", cmp.Diff(tc.expectedPatchedObject, result)) 138 } 139 return nil 140 } 141 142 func TestPatch(t *testing.T) { 143 testCases := []patchTestCase{ 144 { 145 name: "jsonpatch fails with merge type", 146 object: newPartialObjectMetadataWithAnnotations(map[string]string{"foo": "bar"}), 147 patchType: types.StrategicMergePatchType, 148 patchBytes: []byte(`[]`), 149 wantErrMsg: "invalid JSON document", 150 }, { 151 name: "jsonpatch works with empty patch", 152 object: newPartialObjectMetadataWithAnnotations(map[string]string{"foo": "bar"}), 153 patchType: types.JSONPatchType, 154 // No-op 155 patchBytes: []byte(`[]`), 156 expectedPatchedObject: newPartialObjectMetadataWithAnnotations(map[string]string{"foo": "bar"}), 157 }, { 158 name: "jsonpatch works with simple change patch", 159 object: newPartialObjectMetadataWithAnnotations(map[string]string{"foo": "bar"}), 160 patchType: types.JSONPatchType, 161 // change spec.foo from bar to foobar 162 patchBytes: []byte(`[{"op": "replace", "path": "/metadata/annotations/foo", "value": "foobar"}]`), 163 expectedPatchedObject: newPartialObjectMetadataWithAnnotations(map[string]string{"foo": "foobar"}), 164 }, { 165 name: "jsonpatch works with simple addition", 166 object: newPartialObjectMetadataWithAnnotations(map[string]string{"foo": "bar"}), 167 patchType: types.JSONPatchType, 168 // add spec.newvalue = dummy 169 patchBytes: []byte(`[{"op": "add", "path": "/metadata/annotations/newvalue", "value": "dummy"}]`), 170 expectedPatchedObject: newPartialObjectMetadataWithAnnotations(map[string]string{"foo": "bar", "newvalue": "dummy"}), 171 }, { 172 name: "jsonpatch works with simple deletion", 173 object: newPartialObjectMetadataWithAnnotations(map[string]string{"foo": "bar", "toremove": "shouldnotbehere"}), 174 patchType: types.JSONPatchType, 175 // remove spec.newvalue = dummy 176 patchBytes: []byte(`[{"op": "remove", "path": "/metadata/annotations/toremove"}]`), 177 expectedPatchedObject: newPartialObjectMetadataWithAnnotations(map[string]string{"foo": "bar"}), 178 }, { 179 name: "strategic merge patch fails with JSONPatch", 180 object: newPartialObjectMetadataWithAnnotations(map[string]string{"foo": "bar"}), 181 patchType: types.StrategicMergePatchType, 182 // add spec.newvalue = dummy 183 patchBytes: []byte(`[{"op": "add", "path": "/metadata/annotations/newvalue", "value": "dummy"}]`), 184 wantErrMsg: "invalid JSON document", 185 }, { 186 name: "merge patch works with simple replacement", 187 object: newPartialObjectMetadataWithAnnotations(map[string]string{"foo": "bar"}), 188 patchType: types.MergePatchType, 189 patchBytes: []byte(`{ "metadata": {"annotations": { "foo": "baz" } } }`), 190 expectedPatchedObject: newPartialObjectMetadataWithAnnotations(map[string]string{"foo": "baz"}), 191 }, 192 // TODO: Add tests for strategic merge using v1.Pod for example to ensure the test cases 193 // demonstrate expected use cases. 194 } 195 196 for _, tc := range testCases { 197 t.Run(tc.name, tc.runner) 198 } 199 }