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  }