1/* 2Copyright 2019 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package testing 18 19import ( 20 "testing" 21 22 "github.com/stretchr/testify/assert" 23 "k8s.io/apimachinery/pkg/api/meta" 24 "k8s.io/apimachinery/pkg/runtime" 25 "k8s.io/apimachinery/pkg/runtime/schema" 26) 27 28func TestOriginalObjectCaptured(t *testing.T) { 29 // this ReactionFunc sets the resources ClusterName 30 const testClusterName = "some-value" 31 reactors := []ReactionFunc{ 32 func(action Action) (bool, runtime.Object, error) { 33 createAction := action.(CreateActionImpl) 34 accessor, err := meta.Accessor(createAction.Object) 35 if err != nil { 36 return false, nil, err 37 } 38 39 // set any field on the resource 40 accessor.SetClusterName(testClusterName) 41 42 return true, createAction.Object, nil 43 }, 44 } 45 46 // create a new Fake with the test reactors 47 f := &Fake{} 48 for _, r := range reactors { 49 f.AddReactor("", "", r) 50 } 51 52 // construct a test resource 53 testResource := schema.GroupVersionResource{Group: "", Version: "test_version", Resource: "test_kind"} 54 testObj := getArbitraryResource(testResource, "test_name", "test_namespace") 55 56 // create a fake CreateAction 57 action := CreateActionImpl{ 58 Object: testObj, 59 } 60 61 // execute the reaction chain 62 ret, err := f.Invokes(action, nil) 63 assert.NoError(t, err, "running Invokes failed") 64 65 // obtain a metadata accessor for the returned resource 66 accessor, err := meta.Accessor(ret) 67 if err != nil { 68 t.Fatalf("unexpected error: %v", err) 69 } 70 71 // validate that the returned resource was modified by the ReactionFunc 72 if accessor.GetClusterName() != testClusterName { 73 t.Errorf("expected resource returned by Invokes to be modified by the ReactionFunc") 74 } 75 // verify one action was performed 76 if len(f.actions) != 1 { 77 t.Errorf("expected 1 action to be executed") 78 t.FailNow() 79 } 80 // check to ensure the recorded action has not been modified by the chain 81 createAction := f.actions[0].(CreateActionImpl) 82 accessor, err = meta.Accessor(createAction.Object) 83 if err != nil { 84 t.Fatalf("unexpected error: %v", err) 85 } 86 if accessor.GetClusterName() != "" { 87 t.Errorf("expected Action recorded to not be modified by ReactionFunc but it was") 88 } 89} 90 91func TestReactorChangesPersisted(t *testing.T) { 92 // this ReactionFunc sets the resources ClusterName 93 const testClusterName = "some-value" 94 reactors := []ReactionFunc{ 95 func(action Action) (bool, runtime.Object, error) { 96 createAction := action.(CreateActionImpl) 97 accessor, err := meta.Accessor(createAction.Object) 98 if err != nil { 99 return false, nil, err 100 } 101 102 // set any field on the resource 103 accessor.SetClusterName(testClusterName) 104 105 return false, createAction.Object, nil 106 }, 107 func(action Action) (bool, runtime.Object, error) { 108 createAction := action.(CreateActionImpl) 109 accessor, err := meta.Accessor(createAction.Object) 110 if err != nil { 111 return false, nil, err 112 } 113 114 // ensure the clusterName is set to testClusterName already 115 if accessor.GetClusterName() != testClusterName { 116 t.Errorf("expected resource passed to second reactor to be modified by first reactor") 117 } 118 119 return true, createAction.Object, nil 120 }, 121 } 122 123 // create a new Fake with the test reactors 124 f := &Fake{} 125 for _, r := range reactors { 126 f.AddReactor("", "", r) 127 } 128 129 // construct a test resource 130 testResource := schema.GroupVersionResource{Group: "", Version: "test_version", Resource: "test_kind"} 131 testObj := getArbitraryResource(testResource, "test_name", "test_namespace") 132 133 // create a fake CreateAction 134 action := CreateActionImpl{ 135 Object: testObj, 136 } 137 138 // execute the reaction chain 139 ret, err := f.Invokes(action, nil) 140 assert.NoError(t, err, "running Invokes failed") 141 142 // obtain a metadata accessor for the returned resource 143 accessor, err := meta.Accessor(ret) 144 if err != nil { 145 t.Fatalf("unexpected error: %v", err) 146 } 147 148 // validate that the returned resource was modified by the ReactionFunc 149 if accessor.GetClusterName() != testClusterName { 150 t.Errorf("expected resource returned by Invokes to be modified by the ReactionFunc") 151 } 152 // verify one action was performed 153 if len(f.actions) != 1 { 154 t.Errorf("expected 1 action to be executed") 155 t.FailNow() 156 } 157 // check to ensure the recorded action has not been modified by the chain 158 createAction := f.actions[0].(CreateActionImpl) 159 accessor, err = meta.Accessor(createAction.Object) 160 if err != nil { 161 t.Fatalf("unexpected error: %v", err) 162 } 163 if accessor.GetClusterName() != "" { 164 t.Errorf("expected Action recorded to not be modified by ReactionFunc but it was") 165 } 166} 167