sigs.k8s.io/cluster-api-provider-azure@v1.17.0/util/webhook/validator_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 webhook 18 19 import ( 20 "testing" 21 22 . "github.com/onsi/gomega" 23 "k8s.io/apimachinery/pkg/util/validation/field" 24 "k8s.io/utils/ptr" 25 ) 26 27 func TestValidateImmutableBoolPtr(t *testing.T) { 28 testPath := field.NewPath("spec", "foo") 29 30 tests := []struct { 31 name string 32 input1 *bool 33 input2 *bool 34 expectedOutput *field.Error 35 }{ 36 { 37 name: "nil", 38 input1: nil, 39 input2: nil, 40 }, 41 { 42 name: "no change", 43 input1: ptr.To(true), 44 input2: ptr.To(true), 45 }, 46 { 47 name: "can't unset", 48 input1: ptr.To(true), 49 input2: nil, 50 expectedOutput: field.Invalid(testPath, nil, unsetMessage), 51 }, 52 { 53 name: "can't set from empty", 54 input1: nil, 55 input2: ptr.To(true), 56 expectedOutput: field.Invalid(testPath, nil, setMessage), 57 }, 58 { 59 name: "can't change", 60 input1: ptr.To(true), 61 input2: ptr.To(false), 62 expectedOutput: field.Invalid(testPath, nil, immutableMessage), 63 }, 64 } 65 for _, tc := range tests { 66 t.Run(tc.name, func(t *testing.T) { 67 g := NewWithT(t) 68 err := ValidateImmutable(testPath, tc.input1, tc.input2) 69 if tc.expectedOutput != nil { 70 g.Expect(err).To(HaveOccurred()) 71 g.Expect(err.Detail).To(Equal(tc.expectedOutput.Detail)) 72 g.Expect(err.Type).To(Equal(tc.expectedOutput.Type)) 73 g.Expect(err.Field).To(Equal(tc.expectedOutput.Field)) 74 } else { 75 g.Expect(err).NotTo(HaveOccurred()) 76 } 77 }) 78 } 79 } 80 81 func TestValidateImmutableString(t *testing.T) { 82 testPath := field.NewPath("spec", "foo") 83 84 tests := []struct { 85 name string 86 input1 string 87 input2 string 88 expectedOutput *field.Error 89 }{ 90 { 91 name: "empty string", 92 input1: "", 93 input2: "", 94 }, 95 { 96 name: "no change", 97 input1: "foo", 98 input2: "foo", 99 }, 100 { 101 name: "can't unset", 102 input1: "foo", 103 input2: "", 104 expectedOutput: field.Invalid(testPath, nil, unsetMessage), 105 }, 106 { 107 name: "can't set from empty", 108 input1: "", 109 input2: "foo", 110 expectedOutput: field.Invalid(testPath, nil, setMessage), 111 }, 112 { 113 name: "can't change", 114 input1: "foo", 115 input2: "bar", 116 expectedOutput: field.Invalid(testPath, nil, immutableMessage), 117 }, 118 } 119 for _, tc := range tests { 120 t.Run(tc.name, func(t *testing.T) { 121 g := NewWithT(t) 122 err := ValidateImmutable(testPath, tc.input1, tc.input2) 123 if tc.expectedOutput != nil { 124 g.Expect(err).To(HaveOccurred()) 125 g.Expect(err.Detail).To(Equal(tc.expectedOutput.Detail)) 126 g.Expect(err.Type).To(Equal(tc.expectedOutput.Type)) 127 g.Expect(err.Field).To(Equal(tc.expectedOutput.Field)) 128 } else { 129 g.Expect(err).NotTo(HaveOccurred()) 130 } 131 }) 132 } 133 } 134 135 func TestValidateImmutableStringPtr(t *testing.T) { 136 testPath := field.NewPath("spec", "foo") 137 138 tests := []struct { 139 name string 140 input1 *string 141 input2 *string 142 expectedOutput *field.Error 143 }{ 144 { 145 name: "nil", 146 input1: nil, 147 input2: nil, 148 }, 149 { 150 name: "no change", 151 input1: ptr.To("foo"), 152 input2: ptr.To("foo"), 153 }, 154 { 155 name: "can't unset", 156 input1: ptr.To("foo"), 157 input2: nil, 158 expectedOutput: field.Invalid(testPath, nil, unsetMessage), 159 }, 160 { 161 name: "can't set from empty", 162 input1: nil, 163 input2: ptr.To("foo"), 164 expectedOutput: field.Invalid(testPath, nil, setMessage), 165 }, 166 { 167 name: "can't change", 168 input1: ptr.To("foo"), 169 input2: ptr.To("bar"), 170 expectedOutput: field.Invalid(testPath, nil, immutableMessage), 171 }, 172 } 173 for _, tc := range tests { 174 t.Run(tc.name, func(t *testing.T) { 175 g := NewWithT(t) 176 err := ValidateImmutable(testPath, tc.input1, tc.input2) 177 if tc.expectedOutput != nil { 178 g.Expect(err).To(HaveOccurred()) 179 g.Expect(err.Detail).To(Equal(tc.expectedOutput.Detail)) 180 g.Expect(err.Type).To(Equal(tc.expectedOutput.Type)) 181 g.Expect(err.Field).To(Equal(tc.expectedOutput.Field)) 182 } else { 183 g.Expect(err).NotTo(HaveOccurred()) 184 } 185 }) 186 } 187 } 188 189 func TestValidateImmutableInt32(t *testing.T) { 190 testPath := field.NewPath("spec", "foo") 191 192 tests := []struct { 193 name string 194 input1 int32 195 input2 int32 196 expectedOutput *field.Error 197 }{ 198 { 199 name: "unset", 200 input1: 0, 201 input2: 0, 202 }, 203 { 204 name: "no change", 205 input1: 5, 206 input2: 5, 207 }, 208 { 209 name: "can't unset", 210 input1: 5, 211 input2: 0, 212 expectedOutput: field.Invalid(testPath, nil, unsetMessage), 213 }, 214 { 215 name: "can't set from empty", 216 input1: 0, 217 input2: 5, 218 expectedOutput: field.Invalid(testPath, nil, setMessage), 219 }, 220 { 221 name: "can't change", 222 input1: 5, 223 input2: 6, 224 expectedOutput: field.Invalid(testPath, nil, immutableMessage), 225 }, 226 } 227 for _, tc := range tests { 228 t.Run(tc.name, func(t *testing.T) { 229 g := NewWithT(t) 230 err := ValidateImmutable(testPath, tc.input1, tc.input2) 231 if tc.expectedOutput != nil { 232 g.Expect(err).To(HaveOccurred()) 233 g.Expect(err.Detail).To(Equal(tc.expectedOutput.Detail)) 234 g.Expect(err.Type).To(Equal(tc.expectedOutput.Type)) 235 g.Expect(err.Field).To(Equal(tc.expectedOutput.Field)) 236 } else { 237 g.Expect(err).NotTo(HaveOccurred()) 238 } 239 }) 240 } 241 } 242 243 func TestEnsureStringSlicesAreEquivalent(t *testing.T) { 244 tests := []struct { 245 name string 246 input1 []string 247 input2 []string 248 expectedOutput bool 249 }{ 250 { 251 name: "nil", 252 input1: nil, 253 input2: nil, 254 expectedOutput: true, 255 }, 256 { 257 name: "no change", 258 input1: []string{"foo", "bar"}, 259 input2: []string{"foo", "bar"}, 260 expectedOutput: true, 261 }, 262 { 263 name: "different", 264 input1: []string{"foo", "bar"}, 265 input2: []string{"foo", "foo"}, 266 expectedOutput: false, 267 }, 268 { 269 name: "different order, but equal", 270 input1: []string{"1", "2"}, 271 input2: []string{"2", "1"}, 272 expectedOutput: true, 273 }, 274 { 275 name: "different lengths", 276 input1: []string{"foo"}, 277 input2: []string{"foo", "foo"}, 278 expectedOutput: false, 279 }, 280 { 281 name: "different", 282 input1: []string{"1", "2", "3", "4", "5", "6", "7", "8", "9"}, 283 input2: []string{"1", "2", "3", "4", "5", "7", "8", "9"}, 284 expectedOutput: false, 285 }, 286 { 287 name: "another different variant", 288 input1: []string{"a", "a", "b"}, 289 input2: []string{"a", "b", "b"}, 290 expectedOutput: false, 291 }, 292 } 293 for _, tc := range tests { 294 t.Run(tc.name, func(t *testing.T) { 295 g := NewWithT(t) 296 ret := EnsureStringSlicesAreEquivalent(tc.input1, tc.input2) 297 g.Expect(ret).To(Equal(tc.expectedOutput)) 298 }) 299 } 300 } 301 302 func TestValidateZeroTransitionPtr(t *testing.T) { 303 testPath := field.NewPath("spec", "foo") 304 305 tests := []struct { 306 name string 307 input1 *bool 308 input2 *bool 309 expectedOutput *field.Error 310 }{ 311 { 312 name: "nil", 313 input1: nil, 314 input2: nil, 315 }, 316 { 317 name: "no change", 318 input1: ptr.To(true), 319 input2: ptr.To(true), 320 }, 321 { 322 name: "can unset", 323 input1: ptr.To(true), 324 input2: nil, 325 }, 326 { 327 name: "can't set from empty", 328 input1: nil, 329 input2: ptr.To(true), 330 expectedOutput: field.Invalid(testPath, nil, setMessage), 331 }, 332 { 333 name: "can't change", 334 input1: ptr.To(true), 335 input2: ptr.To(false), 336 expectedOutput: field.Invalid(testPath, nil, immutableMessage), 337 }, 338 } 339 for _, tc := range tests { 340 t.Run(tc.name, func(t *testing.T) { 341 g := NewWithT(t) 342 err := ValidateZeroTransition(testPath, tc.input1, tc.input2) 343 if tc.expectedOutput != nil { 344 g.Expect(err).To(HaveOccurred()) 345 g.Expect(err.Detail).To(Equal(tc.expectedOutput.Detail)) 346 g.Expect(err.Type).To(Equal(tc.expectedOutput.Type)) 347 g.Expect(err.Field).To(Equal(tc.expectedOutput.Field)) 348 } else { 349 g.Expect(err).NotTo(HaveOccurred()) 350 } 351 }) 352 } 353 } 354 355 func TestValidateZeroTransitionString(t *testing.T) { 356 testPath := field.NewPath("spec", "foo") 357 358 tests := []struct { 359 name string 360 input1 string 361 input2 string 362 expectedOutput *field.Error 363 }{ 364 { 365 name: "empty string", 366 input1: "", 367 input2: "", 368 }, 369 { 370 name: "no change", 371 input1: "foo", 372 input2: "foo", 373 }, 374 { 375 name: "can unset", 376 input1: "foo", 377 input2: "", 378 }, 379 { 380 name: "can't set from empty", 381 input1: "", 382 input2: "foo", 383 expectedOutput: field.Invalid(testPath, nil, setMessage), 384 }, 385 { 386 name: "can't change", 387 input1: "foo", 388 input2: "bar", 389 expectedOutput: field.Invalid(testPath, nil, immutableMessage), 390 }, 391 } 392 for _, tc := range tests { 393 t.Run(tc.name, func(t *testing.T) { 394 g := NewWithT(t) 395 err := ValidateZeroTransition(testPath, tc.input1, tc.input2) 396 if tc.expectedOutput != nil { 397 g.Expect(err).To(HaveOccurred()) 398 g.Expect(err.Detail).To(Equal(tc.expectedOutput.Detail)) 399 g.Expect(err.Type).To(Equal(tc.expectedOutput.Type)) 400 g.Expect(err.Field).To(Equal(tc.expectedOutput.Field)) 401 } else { 402 g.Expect(err).NotTo(HaveOccurred()) 403 } 404 }) 405 } 406 } 407 408 func TestValidateZeroTransitionStringPtr(t *testing.T) { 409 testPath := field.NewPath("spec", "foo") 410 411 tests := []struct { 412 name string 413 input1 *string 414 input2 *string 415 expectedOutput *field.Error 416 }{ 417 { 418 name: "nil", 419 input1: nil, 420 input2: nil, 421 }, 422 { 423 name: "no change", 424 input1: ptr.To("foo"), 425 input2: ptr.To("foo"), 426 }, 427 { 428 name: "can unset", 429 input1: ptr.To("foo"), 430 input2: nil, 431 }, 432 { 433 name: "can't set from empty", 434 input1: nil, 435 input2: ptr.To("foo"), 436 expectedOutput: field.Invalid(testPath, nil, setMessage), 437 }, 438 { 439 name: "can't change", 440 input1: ptr.To("foo"), 441 input2: ptr.To("bar"), 442 expectedOutput: field.Invalid(testPath, nil, immutableMessage), 443 }, 444 } 445 for _, tc := range tests { 446 t.Run(tc.name, func(t *testing.T) { 447 g := NewWithT(t) 448 err := ValidateZeroTransition(testPath, tc.input1, tc.input2) 449 if tc.expectedOutput != nil { 450 g.Expect(err).To(HaveOccurred()) 451 g.Expect(err.Detail).To(Equal(tc.expectedOutput.Detail)) 452 g.Expect(err.Type).To(Equal(tc.expectedOutput.Type)) 453 g.Expect(err.Field).To(Equal(tc.expectedOutput.Field)) 454 } else { 455 g.Expect(err).NotTo(HaveOccurred()) 456 } 457 }) 458 } 459 } 460 461 func TestValidateZeroTransitionInt32(t *testing.T) { 462 testPath := field.NewPath("spec", "foo") 463 464 tests := []struct { 465 name string 466 input1 int32 467 input2 int32 468 expectedOutput *field.Error 469 }{ 470 { 471 name: "unset", 472 input1: 0, 473 input2: 0, 474 }, 475 { 476 name: "no change", 477 input1: 5, 478 input2: 5, 479 }, 480 { 481 name: "can unset", 482 input1: 5, 483 input2: 0, 484 }, 485 { 486 name: "can't set from empty", 487 input1: 0, 488 input2: 5, 489 expectedOutput: field.Invalid(testPath, nil, setMessage), 490 }, 491 { 492 name: "can't change", 493 input1: 5, 494 input2: 6, 495 expectedOutput: field.Invalid(testPath, nil, immutableMessage), 496 }, 497 } 498 for _, tc := range tests { 499 t.Run(tc.name, func(t *testing.T) { 500 g := NewWithT(t) 501 err := ValidateZeroTransition(testPath, tc.input1, tc.input2) 502 if tc.expectedOutput != nil { 503 g.Expect(err).To(HaveOccurred()) 504 g.Expect(err.Detail).To(Equal(tc.expectedOutput.Detail)) 505 g.Expect(err.Type).To(Equal(tc.expectedOutput.Type)) 506 g.Expect(err.Field).To(Equal(tc.expectedOutput.Field)) 507 } else { 508 g.Expect(err).NotTo(HaveOccurred()) 509 } 510 }) 511 } 512 }