...

Source file src/k8s.io/client-go/metadata/fake/simple_test.go

Documentation: k8s.io/client-go/metadata/fake

     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  }
   200  

View as plain text