knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/apis/deprecated_test.go (about) 1 /* 2 Copyright 2019 The Knative 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 apis_test 18 19 import ( 20 "context" 21 "strings" 22 "testing" 23 24 "knative.dev/pkg/apis" 25 "knative.dev/pkg/ptr" 26 . "knative.dev/pkg/testing" 27 ) 28 29 func TestCheckDeprecated(t *testing.T) { 30 testCases := map[string]struct { 31 strict bool 32 obj interface{} 33 wantErrs []string 34 }{ 35 "create strict, string": { 36 strict: true, 37 obj: &InnerDefaultSubSpec{ 38 DeprecatedString: "an error", 39 }, 40 wantErrs: []string{ 41 "must not set", 42 "string", 43 }, 44 }, 45 "create strict, stringptr": { 46 strict: true, 47 obj: &InnerDefaultSubSpec{ 48 DeprecatedStringPtr: ptr.String("test string"), 49 }, 50 wantErrs: []string{ 51 "must not set", 52 "stringPtr", 53 }, 54 }, 55 "create strict, int": { 56 strict: true, 57 obj: &InnerDefaultSubSpec{ 58 DeprecatedInt: 42, 59 }, 60 wantErrs: []string{ 61 "must not set", 62 "int", 63 }, 64 }, 65 "create strict, intptr": { 66 strict: true, 67 obj: &InnerDefaultSubSpec{ 68 DeprecatedIntPtr: ptr.Int64(42), 69 }, 70 wantErrs: []string{ 71 "must not set", 72 "intPtr", 73 }, 74 }, 75 "create strict, map": { 76 strict: true, 77 obj: &InnerDefaultSubSpec{ 78 DeprecatedMap: map[string]string{"hello": "failure"}, 79 }, 80 wantErrs: []string{ 81 "must not set", 82 "map", 83 }, 84 }, 85 "create strict, slice": { 86 strict: true, 87 obj: &InnerDefaultSubSpec{ 88 DeprecatedSlice: []string{"hello", "failure"}, 89 }, 90 wantErrs: []string{ 91 "must not set", 92 "slice", 93 }, 94 }, 95 "create strict, struct": { 96 strict: true, 97 obj: &InnerDefaultSubSpec{ 98 DeprecatedStruct: InnerDefaultStruct{FieldAsString: "not ok"}, 99 }, 100 wantErrs: []string{ 101 "must not set", 102 "struct", 103 }, 104 }, 105 "create strict, structptr": { 106 strict: true, 107 obj: &InnerDefaultSubSpec{ 108 DeprecatedStructPtr: &InnerDefaultStruct{ 109 FieldAsString: "fail", 110 }, 111 }, 112 wantErrs: []string{ 113 "must not set", 114 "structPtr", 115 }, 116 }, 117 "create strict, not json": { 118 strict: true, 119 obj: &InnerDefaultSubSpec{ 120 DeprecatedNotJSON: "fail", 121 }, 122 wantErrs: []string{ 123 "must not set", 124 "DeprecatedNotJSON", 125 }, 126 }, 127 "create strict, inlined": { 128 strict: true, 129 obj: &InnerDefaultSubSpec{ 130 InlinedStruct: InlinedStruct{ 131 DeprecatedField: "fail", 132 }, 133 }, 134 wantErrs: []string{ 135 "must not set", 136 "fieldA", 137 }, 138 }, 139 "create strict, inlined ptr": { 140 strict: true, 141 obj: &InnerDefaultSubSpec{ 142 InlinedPtrStruct: &InlinedPtrStruct{ 143 DeprecatedField: "fail", 144 }, 145 }, 146 wantErrs: []string{ 147 "must not set", 148 "fieldB", 149 }, 150 }, 151 "create strict, inlined nested": { 152 strict: true, 153 obj: &InnerDefaultSubSpec{ 154 InlinedStruct: InlinedStruct{ 155 InlinedPtrStruct: &InlinedPtrStruct{ 156 DeprecatedField: "fail", 157 }, 158 }, 159 }, 160 wantErrs: []string{ 161 "must not set", 162 "fieldB", 163 }, 164 }, 165 "create strict, all errors": { 166 strict: true, 167 obj: &InnerDefaultSubSpec{ 168 DeprecatedString: "an error", 169 DeprecatedStringPtr: ptr.String("test string"), 170 DeprecatedInt: 42, 171 DeprecatedIntPtr: ptr.Int64(42), 172 DeprecatedMap: map[string]string{"hello": "failure"}, 173 DeprecatedSlice: []string{"hello", "failure"}, 174 DeprecatedStruct: InnerDefaultStruct{FieldAsString: "not ok"}, 175 DeprecatedStructPtr: &InnerDefaultStruct{ 176 FieldAsString: "fail", 177 }, 178 InlinedStruct: InlinedStruct{ 179 DeprecatedField: "fail", 180 InlinedPtrStruct: &InlinedPtrStruct{ 181 DeprecatedField: "fail", 182 }, 183 }, 184 }, 185 wantErrs: []string{ 186 "string", 187 "stringPtr", 188 "int", 189 "intPtr", 190 "map", 191 "slice", 192 "struct", 193 "structPtr", 194 "fieldA", 195 "fieldB", 196 }, 197 }, 198 } 199 for n, tc := range testCases { 200 t.Run(n, func(t *testing.T) { 201 ctx := context.Background() 202 if tc.strict { 203 ctx = apis.DisallowDeprecated(ctx) 204 } 205 resp := apis.CheckDeprecated(ctx, tc.obj) 206 207 if len(tc.wantErrs) > 0 { 208 for _, err := range tc.wantErrs { 209 var gotErr string 210 if resp != nil { 211 gotErr = resp.Error() 212 } 213 if !strings.Contains(gotErr, err) { 214 t.Errorf("Expected failure containing %q got %q", err, gotErr) 215 } 216 } 217 } else if resp != nil { 218 t.Errorf("Expected no error, got %q", resp.Error()) 219 } 220 }) 221 } 222 } 223 224 // This test makes sure that errors will flatten the duped error for fieldB. 225 // It comes in on obj.InlinedStruct.InlinedPtrStruct.DeprecatedField and 226 // obj.InlinedPtrStruct.DeprecatedField. 227 func TestCheckDeprecated_Dedupe(t *testing.T) { 228 obj := &InnerDefaultSubSpec{ 229 InlinedStruct: InlinedStruct{ 230 DeprecatedField: "fail", 231 InlinedPtrStruct: &InlinedPtrStruct{ 232 DeprecatedField: "fail", 233 }, 234 }, 235 InlinedPtrStruct: &InlinedPtrStruct{ 236 DeprecatedField: "fail", 237 }, 238 } 239 wantErr := "must not set the field(s): fieldA, fieldB" 240 241 ctx := apis.DisallowDeprecated(context.Background()) 242 resp := apis.CheckDeprecated(ctx, obj) 243 244 gotErr := resp.Error() 245 if gotErr != wantErr { 246 t.Errorf("Expected failure %q got %q", wantErr, gotErr) 247 } 248 } 249 250 func TestCheckDeprecatedUpdate(t *testing.T) { 251 testCases := map[string]struct { 252 strict bool 253 obj interface{} 254 org interface{} 255 wantErrs []string 256 }{ 257 "update strict, intptr": { 258 strict: true, 259 org: &InnerDefaultSubSpec{}, 260 obj: &InnerDefaultSubSpec{ 261 DeprecatedIntPtr: ptr.Int64(42), 262 }, 263 wantErrs: []string{ 264 "must not set", 265 "intPtr", 266 }, 267 }, 268 "update strict, map": { 269 strict: true, 270 org: &InnerDefaultSubSpec{}, 271 obj: &InnerDefaultSubSpec{ 272 DeprecatedMap: map[string]string{"hello": "failure"}, 273 }, 274 275 wantErrs: []string{ 276 "must not set", 277 "map", 278 }, 279 }, 280 "update strict, slice": { 281 strict: true, 282 org: &InnerDefaultSubSpec{}, 283 obj: &InnerDefaultSubSpec{ 284 DeprecatedSlice: []string{"hello", "failure"}, 285 }, 286 wantErrs: []string{ 287 "must not set", 288 "slice", 289 }, 290 }, 291 "update strict, struct": { 292 strict: true, 293 org: &InnerDefaultSubSpec{}, 294 obj: &InnerDefaultSubSpec{ 295 DeprecatedStruct: InnerDefaultStruct{ 296 FieldAsString: "fail", 297 }, 298 }, 299 wantErrs: []string{ 300 "must not set", 301 "struct", 302 }, 303 }, 304 "update strict, structptr": { 305 strict: true, 306 org: &InnerDefaultSubSpec{}, 307 obj: &InnerDefaultSubSpec{ 308 DeprecatedStructPtr: &InnerDefaultStruct{ 309 FieldAsString: "fail", 310 }, 311 }, 312 wantErrs: []string{ 313 "must not set", 314 "structPtr", 315 }, 316 }, 317 "update strict, all errors": { 318 strict: true, 319 org: &InnerDefaultSubSpec{}, 320 obj: &InnerDefaultSubSpec{ 321 DeprecatedString: "an error", 322 DeprecatedStringPtr: ptr.String("test string"), 323 DeprecatedInt: 42, 324 DeprecatedIntPtr: ptr.Int64(42), 325 DeprecatedMap: map[string]string{"hello": "failure"}, 326 DeprecatedSlice: []string{"hello", "failure"}, 327 DeprecatedStruct: InnerDefaultStruct{FieldAsString: "not ok"}, 328 DeprecatedStructPtr: &InnerDefaultStruct{ 329 FieldAsString: "fail", 330 }, 331 InlinedStruct: InlinedStruct{ 332 DeprecatedField: "fail", 333 InlinedPtrStruct: &InlinedPtrStruct{ 334 DeprecatedField: "fail", 335 }, 336 }, 337 }, 338 wantErrs: []string{ 339 "string", 340 "stringPtr", 341 "int", 342 "intPtr", 343 "map", 344 "slice", 345 "struct", 346 "structPtr", 347 "fieldA", 348 "fieldB", 349 }, 350 }, 351 352 "overwrite strict, string": { 353 strict: true, 354 org: &InnerDefaultSubSpec{ 355 DeprecatedString: "original setting.", 356 }, 357 obj: &InnerDefaultSubSpec{ 358 DeprecatedString: "fail setting.", 359 }, 360 wantErrs: []string{ 361 "must not update", 362 "string", 363 }, 364 }, 365 "overwrite strict, stringptr": { 366 strict: true, 367 org: &InnerDefaultSubSpec{ 368 DeprecatedStringPtr: ptr.String("original string"), 369 }, 370 obj: &InnerDefaultSubSpec{ 371 DeprecatedStringPtr: ptr.String("fail string"), 372 }, 373 wantErrs: []string{ 374 "must not update", 375 "stringPtr", 376 }, 377 }, 378 "overwrite strict, int": { 379 strict: true, 380 org: &InnerDefaultSubSpec{ 381 DeprecatedInt: 10, 382 }, 383 obj: &InnerDefaultSubSpec{ 384 DeprecatedInt: 42, 385 }, 386 wantErrs: []string{ 387 "must not update", 388 "int", 389 }, 390 }, 391 "overwrite strict, intptr": { 392 strict: true, 393 org: &InnerDefaultSubSpec{ 394 DeprecatedIntPtr: ptr.Int64(10), 395 }, 396 obj: &InnerDefaultSubSpec{ 397 DeprecatedIntPtr: ptr.Int64(42), 398 }, 399 wantErrs: []string{ 400 "must not update", 401 "intPtr", 402 }, 403 }, 404 "overwrite strict, map": { 405 strict: true, 406 org: &InnerDefaultSubSpec{ 407 DeprecatedMap: map[string]string{"goodbye": "existing"}, 408 }, 409 obj: &InnerDefaultSubSpec{ 410 DeprecatedMap: map[string]string{"hello": "failure"}, 411 }, 412 wantErrs: []string{ 413 "must not update", 414 "map", 415 }, 416 }, 417 "overwrite strict, slice": { 418 strict: true, 419 org: &InnerDefaultSubSpec{ 420 DeprecatedSlice: []string{"hello", "existing"}, 421 }, 422 obj: &InnerDefaultSubSpec{ 423 DeprecatedSlice: []string{"hello", "failure"}, 424 }, 425 wantErrs: []string{ 426 "must not update", 427 "slice", 428 }, 429 }, 430 "overwrite strict, struct": { 431 strict: true, 432 org: &InnerDefaultSubSpec{ 433 DeprecatedStruct: InnerDefaultStruct{ 434 FieldAsString: "original", 435 }, 436 }, 437 obj: &InnerDefaultSubSpec{ 438 DeprecatedStruct: InnerDefaultStruct{ 439 FieldAsString: "fail", 440 }, 441 }, 442 wantErrs: []string{ 443 "must not update", 444 "struct", 445 }, 446 }, 447 "overwrite strict, structptr": { 448 strict: true, 449 org: &InnerDefaultSubSpec{ 450 DeprecatedStructPtr: &InnerDefaultStruct{ 451 FieldAsString: "original", 452 }, 453 }, 454 obj: &InnerDefaultSubSpec{ 455 DeprecatedStructPtr: &InnerDefaultStruct{ 456 FieldAsString: "fail", 457 }, 458 }, 459 wantErrs: []string{ 460 "must not update", 461 "structPtr", 462 }, 463 }, 464 465 "create, not strict": { 466 strict: false, 467 obj: &InnerDefaultSubSpec{ 468 DeprecatedString: "fail setting.", 469 }, 470 }, 471 "update, not strict": { 472 strict: false, 473 org: &InnerDefaultSubSpec{}, 474 obj: &InnerDefaultSubSpec{ 475 DeprecatedString: "it's k", 476 }, 477 }, 478 "overwrite, not strict": { 479 strict: false, 480 org: &InnerDefaultSubSpec{ 481 DeprecatedString: "org", 482 }, 483 obj: &InnerDefaultSubSpec{ 484 DeprecatedString: "it's k", 485 }, 486 }, 487 } 488 for n, tc := range testCases { 489 t.Run(n, func(t *testing.T) { 490 ctx := context.Background() 491 if tc.strict { 492 ctx = apis.DisallowDeprecated(ctx) 493 } 494 resp := apis.CheckDeprecatedUpdate(ctx, tc.obj, tc.org) 495 496 if len(tc.wantErrs) > 0 { 497 for _, err := range tc.wantErrs { 498 var gotErr string 499 if resp != nil { 500 gotErr = resp.Error() 501 } 502 if !strings.Contains(gotErr, err) { 503 t.Errorf("Expected failure containing %q got %q", err, gotErr) 504 } 505 } 506 } else if resp != nil { 507 t.Errorf("Expected no error, got %q", resp.Error()) 508 } 509 }) 510 } 511 }