sigs.k8s.io/cluster-api@v1.7.1/exp/runtime/catalog/test/catalog_test.go (about) 1 /* 2 Copyright 2022 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 test contains catalog tests 18 // Note: They have to be outside the catalog package to be realistic. Otherwise using 19 // test types with different versions would result in a cyclic dependency and thus 20 // wouldn't be possible. 21 package test 22 23 import ( 24 "testing" 25 26 . "github.com/onsi/gomega" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/runtime" 29 "k8s.io/apimachinery/pkg/runtime/schema" 30 31 runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog" 32 "sigs.k8s.io/cluster-api/internal/runtime/test/v1alpha1" 33 "sigs.k8s.io/cluster-api/internal/runtime/test/v1alpha2" 34 ) 35 36 var c = runtimecatalog.New() 37 38 func init() { 39 _ = v1alpha1.AddToCatalog(c) 40 _ = v1alpha2.AddToCatalog(c) 41 } 42 43 func TestCatalog(t *testing.T) { 44 g := NewWithT(t) 45 46 verify := func(hook runtimecatalog.Hook, expectedGV schema.GroupVersion) { 47 // Test GroupVersionHook 48 hookGVH, err := c.GroupVersionHook(hook) 49 g.Expect(err).ToNot(HaveOccurred()) 50 g.Expect(hookGVH.GroupVersion()).To(BeComparableTo(expectedGV)) 51 g.Expect(hookGVH.Hook).To(Equal("FakeHook")) 52 53 // Test Request 54 requestGVK, err := c.Request(hookGVH) 55 g.Expect(err).ToNot(HaveOccurred()) 56 g.Expect(requestGVK.GroupVersion()).To(BeComparableTo(expectedGV)) 57 g.Expect(requestGVK.Kind).To(Equal("FakeRequest")) 58 59 // Test Response 60 responseGVK, err := c.Response(hookGVH) 61 g.Expect(err).ToNot(HaveOccurred()) 62 g.Expect(responseGVK.GroupVersion()).To(BeComparableTo(expectedGV)) 63 g.Expect(responseGVK.Kind).To(Equal("FakeResponse")) 64 65 // Test NewRequest 66 request, err := c.NewRequest(hookGVH) 67 g.Expect(err).ToNot(HaveOccurred()) 68 69 // Test NewResponse 70 response, err := c.NewResponse(hookGVH) 71 g.Expect(err).ToNot(HaveOccurred()) 72 73 // Test ValidateRequest/ValidateResponse 74 g.Expect(c.ValidateRequest(hookGVH, request)).To(Succeed()) 75 g.Expect(c.ValidateResponse(hookGVH, response)).To(Succeed()) 76 } 77 78 verify(v1alpha1.FakeHook, v1alpha1.GroupVersion) 79 verify(v1alpha2.FakeHook, v1alpha2.GroupVersion) 80 } 81 82 func TestValidateRequest(t *testing.T) { 83 v1alpha1Hook, err := c.GroupVersionHook(v1alpha1.FakeHook) 84 if err != nil { 85 panic("failed to get GVH of hook") 86 } 87 v1alpha1HookRequest, err := c.NewRequest(v1alpha1Hook) 88 if err != nil { 89 panic("failed to create request for hook") 90 } 91 92 v1alpha2Hook, err := c.GroupVersionHook(v1alpha2.FakeHook) 93 if err != nil { 94 panic("failed to get GVH of hook") 95 } 96 97 tests := []struct { 98 name string 99 hook runtimecatalog.GroupVersionHook 100 request runtime.Object 101 wantError bool 102 }{ 103 { 104 name: "should succeed when hook and request match", 105 hook: v1alpha1Hook, 106 request: v1alpha1HookRequest, 107 wantError: false, 108 }, 109 { 110 name: "should error when hook and request do not match", 111 hook: v1alpha2Hook, 112 request: v1alpha1HookRequest, 113 wantError: true, 114 }, 115 } 116 117 for _, tt := range tests { 118 t.Run(tt.name, func(t *testing.T) { 119 g := NewWithT(t) 120 err := c.ValidateRequest(tt.hook, tt.request) 121 if tt.wantError { 122 g.Expect(err).To(HaveOccurred()) 123 } else { 124 g.Expect(err).ToNot(HaveOccurred()) 125 } 126 }) 127 } 128 } 129 130 func TestValidateResponse(t *testing.T) { 131 v1alpha1Hook, err := c.GroupVersionHook(v1alpha1.FakeHook) 132 if err != nil { 133 panic("failed to get GVH of hook") 134 } 135 v1alpha1HookResponse, err := c.NewResponse(v1alpha1Hook) 136 if err != nil { 137 panic("failed to create request for hook") 138 } 139 140 v1alpha2Hook, err := c.GroupVersionHook(v1alpha2.FakeHook) 141 if err != nil { 142 panic("failed to get GVH of hook") 143 } 144 145 tests := []struct { 146 name string 147 hook runtimecatalog.GroupVersionHook 148 response runtime.Object 149 wantError bool 150 }{ 151 { 152 name: "should succeed when hook and response match", 153 hook: v1alpha1Hook, 154 response: v1alpha1HookResponse, 155 wantError: false, 156 }, 157 { 158 name: "should error when hook and response do not match", 159 hook: v1alpha2Hook, 160 response: v1alpha1HookResponse, 161 wantError: true, 162 }, 163 } 164 165 for _, tt := range tests { 166 t.Run(tt.name, func(t *testing.T) { 167 g := NewWithT(t) 168 err := c.ValidateResponse(tt.hook, tt.response) 169 if tt.wantError { 170 g.Expect(err).To(HaveOccurred()) 171 } else { 172 g.Expect(err).ToNot(HaveOccurred()) 173 } 174 }) 175 } 176 } 177 178 func TestHookName(t *testing.T) { 179 g := NewWithT(t) 180 expected := "FakeHook" 181 actual := runtimecatalog.HookName(v1alpha1.FakeHook) 182 g.Expect(actual).To(Equal(expected)) 183 } 184 185 type GoodRequest struct { 186 metav1.TypeMeta `json:",inline"` 187 188 First string `json:"first"` 189 } 190 191 func (in *GoodRequest) DeepCopyObject() runtime.Object { 192 panic("implement me!") 193 } 194 195 // BadRequest does not implement runtime.Object interface (missing DeepCopyObject function). 196 type BadRequest struct { 197 metav1.TypeMeta `json:",inline"` 198 199 First string `json:"first"` 200 } 201 202 type GoodResponse struct { 203 metav1.TypeMeta `json:",inline"` 204 205 First string `json:"first"` 206 } 207 208 func (out *GoodResponse) DeepCopyObject() runtime.Object { 209 panic("implement me!") 210 } 211 212 // BadResponse does not implement runtime.Object interface (missing DeepCopyObject function). 213 type BadResponse struct { 214 metav1.TypeMeta `json:",inline"` 215 216 First string `json:"first"` 217 } 218 219 func GoodHook(*GoodRequest, *GoodResponse) {} 220 221 func HookWithReturn(*GoodRequest) *GoodResponse { return nil } 222 223 func HookWithNoInputs() {} 224 225 func HookWithThreeInputs(*GoodRequest, *GoodRequest, *GoodResponse) {} 226 227 func HookWithBadRequestAndResponse(*BadRequest, *BadResponse) {} 228 229 func TestAddHook(t *testing.T) { 230 c := runtimecatalog.New() 231 232 tests := []struct { 233 name string 234 hook runtimecatalog.Hook 235 hookMeta *runtimecatalog.HookMeta 236 wantPanic bool 237 }{ 238 { 239 name: "should pass for valid hook", 240 hook: GoodHook, 241 hookMeta: &runtimecatalog.HookMeta{}, 242 wantPanic: false, 243 }, 244 { 245 name: "should fail for hook with a return value", 246 hook: HookWithReturn, 247 hookMeta: &runtimecatalog.HookMeta{}, 248 wantPanic: true, 249 }, 250 { 251 name: "should fail for a hook with no inputs", 252 hook: HookWithNoInputs, 253 hookMeta: &runtimecatalog.HookMeta{}, 254 wantPanic: true, 255 }, 256 { 257 name: "should fail for a hook with more than two arguments", 258 hook: HookWithThreeInputs, 259 hookMeta: &runtimecatalog.HookMeta{}, 260 wantPanic: true, 261 }, 262 { 263 name: "should fail for hook with bad request and response arguments", 264 hook: HookWithBadRequestAndResponse, 265 hookMeta: &runtimecatalog.HookMeta{}, 266 wantPanic: true, 267 }, 268 { 269 name: "should fail if the hookMeta is nil", 270 hook: GoodHook, 271 hookMeta: nil, 272 wantPanic: true, 273 }, 274 } 275 276 for _, tt := range tests { 277 t.Run(tt.name, func(t *testing.T) { 278 g := NewWithT(t) 279 testFunc := func() { 280 c.AddHook(v1alpha1.GroupVersion, tt.hook, tt.hookMeta) 281 } 282 if tt.wantPanic { 283 g.Expect(testFunc).Should(Panic()) 284 } else { 285 g.Expect(testFunc).ShouldNot(Panic()) 286 } 287 }) 288 } 289 }