github.com/oam-dev/kubevela@v1.9.11/pkg/oam/util/helper_test.go (about) 1 /* 2 Copyright 2021 The KubeVela 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 util_test 18 19 import ( 20 "context" 21 "fmt" 22 "hash/adler32" 23 "testing" 24 "time" 25 26 "github.com/crossplane/crossplane-runtime/pkg/test" 27 "github.com/pkg/errors" 28 "github.com/stretchr/testify/assert" 29 apierrors "k8s.io/apimachinery/pkg/api/errors" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 32 "k8s.io/apimachinery/pkg/runtime" 33 "k8s.io/apimachinery/pkg/runtime/schema" 34 "sigs.k8s.io/controller-runtime/pkg/client" 35 36 "github.com/oam-dev/kubevela/apis/core.oam.dev/common" 37 "github.com/oam-dev/kubevela/apis/core.oam.dev/condition" 38 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" 39 "github.com/oam-dev/kubevela/pkg/oam" 40 "github.com/oam-dev/kubevela/pkg/oam/mock" 41 "github.com/oam-dev/kubevela/pkg/oam/util" 42 ) 43 44 func TestUnstructured(t *testing.T) { 45 tests := map[string]struct { 46 u *unstructured.Unstructured 47 typeLabel string 48 exp string 49 resource string 50 }{ 51 "native resource": { 52 u: &unstructured.Unstructured{Object: map[string]interface{}{ 53 "apiVersion": "apps/v1", 54 "kind": "Deployment", 55 }}, 56 resource: "deployments", 57 exp: "deployments.apps", 58 }, 59 "workload": { 60 u: &unstructured.Unstructured{Object: map[string]interface{}{ 61 "apiVersion": "apps/v1", 62 "kind": "Deployment", 63 "metadata": map[string]interface{}{ 64 "labels": map[string]interface{}{ 65 oam.WorkloadTypeLabel: "deploy", 66 }, 67 }, 68 }}, 69 typeLabel: oam.WorkloadTypeLabel, 70 exp: "deploy", 71 }, 72 } 73 for name, ti := range tests { 74 mapper := mock.NewClient(nil, nil).RESTMapper() 75 got, err := util.GetDefinitionName(mapper, ti.u, ti.typeLabel) 76 assert.NoError(t, err) 77 t.Log(fmt.Sprint("Running test: ", name)) 78 assert.Equal(t, ti.exp, got) 79 } 80 } 81 82 func TestGetGVKFromDef(t *testing.T) { 83 mapper := mock.NewClient(nil, map[schema.GroupVersionResource][]schema.GroupVersionKind{ 84 schema.GroupVersionResource{Group: "example.com", Resource: "abcs"}: {{Group: "example.com", Version: "v1", Kind: "Abc"}}, 85 schema.GroupVersionResource{Group: "example.com", Resource: "abcs", Version: "v2"}: {{Group: "example.com", Version: "v2", Kind: "Abc"}}, 86 }).RESTMapper() 87 gvk, err := util.GetGVKFromDefinition(mapper, common.DefinitionReference{Name: "abcs.example.com"}) 88 assert.NoError(t, err) 89 assert.Equal(t, metav1.GroupVersionKind{ 90 Group: "example.com", 91 Version: "v1", 92 Kind: "Abc", 93 }, gvk) 94 95 gvk, err = util.GetGVKFromDefinition(mapper, common.DefinitionReference{Name: "abcs.example.com", Version: "v2"}) 96 assert.NoError(t, err) 97 assert.Equal(t, metav1.GroupVersionKind{ 98 Group: "example.com", 99 Version: "v2", 100 Kind: "Abc", 101 }, gvk) 102 103 gvk, err = util.GetGVKFromDefinition(mapper, common.DefinitionReference{}) 104 assert.NoError(t, err) 105 assert.Equal(t, metav1.GroupVersionKind{ 106 Group: "", 107 Version: "", 108 Kind: "", 109 }, gvk) 110 111 gvk, err = util.GetGVKFromDefinition(mapper, common.DefinitionReference{Name: "dummy"}) 112 assert.NoError(t, err) 113 assert.Equal(t, metav1.GroupVersionKind{ 114 Group: "", 115 Version: "", 116 Kind: "", 117 }, gvk) 118 } 119 120 func TestConvertWorkloadGVK2Def(t *testing.T) { 121 mapper := mock.NewClient(nil, map[schema.GroupVersionResource][]schema.GroupVersionKind{}).RESTMapper() 122 ref, err := util.ConvertWorkloadGVK2Definition(mapper, common.WorkloadGVK{APIVersion: "apps.kruise.io/v1alpha1", 123 Kind: "CloneSet"}) 124 assert.NoError(t, err) 125 assert.Equal(t, common.DefinitionReference{ 126 Name: "clonesets.apps.kruise.io", 127 Version: "v1alpha1", 128 }, ref) 129 130 ref, err = util.ConvertWorkloadGVK2Definition(mapper, common.WorkloadGVK{APIVersion: "apps/v1", 131 Kind: "Deployment"}) 132 assert.NoError(t, err) 133 assert.Equal(t, common.DefinitionReference{ 134 Name: "deployments.apps", 135 Version: "v1", 136 }, ref) 137 138 _, err = util.ConvertWorkloadGVK2Definition(mapper, common.WorkloadGVK{APIVersion: "/apps/v1", 139 Kind: "Deployment"}) 140 assert.Error(t, err) 141 } 142 143 func TestDeepHashObject(t *testing.T) { 144 successCases := []func() interface{}{ 145 func() interface{} { return 8675309 }, 146 func() interface{} { return "Jenny, I got your number" }, 147 func() interface{} { return []string{"eight", "six", "seven"} }, 148 } 149 150 for _, tc := range successCases { 151 hasher1 := adler32.New() 152 util.DeepHashObject(hasher1, tc()) 153 hash1 := hasher1.Sum32() 154 util.DeepHashObject(hasher1, tc()) 155 hash2 := hasher1.Sum32() 156 157 assert.Equal(t, hash1, hash2) 158 } 159 } 160 161 func TestEndReconcileWithNegativeCondition(t *testing.T) { 162 163 var time1, time2 time.Time 164 time1 = time.Now() 165 time2 = time1.Add(time.Second) 166 167 type args struct { 168 ctx context.Context 169 r client.StatusClient 170 workload util.ConditionedObject 171 condition []condition.Condition 172 } 173 patchErr := fmt.Errorf("eww") 174 tests := []struct { 175 name string 176 args args 177 expected error 178 }{ 179 { 180 name: "no condition is added", 181 args: args{ 182 ctx: context.Background(), 183 r: &test.MockClient{ 184 MockStatusPatch: test.NewMockSubResourcePatchFn(nil), 185 }, 186 workload: &mock.Target{}, 187 condition: []condition.Condition{}, 188 }, 189 expected: nil, 190 }, 191 { 192 name: "condition is changed", 193 args: args{ 194 ctx: context.Background(), 195 r: &test.MockClient{ 196 MockStatusPatch: test.NewMockSubResourcePatchFn(nil), 197 }, 198 workload: &mock.Target{ 199 ConditionedStatus: condition.ConditionedStatus{ 200 Conditions: []condition.Condition{ 201 { 202 Type: "test", 203 LastTransitionTime: metav1.NewTime(time1), 204 Reason: "old reason", 205 Message: "old error msg", 206 }, 207 }, 208 }, 209 }, 210 condition: []condition.Condition{ 211 { 212 Type: "test", 213 LastTransitionTime: metav1.NewTime(time2), 214 Reason: "new reason", 215 Message: "new error msg", 216 }, 217 }, 218 }, 219 expected: nil, 220 }, 221 { 222 name: "condition is not changed", 223 args: args{ 224 ctx: context.Background(), 225 r: &test.MockClient{ 226 MockStatusPatch: test.NewMockSubResourcePatchFn(nil), 227 }, 228 workload: &mock.Target{ 229 ConditionedStatus: condition.ConditionedStatus{ 230 Conditions: []condition.Condition{ 231 { 232 Type: "test", 233 LastTransitionTime: metav1.NewTime(time1), 234 Reason: "old reason", 235 Message: "old error msg", 236 }, 237 }, 238 }, 239 }, 240 condition: []condition.Condition{ 241 { 242 Type: "test", 243 LastTransitionTime: metav1.NewTime(time2), 244 Reason: "old reason", 245 Message: "old error msg", 246 }, 247 }, 248 }, 249 expected: fmt.Errorf(util.ErrReconcileErrInCondition, "test", "old error msg"), 250 }, 251 { 252 name: "fail for patching error", 253 args: args{ 254 ctx: context.Background(), 255 r: &test.MockClient{ 256 MockStatusPatch: test.NewMockSubResourcePatchFn(patchErr), 257 }, 258 workload: &mock.Target{}, 259 condition: []condition.Condition{ 260 {}, 261 }, 262 }, 263 expected: errors.Wrap(patchErr, util.ErrUpdateStatus), 264 }, 265 } 266 for _, tt := range tests { 267 err := util.EndReconcileWithNegativeCondition(tt.args.ctx, tt.args.r, tt.args.workload, tt.args.condition...) 268 if tt.expected == nil { 269 assert.NoError(t, err) 270 } else { 271 assert.Equal(t, tt.expected.Error(), err.Error()) 272 } 273 } 274 } 275 276 func TestEndReconcileWithPositiveCondition(t *testing.T) { 277 type args struct { 278 ctx context.Context 279 r client.StatusClient 280 workload util.ConditionedObject 281 condition []condition.Condition 282 } 283 patchErr := fmt.Errorf("eww") 284 tests := []struct { 285 name string 286 args args 287 expected error 288 }{ 289 { 290 name: "success", 291 args: args{ 292 ctx: context.Background(), 293 r: &test.MockClient{ 294 MockStatusPatch: test.NewMockSubResourcePatchFn(nil), 295 }, 296 workload: &mock.Target{}, 297 condition: []condition.Condition{ 298 {}, 299 }, 300 }, 301 expected: nil, 302 }, 303 { 304 name: "fail", 305 args: args{ 306 ctx: context.Background(), 307 r: &test.MockClient{ 308 MockStatusPatch: test.NewMockSubResourcePatchFn(patchErr), 309 }, 310 workload: &mock.Target{}, 311 condition: []condition.Condition{ 312 {}, 313 }, 314 }, 315 expected: errors.Wrap(patchErr, util.ErrUpdateStatus), 316 }, 317 } 318 for _, tt := range tests { 319 err := util.EndReconcileWithPositiveCondition(tt.args.ctx, tt.args.r, tt.args.workload, tt.args.condition...) 320 if tt.expected == nil { 321 assert.NoError(t, err) 322 } else { 323 assert.Equal(t, tt.expected.Error(), err.Error()) 324 } 325 } 326 } 327 328 func TestAddLabels(t *testing.T) { 329 basicLabels := map[string]string{ 330 "basic.label.key": "basic", 331 } 332 obj1 := new(unstructured.Unstructured) 333 wantObj1 := new(unstructured.Unstructured) 334 wantObj1.SetLabels(map[string]string{ 335 "basic.label.key": "basic", 336 "newKey": "newValue", 337 }) 338 obj2 := new(unstructured.Unstructured) 339 wantObj2 := new(unstructured.Unstructured) 340 obj2.SetLabels(map[string]string{ 341 "key": "value", 342 }) 343 wantObj2.SetLabels(map[string]string{ 344 "basic.label.key": "basic", 345 "key": "value", 346 "newKey2": "newValue2", 347 }) 348 349 cases := map[string]struct { 350 obj *unstructured.Unstructured 351 newLabels map[string]string 352 want *unstructured.Unstructured 353 }{ 354 "add labels to workload without labels": { 355 obj1, 356 map[string]string{ 357 "newKey": "newValue", 358 }, 359 wantObj1, 360 }, 361 "add labels to workload with labels": { 362 obj2, 363 map[string]string{ 364 "newKey2": "newValue2", 365 }, 366 wantObj2, 367 }, 368 } 369 370 for name, tc := range cases { 371 t.Log("Running test case: " + name) 372 obj := tc.obj 373 wantObj := tc.want 374 util.AddLabels(obj, basicLabels) 375 util.AddLabels(obj, tc.newLabels) 376 assert.Equal(t, wantObj.GetLabels(), obj.GetLabels()) 377 } 378 } 379 380 func TestMergeMapOverrideWithDst(t *testing.T) { 381 const ( 382 basicKey = "basicKey" 383 dstKey = "dstKey" 384 srcKey = "srcKey" 385 basicValue = "basicValue" 386 dstValue = "dstValue" 387 srcValue = "srcValue" 388 ) 389 basicDst := map[string]string{basicKey: basicValue} 390 391 cases := map[string]struct { 392 src map[string]string 393 dst map[string]string 394 want map[string]string 395 }{ 396 "src is nil, dst is not nil": { 397 src: nil, 398 dst: map[string]string{dstKey: dstValue}, 399 want: map[string]string{basicKey: basicValue, dstKey: dstValue}, 400 }, 401 "src is not nil, dst is nil": { 402 src: map[string]string{srcKey: srcValue}, 403 dst: nil, 404 want: map[string]string{basicKey: basicValue, srcKey: srcValue}, 405 }, 406 "both nil": { 407 src: nil, 408 dst: nil, 409 want: map[string]string{basicKey: basicValue}, 410 }, 411 "both not nil": { 412 src: map[string]string{srcKey: srcValue}, 413 dst: map[string]string{dstKey: dstValue}, 414 want: map[string]string{basicKey: basicValue, srcKey: srcValue, dstKey: dstValue}, 415 }, 416 } 417 for name, tc := range cases { 418 t.Log("Running test case: " + name) 419 result := util.MergeMapOverrideWithDst(tc.src, basicDst) 420 result = util.MergeMapOverrideWithDst(result, tc.dst) 421 assert.Equal(t, result, tc.want) 422 } 423 424 } 425 426 func TestRawExtension2Map(t *testing.T) { 427 r1 := runtime.RawExtension{ 428 Raw: []byte(`{"a":{"c":"d"},"b":1}`), 429 Object: nil, 430 } 431 exp1 := map[string]interface{}{ 432 "a": map[string]interface{}{ 433 "c": "d", 434 }, 435 "b": float64(1), 436 } 437 got1, err := util.RawExtension2Map(&r1) 438 assert.NoError(t, err) 439 assert.Equal(t, exp1, got1) 440 441 r2 := runtime.RawExtension{ 442 Raw: nil, 443 Object: &unstructured.Unstructured{Object: map[string]interface{}{ 444 "a": map[string]interface{}{ 445 "c": "d", 446 }, 447 "b": float64(1), 448 }}, 449 } 450 got2, err := util.RawExtension2Map(&r2) 451 assert.NoError(t, err) 452 assert.Equal(t, exp1, got2) 453 } 454 455 func TestGenDefinitionNsFromCtx(t *testing.T) { 456 type testcase struct { 457 ctx context.Context 458 wantNs string 459 } 460 testcases := []testcase{ 461 {ctx: context.TODO(), wantNs: "vela-system"}, 462 {ctx: util.SetNamespaceInCtx(context.Background(), "vela-app"), wantNs: "vela-app"}, 463 {ctx: util.SetNamespaceInCtx(context.Background(), ""), wantNs: "default"}, 464 } 465 for _, ts := range testcases { 466 resNs := util.GetDefinitionNamespaceWithCtx(ts.ctx) 467 assert.Equal(t, ts.wantNs, resNs) 468 469 } 470 } 471 472 // TestGetDefinitionError is try to mock test when get an not existed definition in namespaced scope cluster 473 // will get an error that tpye is not found 474 func TestGetDefinitionError(t *testing.T) { 475 ctx := context.Background() 476 ctx = util.SetNamespaceInCtx(ctx, "vela-app") 477 478 errNotFound := apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "traitDefinition"}, "mock") 479 errNeedNamespace := fmt.Errorf("an empty namespace may not be set when a resource name is provided") 480 481 getFunc := func(ctx context.Context, key client.ObjectKey, obj client.Object) error { 482 ns := key.Namespace 483 if ns != "" { 484 return errNotFound 485 } else { 486 return errNeedNamespace 487 } 488 } 489 490 client := test.MockClient{MockGet: getFunc} 491 td := new(v1beta1.TraitDefinition) 492 got := util.GetDefinition(ctx, &client, td, "mock") 493 assert.Equal(t, errNotFound, got) 494 } 495 496 // TestGetDefinitionWithClusterScope is try to test compatibility of GetDefinition, 497 // GetDefinition try to search definition in system-level namespace firstly, 498 // if not found will search in app namespace, still cannot find it, try to search definition without namespace 499 func TestGetDefinitionWithClusterScope(t *testing.T) { 500 ctx := context.Background() 501 ctx = util.SetNamespaceInCtx(ctx, "vela-app") 502 // system-level definition 503 sys := v1beta1.TraitDefinition{ 504 ObjectMeta: metav1.ObjectMeta{ 505 Name: "sysDefinition", 506 Namespace: "vela-system", 507 }, 508 Spec: v1beta1.TraitDefinitionSpec{ 509 Reference: common.DefinitionReference{ 510 Name: "definitionrefrence.core.oam.dev", 511 }, 512 }, 513 } 514 // app workload Definition 515 app := v1beta1.TraitDefinition{ 516 ObjectMeta: metav1.ObjectMeta{ 517 Name: "appDefinition", 518 Namespace: "vela-app", 519 }, 520 Spec: v1beta1.TraitDefinitionSpec{ 521 Reference: common.DefinitionReference{ 522 Name: "definitionrefrence", 523 }, 524 }, 525 } 526 // old cluster workload trait scope definition crd is cluster scope, the namespace field is empty 527 noNs := v1beta1.TraitDefinition{ 528 ObjectMeta: metav1.ObjectMeta{ 529 Name: "noNsDefinition", 530 }, 531 Spec: v1beta1.TraitDefinitionSpec{ 532 Reference: common.DefinitionReference{ 533 Name: "definitionrefrence", 534 }, 535 }, 536 } 537 tdList := []v1beta1.TraitDefinition{app, sys, noNs} 538 mockIndexer := map[string]v1beta1.TraitDefinition{} 539 for i := 0; i < len(tdList); i++ { 540 var key string 541 if tdList[i].Namespace != "" { 542 key = tdList[i].Namespace + "/" + tdList[i].Name 543 } else { 544 key = tdList[i].Name 545 } 546 mockIndexer[key] = tdList[i] 547 } 548 549 getFunc := func(ctx context.Context, key client.ObjectKey, obj client.Object) error { 550 var namespacedName string 551 if key.Namespace != "" { 552 namespacedName = key.Namespace + "/" + key.Name 553 } else { 554 namespacedName = key.Name 555 } 556 td, ok := mockIndexer[namespacedName] 557 if ok { 558 obj, _ := obj.(*v1beta1.TraitDefinition) 559 *obj = td 560 return nil 561 } else { 562 return apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "traitDefinition"}, namespacedName) 563 } 564 } 565 566 type want struct { 567 td *v1beta1.TraitDefinition 568 err error 569 } 570 testcases := map[string]struct { 571 tdName string 572 want want 573 }{ 574 "app namespace is first level": { 575 tdName: "appDefinition", 576 want: want{ 577 err: nil, 578 td: &app, 579 }, 580 }, 581 "got sys namespace in system levle": { 582 tdName: "sysDefinition", 583 want: want{ 584 err: nil, 585 td: &sys, 586 }, 587 }, 588 "old cluster traitdefinition crd is cluster scope": { 589 tdName: "noNsDefinition", 590 want: want{ 591 err: nil, 592 td: &noNs, 593 }, 594 }, 595 "return err search not exsited definition": { 596 tdName: "notExistedDefinition", 597 want: want{ 598 err: apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "traitDefinition"}, "notExistedDefinition"), 599 td: new(v1beta1.TraitDefinition), 600 }, 601 }, 602 } 603 604 tclient := test.MockClient{MockGet: getFunc} 605 606 for name, tc := range testcases { 607 got := new(v1beta1.TraitDefinition) 608 err := util.GetDefinition(ctx, &tclient, got, tc.tdName) 609 t.Log(fmt.Sprint("Running test: ", name)) 610 611 assert.Equal(t, tc.want.err, err) 612 assert.Equal(t, tc.want.td, got) 613 } 614 } 615 616 func TestGetWorkloadDefinition(t *testing.T) { 617 // Test common variables 618 ctx := context.Background() 619 ctx = util.SetNamespaceInCtx(ctx, "vela-app") 620 621 // sys workload Definition 622 sysWorkloadDefinition := v1beta1.WorkloadDefinition{ 623 ObjectMeta: metav1.ObjectMeta{ 624 Name: "mockdefinition", 625 Namespace: "vela-system", 626 }, 627 Spec: v1beta1.WorkloadDefinitionSpec{ 628 Reference: common.DefinitionReference{ 629 Name: "definitionrefrence.core.oam.dev", 630 }, 631 }, 632 } 633 634 // app workload Definition 635 appWorkloadDefinition := v1beta1.WorkloadDefinition{ 636 ObjectMeta: metav1.ObjectMeta{ 637 Name: "mockdefinition.core.oam.dev", 638 Namespace: "vela-app", 639 }, 640 Spec: v1beta1.WorkloadDefinitionSpec{ 641 Reference: common.DefinitionReference{ 642 Name: "definitionrefrence.core.oam.dev", 643 }, 644 }, 645 } 646 647 type fields struct { 648 getFunc test.MockGetFn 649 } 650 type want struct { 651 wld v1beta1.WorkloadDefinition 652 err error 653 } 654 655 cases := map[string]struct { 656 fields fields 657 want want 658 }{ 659 660 "app defintion will overlay system definition": { 661 fields: fields{ 662 getFunc: func(ctx context.Context, key client.ObjectKey, obj client.Object) error { 663 o := obj.(*v1beta1.WorkloadDefinition) 664 if key.Namespace == "vela-system" { 665 *o = sysWorkloadDefinition 666 } else { 667 *o = appWorkloadDefinition 668 } 669 return nil 670 }, 671 }, 672 want: want{ 673 wld: appWorkloadDefinition, 674 err: nil, 675 }, 676 }, 677 678 "return system definition when cannot find in app ns": { 679 fields: fields{ 680 getFunc: func(ctx context.Context, key client.ObjectKey, obj client.Object) error { 681 if key.Namespace == "vela-system" { 682 o := obj.(*v1beta1.WorkloadDefinition) 683 *o = sysWorkloadDefinition 684 return nil 685 } 686 return apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "workloadDefinition"}, key.Name) 687 }, 688 }, 689 want: want{ 690 wld: sysWorkloadDefinition, 691 err: nil, 692 }, 693 }, 694 } 695 for name, tc := range cases { 696 tclient := test.MockClient{ 697 MockGet: tc.fields.getFunc, 698 } 699 got := new(v1beta1.WorkloadDefinition) 700 err := util.GetDefinition(ctx, &tclient, got, "mockdefinition") 701 t.Log(fmt.Sprint("Running test: ", name)) 702 703 assert.Equal(t, tc.want.err, err) 704 assert.Equal(t, tc.want.wld, *got) 705 } 706 } 707 708 func TestGetTraitDefinition(t *testing.T) { 709 // Test common variables 710 ctx := context.Background() 711 ctx = util.SetNamespaceInCtx(ctx, "vela-app") 712 713 // sys workload Definition 714 sysTraitDefinition := v1beta1.TraitDefinition{ 715 ObjectMeta: metav1.ObjectMeta{ 716 Name: "mockdefinition", 717 Namespace: "vela-system", 718 }, 719 Spec: v1beta1.TraitDefinitionSpec{ 720 Reference: common.DefinitionReference{ 721 Name: "definitionrefrence.core.oam.dev", 722 }, 723 }, 724 } 725 726 // app workload Definition 727 appTraitDefinition := v1beta1.TraitDefinition{ 728 ObjectMeta: metav1.ObjectMeta{ 729 Name: "mockdefinition.core.oam.dev", 730 Namespace: "vela-app", 731 }, 732 Spec: v1beta1.TraitDefinitionSpec{ 733 Reference: common.DefinitionReference{ 734 Name: "definitionrefrence.core.oam.dev", 735 }, 736 }, 737 } 738 739 type fields struct { 740 getFunc test.MockGetFn 741 } 742 type want struct { 743 wld v1beta1.TraitDefinition 744 err error 745 } 746 747 cases := map[string]struct { 748 fields fields 749 want want 750 }{ 751 "app defintion will overlay system definition": { 752 fields: fields{ 753 getFunc: func(ctx context.Context, key client.ObjectKey, obj client.Object) error { 754 o := obj.(*v1beta1.TraitDefinition) 755 if key.Namespace == "vela-system" { 756 *o = sysTraitDefinition 757 } else { 758 *o = appTraitDefinition 759 } 760 return nil 761 }, 762 }, 763 want: want{ 764 wld: appTraitDefinition, 765 err: nil, 766 }, 767 }, 768 769 "return system definition when cannot find in app ns": { 770 fields: fields{ 771 getFunc: func(ctx context.Context, key client.ObjectKey, obj client.Object) error { 772 if key.Namespace == "vela-system" { 773 o := obj.(*v1beta1.TraitDefinition) 774 *o = sysTraitDefinition 775 return nil 776 } 777 return apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "workloadDefinition"}, key.Name) 778 }, 779 }, 780 want: want{ 781 wld: sysTraitDefinition, 782 err: nil, 783 }, 784 }, 785 } 786 for name, tc := range cases { 787 tclient := test.MockClient{ 788 MockGet: tc.fields.getFunc, 789 } 790 got := new(v1beta1.TraitDefinition) 791 err := util.GetDefinition(ctx, &tclient, got, "mockdefinition") 792 t.Log(fmt.Sprint("Running test: ", name)) 793 794 assert.Equal(t, tc.want.err, err) 795 assert.Equal(t, tc.want.wld, *got) 796 } 797 } 798 799 func TestGetDefinition(t *testing.T) { 800 // Test common variables 801 env := "env-namespace" 802 803 // sys workload Definition 804 sysTraitDefinition := v1beta1.TraitDefinition{ 805 ObjectMeta: metav1.ObjectMeta{ 806 Name: "mockdefinition", 807 Namespace: "vela-system", 808 }, 809 } 810 811 // app workload Definition 812 appTraitDefinition := v1beta1.TraitDefinition{ 813 ObjectMeta: metav1.ObjectMeta{ 814 Name: "mockdefinition", 815 Namespace: "vela-app", 816 }, 817 } 818 819 // env workload Definition 820 envTraitDefinition := v1beta1.TraitDefinition{ 821 ObjectMeta: metav1.ObjectMeta{ 822 Name: "mockdefinition", 823 Namespace: env, 824 }, 825 } 826 827 cli := test.MockClient{MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error { 828 o := obj.(*v1beta1.TraitDefinition) 829 switch key.Namespace { 830 case "vela-system": 831 *o = sysTraitDefinition 832 case "vela-app": 833 *o = appTraitDefinition 834 case env: 835 *o = envTraitDefinition 836 default: 837 return apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "traitDefinition"}, key.Name) 838 } 839 return nil 840 }} 841 842 ctx := context.Background() 843 ctx = util.SetNamespaceInCtx(ctx, "vela-app") 844 appTd := new(v1beta1.TraitDefinition) 845 err := util.GetDefinition(ctx, &cli, appTd, "mockTrait") 846 assert.Equal(t, nil, err) 847 assert.Equal(t, &appTraitDefinition, appTd) 848 } 849 850 func TestExtractRevisionNum(t *testing.T) { 851 testcases := []struct { 852 revName string 853 wantRevisionNum int 854 delimiter string 855 hasError bool 856 }{{ 857 revName: "myapp-v1", 858 wantRevisionNum: 1, 859 delimiter: "-", 860 hasError: false, 861 }, { 862 revName: "new-app-v2", 863 wantRevisionNum: 2, 864 delimiter: "-", 865 hasError: false, 866 }, { 867 revName: "v1-v10", 868 wantRevisionNum: 10, 869 delimiter: "-", 870 hasError: false, 871 }, { 872 revName: "v10-v1-v1", 873 wantRevisionNum: 1, 874 delimiter: "-", 875 hasError: false, 876 }, { 877 revName: "myapp-v1-v2", 878 wantRevisionNum: 2, 879 delimiter: "-", 880 hasError: false, 881 }, { 882 revName: "myapp-v1-vv", 883 wantRevisionNum: 0, 884 delimiter: "-", 885 hasError: true, 886 }, { 887 revName: "v1", 888 wantRevisionNum: 0, 889 delimiter: "-", 890 hasError: true, 891 }, { 892 revName: "myapp-a1", 893 wantRevisionNum: 0, 894 delimiter: "-", 895 hasError: true, 896 }, { 897 revName: "worker@v1", 898 wantRevisionNum: 1, 899 delimiter: "@", 900 hasError: false, 901 }, { 902 revName: "worke@10r@v1", 903 wantRevisionNum: 1, 904 delimiter: "@", 905 hasError: false, 906 }, { 907 revName: "webservice@a10", 908 wantRevisionNum: 0, 909 delimiter: "@", 910 hasError: true, 911 }} 912 913 for _, tt := range testcases { 914 revision, err := util.ExtractRevisionNum(tt.revName, tt.delimiter) 915 hasError := err != nil 916 assert.Equal(t, tt.wantRevisionNum, revision) 917 assert.Equal(t, tt.hasError, hasError) 918 } 919 } 920 921 func TestConvertDefinitionRevName(t *testing.T) { 922 testcases := []struct { 923 defName string 924 wantRevName string 925 hasError bool 926 }{{ 927 defName: "worker@v2", 928 wantRevName: "worker-v2", 929 hasError: false, 930 }, { 931 defName: "worker@v10", 932 wantRevName: "worker-v10", 933 hasError: false, 934 }, { 935 defName: "worker", 936 wantRevName: "worker", 937 hasError: false, 938 }, { 939 defName: "webservice@@v2", 940 hasError: true, 941 }, { 942 defName: "webservice@v10@v3", 943 hasError: true, 944 }, { 945 defName: "@v10", 946 hasError: true, 947 }} 948 949 for _, tt := range testcases { 950 revName, err := util.ConvertDefinitionRevName(tt.defName) 951 assert.Equal(t, tt.hasError, err != nil) 952 if !tt.hasError { 953 assert.Equal(t, tt.wantRevName, revName) 954 } 955 } 956 } 957 958 func TestXDefinitionNamespaceInCtx(t *testing.T) { 959 testcases := []struct { 960 namespace string 961 expectedNamespace string 962 }{{ 963 namespace: "", 964 expectedNamespace: oam.SystemDefinitionNamespace, 965 }, { 966 namespace: oam.SystemDefinitionNamespace, 967 expectedNamespace: oam.SystemDefinitionNamespace, 968 }, { 969 namespace: "my-vela-system", 970 expectedNamespace: "my-vela-system"}, 971 } 972 973 ctx := context.Background() 974 ns := util.GetXDefinitionNamespaceWithCtx(ctx) 975 assert.Equal(t, oam.SystemDefinitionNamespace, ns) 976 977 for _, tc := range testcases { 978 ctx = util.SetXDefinitionNamespaceInCtx(ctx, tc.namespace) 979 ns = util.GetXDefinitionNamespaceWithCtx(ctx) 980 assert.Equal(t, tc.expectedNamespace, ns) 981 } 982 }