Files
kubevela/pkg/oam/util/helper_test.go
T
Diego Pinheiro 0aaab7fa30 fix typo (#4827)
Signed-off-by: Diego Pinheiro <diego.milhomes@gmail.com>

Signed-off-by: Diego Pinheiro <diego.milhomes@gmail.com>
2022-10-07 17:05:46 +08:00

1792 lines
47 KiB
Go

/*
Copyright 2021 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util_test
import (
"context"
"encoding/json"
"fmt"
"hash/adler32"
"reflect"
"testing"
"time"
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/condition"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/mock"
"github.com/oam-dev/kubevela/pkg/oam/util"
)
func TestLocateParentAppConfig(t *testing.T) {
ctx := context.Background()
const namespace = "oamNS"
acKind := reflect.TypeOf(v1alpha2.ApplicationConfiguration{}).Name()
mockVersion := "core.oam.dev/v1alpha2"
acName := "mockAC"
mockAC := v1alpha2.ApplicationConfiguration{
TypeMeta: metav1.TypeMeta{
Kind: acKind,
APIVersion: mockVersion,
},
ObjectMeta: metav1.ObjectMeta{
Name: acName,
Namespace: namespace,
},
Spec: v1alpha2.ApplicationConfigurationSpec{
Components: nil,
},
}
mockOwnerRef := metav1.OwnerReference{
APIVersion: mockVersion,
Kind: acKind,
Name: acName,
}
cmpKind := "Component"
cmpName := "mockComponent"
// use Component as mock oam.Object
mockComp := v1alpha2.Component{
TypeMeta: metav1.TypeMeta{
Kind: cmpKind,
APIVersion: mockVersion,
},
ObjectMeta: metav1.ObjectMeta{
Name: cmpName,
Namespace: namespace,
OwnerReferences: []metav1.OwnerReference{mockOwnerRef},
},
Spec: v1alpha2.ComponentSpec{
Workload: runtime.RawExtension{
Raw: nil,
Object: nil,
},
Parameters: nil,
},
}
mockCompWithEmptyOwnerRef := mockComp
mockCompWithEmptyOwnerRef.ObjectMeta.OwnerReferences = nil
getErr := fmt.Errorf("get error")
type fields struct {
getFunc test.ObjectFn
oamObj oam.Object
}
type want struct {
ac oam.Object
err error
}
cases := map[string]struct {
fields fields
want want
}{
"LocateParentAppConfig fail when getAppConfig fails": {
fields: fields{
getFunc: func(obj client.Object) error {
return getErr
},
oamObj: &mockComp,
},
want: want{
ac: nil,
err: getErr,
},
},
"LocateParentAppConfig fail when no ApplicationConfiguration in OwnerReferences": {
fields: fields{
getFunc: func(obj client.Object) error {
return getErr
},
oamObj: &mockCompWithEmptyOwnerRef,
},
want: want{
ac: nil,
err: errors.Errorf(util.ErrLocateAppConfig),
},
},
"LocateParentAppConfig success": {
fields: fields{
getFunc: func(obj client.Object) error {
o, _ := obj.(*v1alpha2.ApplicationConfiguration)
ac := mockAC
*o = ac
return nil
},
oamObj: &mockComp,
},
want: want{
ac: &mockAC,
err: nil,
},
},
}
for name, tc := range cases {
tclient := test.MockClient{
MockGet: test.NewMockGetFn(nil, tc.fields.getFunc),
}
got, err := util.LocateParentAppConfig(ctx, &tclient, tc.fields.oamObj)
t.Log(fmt.Sprint("Running test: ", name))
if tc.want.err == nil {
assert.NoError(t, err)
} else {
assert.Equal(t, tc.want.err.Error(), err.Error())
}
if tc.want.ac == nil {
assert.Equal(t, tc.want.ac, nil)
} else {
assert.Equal(t, tc.want.ac, got)
}
}
}
func TestScopeRelatedUtils(t *testing.T) {
ctx := context.Background()
namespace := "oamNS"
scopeDefinitionKind := "ScopeDefinition"
mockVerision := "core.oam.dev/v1alpha2"
scopeDefinitionName := "mockscopes.core.oam.dev"
scopeDefinitionRefName := "mockscopes.core.oam.dev"
scopeDefinitionWorkloadRefsPath := "spec.workloadRefs"
mockScopeDefinition := v1alpha2.ScopeDefinition{
TypeMeta: metav1.TypeMeta{
Kind: scopeDefinitionKind,
APIVersion: mockVerision,
},
ObjectMeta: metav1.ObjectMeta{
Name: scopeDefinitionName,
Namespace: namespace,
},
Spec: v1alpha2.ScopeDefinitionSpec{
Reference: common.DefinitionReference{
Name: scopeDefinitionRefName,
},
WorkloadRefsPath: scopeDefinitionWorkloadRefsPath,
AllowComponentOverlap: false,
},
}
scopeKind := "HealthScope"
scopeName := "HealthScope"
mockScope := v1alpha2.HealthScope{
TypeMeta: metav1.TypeMeta{
Kind: scopeKind,
APIVersion: mockVerision,
},
ObjectMeta: metav1.ObjectMeta{
Name: scopeName,
Namespace: namespace,
},
Spec: v1alpha2.HealthScopeSpec{
ProbeTimeout: nil,
ProbeInterval: nil,
WorkloadReferences: nil,
},
}
unstructuredScope, _ := util.Object2Unstructured(mockScope)
getErr := fmt.Errorf("get error")
type fields struct {
getFunc test.ObjectFn
}
type want struct {
spd *v1alpha2.ScopeDefinition
err error
}
cases := map[string]struct {
fields fields
want want
}{
"FetchScopeDefinition fail when getScopeDefinition fails": {
fields: fields{
getFunc: func(obj client.Object) error {
return getErr
},
},
want: want{
spd: nil,
err: getErr,
},
},
"FetchScopeDefinition Success": {
fields: fields{
getFunc: func(obj client.Object) error {
o, _ := obj.(*v1alpha2.ScopeDefinition)
sd := mockScopeDefinition
*o = sd
return nil
},
},
want: want{
spd: &mockScopeDefinition,
err: nil,
},
},
}
for name, tc := range cases {
tclient := test.MockClient{
MockGet: test.NewMockGetFn(nil, tc.fields.getFunc),
}
got, err := util.FetchScopeDefinition(ctx, &tclient, mock.NewMockDiscoveryMapper(), unstructuredScope)
t.Log(fmt.Sprint("Running test: ", name))
assert.Equal(t, tc.want.err, err)
assert.Equal(t, tc.want.spd, got)
}
}
func TestUtils(t *testing.T) {
// Test common variables
ctx := context.Background()
namespace := "oamNS"
workloadName := "oamWorkload"
imageV1 := "wordpress:4.6.1-apache"
workloadDefinitionName := "deployments.apps"
var workloadUID types.UID = "oamWorkloadUID"
workload := appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
APIVersion: "apps/v1",
Kind: "Deployment",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: workloadName,
},
Spec: appsv1.DeploymentSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "wordpress",
},
},
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "wordpress",
Image: imageV1,
},
},
},
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "wordpress"}},
},
},
}
workload.SetUID(workloadUID)
unstructuredWorkload, _ := util.Object2Unstructured(workload)
// workload Definition
workloadDefinition := v1alpha2.WorkloadDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: workloadDefinitionName,
},
Spec: v1alpha2.WorkloadDefinitionSpec{
Reference: common.DefinitionReference{
Name: workloadDefinitionName,
},
},
}
getErr := fmt.Errorf("get failed")
type fields struct {
getFunc test.ObjectFn
}
type want struct {
wld *v1alpha2.WorkloadDefinition
err error
}
cases := map[string]struct {
fields fields
want want
}{
"FetchWorkloadDefinition fail when getWorkloadDefinition fails": {
fields: fields{
getFunc: func(obj client.Object) error {
return getErr
},
},
want: want{
wld: nil,
err: getErr,
},
},
"FetchWorkloadDefinition Success": {
fields: fields{
getFunc: func(obj client.Object) error {
o, _ := obj.(*v1alpha2.WorkloadDefinition)
w := workloadDefinition
*o = w
return nil
},
},
want: want{
wld: &workloadDefinition,
err: nil,
},
},
}
for name, tc := range cases {
tclient := test.MockClient{
MockGet: test.NewMockGetFn(nil, tc.fields.getFunc),
}
got, err := util.FetchWorkloadDefinition(ctx, &tclient, mock.NewMockDiscoveryMapper(), unstructuredWorkload)
t.Log(fmt.Sprint("Running test: ", name))
assert.Equal(t, tc.want.err, err)
assert.Equal(t, tc.want.wld, got)
}
}
func TestUnstructured(t *testing.T) {
tests := map[string]struct {
u *unstructured.Unstructured
typeLabel string
exp string
resource string
}{
"native resource": {
u: &unstructured.Unstructured{Object: map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
}},
resource: "deployments",
exp: "deployments.apps",
},
"extended resource": {
u: &unstructured.Unstructured{Object: map[string]interface{}{
"apiVersion": "extend.oam.dev/v1alpha2",
"kind": "SimpleRolloutTrait",
}},
resource: "simplerollouttraits",
exp: "simplerollouttraits.extend.oam.dev",
},
"trait": {
u: &unstructured.Unstructured{Object: map[string]interface{}{
"apiVersion": "extend.oam.dev/v1alpha2",
"kind": "SimpleRolloutTrait",
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
oam.TraitTypeLabel: "rollout",
},
},
}},
typeLabel: oam.TraitTypeLabel,
exp: "rollout",
},
"workload": {
u: &unstructured.Unstructured{Object: map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
oam.WorkloadTypeLabel: "deploy",
},
},
}},
typeLabel: oam.WorkloadTypeLabel,
exp: "deploy",
},
}
for name, ti := range tests {
mapper := mock.NewMockDiscoveryMapper()
mapper.MockRESTMapping = mock.NewMockRESTMapping(ti.resource)
got, err := util.GetDefinitionName(mapper, ti.u, ti.typeLabel)
assert.NoError(t, err)
t.Log(fmt.Sprint("Running test: ", name))
assert.Equal(t, ti.exp, got)
}
}
func TestGetGVKFromDef(t *testing.T) {
mapper := mock.NewMockDiscoveryMapper()
mapper.MockKindsFor = mock.NewMockKindsFor("Abc", "v1", "v2")
gvk, err := util.GetGVKFromDefinition(mapper, common.DefinitionReference{Name: "abcs.example.com"})
assert.NoError(t, err)
assert.Equal(t, metav1.GroupVersionKind{
Group: "example.com",
Version: "v1",
Kind: "Abc",
}, gvk)
gvk, err = util.GetGVKFromDefinition(mapper, common.DefinitionReference{Name: "abcs.example.com", Version: "v2"})
assert.NoError(t, err)
assert.Equal(t, metav1.GroupVersionKind{
Group: "example.com",
Version: "v2",
Kind: "Abc",
}, gvk)
gvk, err = util.GetGVKFromDefinition(mapper, common.DefinitionReference{})
assert.NoError(t, err)
assert.Equal(t, metav1.GroupVersionKind{
Group: "",
Version: "",
Kind: "",
}, gvk)
gvk, err = util.GetGVKFromDefinition(mapper, common.DefinitionReference{Name: "dummy"})
assert.NoError(t, err)
assert.Equal(t, metav1.GroupVersionKind{
Group: "",
Version: "",
Kind: "",
}, gvk)
}
func TestConvertWorkloadGVK2Def(t *testing.T) {
mapper := mock.NewMockDiscoveryMapper()
mapper.MockRESTMapping = mock.NewMockRESTMapping("clonesets")
ref, err := util.ConvertWorkloadGVK2Definition(mapper, common.WorkloadGVK{APIVersion: "apps.kruise.io/v1alpha1",
Kind: "CloneSet"})
assert.NoError(t, err)
assert.Equal(t, common.DefinitionReference{
Name: "clonesets.apps.kruise.io",
Version: "v1alpha1",
}, ref)
mapper.MockRESTMapping = mock.NewMockRESTMapping("deployments")
ref, err = util.ConvertWorkloadGVK2Definition(mapper, common.WorkloadGVK{APIVersion: "apps/v1",
Kind: "Deployment"})
assert.NoError(t, err)
assert.Equal(t, common.DefinitionReference{
Name: "deployments.apps",
Version: "v1",
}, ref)
_, err = util.ConvertWorkloadGVK2Definition(mapper, common.WorkloadGVK{APIVersion: "/apps/v1",
Kind: "Deployment"})
assert.Error(t, err)
}
func TestDeepHashObject(t *testing.T) {
successCases := []func() interface{}{
func() interface{} { return 8675309 },
func() interface{} { return "Jenny, I got your number" },
func() interface{} { return []string{"eight", "six", "seven"} },
}
for _, tc := range successCases {
hasher1 := adler32.New()
util.DeepHashObject(hasher1, tc())
hash1 := hasher1.Sum32()
util.DeepHashObject(hasher1, tc())
hash2 := hasher1.Sum32()
assert.Equal(t, hash1, hash2)
}
}
func TestEndReconcileWithNegativeCondition(t *testing.T) {
var time1, time2 time.Time
time1 = time.Now()
time2 = time1.Add(time.Second)
type args struct {
ctx context.Context
r client.StatusClient
workload util.ConditionedObject
condition []condition.Condition
}
patchErr := fmt.Errorf("eww")
tests := []struct {
name string
args args
expected error
}{
{
name: "no condition is added",
args: args{
ctx: context.Background(),
r: &test.MockClient{
MockStatusPatch: test.NewMockStatusPatchFn(nil),
},
workload: &mock.Target{},
condition: []condition.Condition{},
},
expected: nil,
},
{
name: "condition is changed",
args: args{
ctx: context.Background(),
r: &test.MockClient{
MockStatusPatch: test.NewMockStatusPatchFn(nil),
},
workload: &mock.Target{
ConditionedStatus: condition.ConditionedStatus{
Conditions: []condition.Condition{
{
Type: "test",
LastTransitionTime: metav1.NewTime(time1),
Reason: "old reason",
Message: "old error msg",
},
},
},
},
condition: []condition.Condition{
{
Type: "test",
LastTransitionTime: metav1.NewTime(time2),
Reason: "new reason",
Message: "new error msg",
},
},
},
expected: nil,
},
{
name: "condition is not changed",
args: args{
ctx: context.Background(),
r: &test.MockClient{
MockStatusPatch: test.NewMockStatusPatchFn(nil),
},
workload: &mock.Target{
ConditionedStatus: condition.ConditionedStatus{
Conditions: []condition.Condition{
{
Type: "test",
LastTransitionTime: metav1.NewTime(time1),
Reason: "old reason",
Message: "old error msg",
},
},
},
},
condition: []condition.Condition{
{
Type: "test",
LastTransitionTime: metav1.NewTime(time2),
Reason: "old reason",
Message: "old error msg",
},
},
},
expected: fmt.Errorf(util.ErrReconcileErrInCondition, "test", "old error msg"),
},
{
name: "fail for patching error",
args: args{
ctx: context.Background(),
r: &test.MockClient{
MockStatusPatch: test.NewMockStatusPatchFn(patchErr),
},
workload: &mock.Target{},
condition: []condition.Condition{
{},
},
},
expected: errors.Wrap(patchErr, util.ErrUpdateStatus),
},
}
for _, tt := range tests {
err := util.EndReconcileWithNegativeCondition(tt.args.ctx, tt.args.r, tt.args.workload, tt.args.condition...)
if tt.expected == nil {
assert.NoError(t, err)
} else {
assert.Equal(t, tt.expected.Error(), err.Error())
}
}
}
func TestEndReconcileWithPositiveCondition(t *testing.T) {
type args struct {
ctx context.Context
r client.StatusClient
workload util.ConditionedObject
condition []condition.Condition
}
patchErr := fmt.Errorf("eww")
tests := []struct {
name string
args args
expected error
}{
{
name: "success",
args: args{
ctx: context.Background(),
r: &test.MockClient{
MockStatusPatch: test.NewMockStatusPatchFn(nil),
},
workload: &mock.Target{},
condition: []condition.Condition{
{},
},
},
expected: nil,
},
{
name: "fail",
args: args{
ctx: context.Background(),
r: &test.MockClient{
MockStatusPatch: test.NewMockStatusPatchFn(patchErr),
},
workload: &mock.Target{},
condition: []condition.Condition{
{},
},
},
expected: errors.Wrap(patchErr, util.ErrUpdateStatus),
},
}
for _, tt := range tests {
err := util.EndReconcileWithPositiveCondition(tt.args.ctx, tt.args.r, tt.args.workload, tt.args.condition...)
if tt.expected == nil {
assert.NoError(t, err)
} else {
assert.Equal(t, tt.expected.Error(), err.Error())
}
}
}
func TestComponentHelper(t *testing.T) {
ctx := context.Background()
type Case struct {
caseName string
acc v1alpha2.ApplicationConfigurationComponent
expectComponent *v1alpha2.Component
expectRevisionName string
expectErrorMatcher bool
}
namespace := "ns"
componentName := "newcomponent"
invalidComponentName := "invalidComponent"
revisionName := "newcomponent-aa1111"
revisionName2 := "newcomponent-bb1111"
unpackErrorRevisionName := "unpackErrorRevision"
errComponentNotFound := errors.New("component not found")
componnet1 := v1alpha2.Component{
ObjectMeta: metav1.ObjectMeta{
Name: componentName,
Namespace: namespace,
},
Spec: v1alpha2.ComponentSpec{Workload: runtime.RawExtension{Object: &unstructured.Unstructured{}}},
Status: v1alpha2.ComponentStatus{},
}
component2 := v1alpha2.Component{
ObjectMeta: metav1.ObjectMeta{
Name: componentName,
Namespace: namespace,
},
Spec: v1alpha2.ComponentSpec{Workload: runtime.RawExtension{Object: &unstructured.Unstructured{Object: map[string]interface{}{
"spec": map[string]interface{}{
"apiVersion": "New",
},
}}}},
Status: v1alpha2.ComponentStatus{
LatestRevision: &common.Revision{Name: revisionName2, Revision: 2},
},
}
client := &test.MockClient{MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
if o, ok := obj.(*v1alpha2.Component); ok {
switch key.Name {
case componentName:
*o = component2
case invalidComponentName:
return errComponentNotFound
default:
return nil
}
}
if o, ok := obj.(*appsv1.ControllerRevision); ok {
switch key.Name {
case revisionName:
*o = appsv1.ControllerRevision{
ObjectMeta: metav1.ObjectMeta{Name: revisionName, Namespace: namespace},
Data: runtime.RawExtension{Object: &componnet1},
Revision: 1,
}
case unpackErrorRevisionName:
*o = appsv1.ControllerRevision{
ObjectMeta: metav1.ObjectMeta{Name: unpackErrorRevisionName, Namespace: namespace},
Data: runtime.RawExtension{},
Revision: 1,
}
default:
return nil
}
}
return nil
}}
testCases := []Case{
{
caseName: "get component by revisionName",
acc: v1alpha2.ApplicationConfigurationComponent{RevisionName: revisionName},
expectComponent: &componnet1,
expectRevisionName: revisionName,
expectErrorMatcher: true,
},
{
caseName: "get component by componentName",
acc: v1alpha2.ApplicationConfigurationComponent{ComponentName: componentName},
expectComponent: &component2,
expectRevisionName: revisionName2,
expectErrorMatcher: true,
},
{
caseName: "not found error occurs when get by revisionName",
acc: v1alpha2.ApplicationConfigurationComponent{RevisionName: "invalidRevisionName"},
expectComponent: nil,
expectRevisionName: "",
expectErrorMatcher: false,
},
{
caseName: "unpack revison data error occurs when get by revisionName",
acc: v1alpha2.ApplicationConfigurationComponent{RevisionName: unpackErrorRevisionName},
expectComponent: nil,
expectRevisionName: "",
expectErrorMatcher: false,
},
{
caseName: "error occurs when get by componentName",
acc: v1alpha2.ApplicationConfigurationComponent{ComponentName: invalidComponentName},
expectComponent: nil,
expectRevisionName: "",
expectErrorMatcher: false,
},
}
for _, tc := range testCases {
t.Log("Running:" + tc.caseName)
c, r, err := util.GetComponent(ctx, client, tc.acc, namespace)
assert.Equal(t, tc.expectComponent, c)
assert.Equal(t, tc.expectRevisionName, r)
if tc.expectErrorMatcher {
assert.NoError(t, err)
} else {
assert.Error(t, err)
}
}
}
func TestUnpackRevisionData(t *testing.T) {
comp1 := v1alpha2.Component{ObjectMeta: metav1.ObjectMeta{Name: "comp1"}}
comp1Raw, _ := json.Marshal(comp1)
tests := map[string]struct {
rev *appsv1.ControllerRevision
expComp *v1alpha2.Component
expErr error
reason string
}{
"controllerRevision with Component Obj": {
rev: &appsv1.ControllerRevision{Data: runtime.RawExtension{Object: &v1alpha2.Component{ObjectMeta: metav1.ObjectMeta{Name: "comp1"}}}},
expComp: &v1alpha2.Component{ObjectMeta: metav1.ObjectMeta{Name: "comp1"}},
reason: "controllerRevision should align with component object",
},
"controllerRevision with Unknown Obj": {
rev: &appsv1.ControllerRevision{ObjectMeta: metav1.ObjectMeta{Name: "rev1"}, Data: runtime.RawExtension{Object: &runtime.Unknown{Raw: comp1Raw}}},
reason: "controllerRevision must be decode into component object",
expErr: fmt.Errorf("invalid type of revision rev1, type should not be *runtime.Unknown"),
},
"unmarshal with component data": {
rev: &appsv1.ControllerRevision{ObjectMeta: metav1.ObjectMeta{Name: "rev1"}, Data: runtime.RawExtension{Raw: comp1Raw}},
reason: "controllerRevision should unmarshal data and align with component object",
expComp: &v1alpha2.Component{ObjectMeta: metav1.ObjectMeta{Name: "comp1"}},
},
}
for name, ti := range tests {
t.Log("Running: " + name)
comp, err := util.UnpackRevisionData(ti.rev)
if ti.expErr != nil {
assert.Equal(t, ti.expErr, err)
} else {
assert.NoError(t, err)
assert.Equal(t, ti.expComp, comp)
}
}
}
func TestPassThroughObjMeta(t *testing.T) {
ac := &v1alpha2.ApplicationConfiguration{}
labels := map[string]string{
"core.oam.dev/ns": "oam-system",
"core.oam.dev/controller": "oam-kubernetes-runtime",
}
annotation := map[string]string{
"key1": "value1",
"key2": "value2",
}
ac.SetLabels(labels)
ac.SetAnnotations(annotation)
t.Log("workload and trait have no labels and annotation")
// test initial pass
var u unstructured.Unstructured
util.PassLabelAndAnnotation(ac, &u)
got := u.GetLabels()
want := labels
assert.Equal(t, want, got)
gotAnnotation := u.GetAnnotations()
wantAnnotation := annotation
assert.Equal(t, wantAnnotation, gotAnnotation)
// test overlapping keys
t.Log("workload and trait contains overlapping keys")
existAnnotation := map[string]string{
"key1": "exist value1",
"key3": "value3",
}
existLabels := map[string]string{
"core.oam.dev/ns": "kube-system",
"core.oam.dev/kube-native": "deployment",
}
u.SetLabels(existLabels)
u.SetAnnotations(existAnnotation)
util.PassLabelAndAnnotation(ac, &u)
gotAnnotation = u.GetAnnotations()
wantAnnotation = map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
}
assert.Equal(t, wantAnnotation, gotAnnotation)
gotLabels := u.GetLabels()
wantLabels := map[string]string{
"core.oam.dev/ns": "oam-system",
"core.oam.dev/kube-native": "deployment",
"core.oam.dev/controller": "oam-kubernetes-runtime",
}
assert.Equal(t, wantLabels, gotLabels)
// test removing annotation
t.Log("removing parent key doesn't remove child's")
util.RemoveAnnotations(ac, []string{"key1", "key2"})
assert.Equal(t, len(ac.GetAnnotations()), 0)
util.PassLabelAndAnnotation(ac, &u)
gotAnnotation = u.GetAnnotations()
assert.Equal(t, wantAnnotation, gotAnnotation)
gotLabels = u.GetLabels()
assert.Equal(t, wantLabels, gotLabels)
}
func TestAddLabels(t *testing.T) {
basicLabels := map[string]string{
"basic.label.key": "basic",
}
obj1 := new(unstructured.Unstructured)
wantObj1 := new(unstructured.Unstructured)
wantObj1.SetLabels(map[string]string{
"basic.label.key": "basic",
"newKey": "newValue",
})
obj2 := new(unstructured.Unstructured)
wantObj2 := new(unstructured.Unstructured)
obj2.SetLabels(map[string]string{
"key": "value",
})
wantObj2.SetLabels(map[string]string{
"basic.label.key": "basic",
"key": "value",
"newKey2": "newValue2",
})
cases := map[string]struct {
obj *unstructured.Unstructured
newLabels map[string]string
want *unstructured.Unstructured
}{
"add labels to workload without labels": {
obj1,
map[string]string{
"newKey": "newValue",
},
wantObj1,
},
"add labels to workload with labels": {
obj2,
map[string]string{
"newKey2": "newValue2",
},
wantObj2,
},
}
for name, tc := range cases {
t.Log("Running test case: " + name)
obj := tc.obj
wantObj := tc.want
util.AddLabels(obj, basicLabels)
util.AddLabels(obj, tc.newLabels)
assert.Equal(t, wantObj.GetLabels(), obj.GetLabels())
}
}
func TestMergeMapOverrideWithDst(t *testing.T) {
const (
basicKey = "basicKey"
dstKey = "dstKey"
srcKey = "srcKey"
basicValue = "basicValue"
dstValue = "dstValue"
srcValue = "srcValue"
)
basicDst := map[string]string{basicKey: basicValue}
cases := map[string]struct {
src map[string]string
dst map[string]string
want map[string]string
}{
"src is nil, dst is not nil": {
src: nil,
dst: map[string]string{dstKey: dstValue},
want: map[string]string{basicKey: basicValue, dstKey: dstValue},
},
"src is not nil, dst is nil": {
src: map[string]string{srcKey: srcValue},
dst: nil,
want: map[string]string{basicKey: basicValue, srcKey: srcValue},
},
"both nil": {
src: nil,
dst: nil,
want: map[string]string{basicKey: basicValue},
},
"both not nil": {
src: map[string]string{srcKey: srcValue},
dst: map[string]string{dstKey: dstValue},
want: map[string]string{basicKey: basicValue, srcKey: srcValue, dstKey: dstValue},
},
}
for name, tc := range cases {
t.Log("Running test case: " + name)
result := util.MergeMapOverrideWithDst(tc.src, basicDst)
result = util.MergeMapOverrideWithDst(result, tc.dst)
assert.Equal(t, result, tc.want)
}
}
func TestGetDummy(t *testing.T) {
var u = &unstructured.Unstructured{}
u.SetKind("Testkind")
u.SetAPIVersion("test.api/v1")
u.SetName("testdummy")
assert.Equal(t, &v1alpha2.TraitDefinition{
TypeMeta: metav1.TypeMeta{Kind: v1alpha2.TraitDefinitionKind, APIVersion: v1alpha2.SchemeGroupVersion.String()},
ObjectMeta: metav1.ObjectMeta{Name: "dummy", Annotations: map[string]string{
"apiVersion": u.GetAPIVersion(),
"kind": u.GetKind(),
"name": u.GetName(),
}},
Spec: v1alpha2.TraitDefinitionSpec{Reference: common.DefinitionReference{Name: "dummy"}},
}, util.GetDummyTraitDefinition(u))
assert.Equal(t, &v1alpha2.WorkloadDefinition{
TypeMeta: metav1.TypeMeta{Kind: v1alpha2.WorkloadDefinitionKind, APIVersion: v1alpha2.SchemeGroupVersion.String()},
ObjectMeta: metav1.ObjectMeta{Name: "dummy", Annotations: map[string]string{
"apiVersion": u.GetAPIVersion(),
"kind": u.GetKind(),
"name": u.GetName(),
}},
Spec: v1alpha2.WorkloadDefinitionSpec{Reference: common.DefinitionReference{Name: "dummy"}},
}, util.GetDummyWorkloadDefinition(u))
}
func TestRawExtension2Map(t *testing.T) {
r1 := runtime.RawExtension{
Raw: []byte(`{"a":{"c":"d"},"b":1}`),
Object: nil,
}
exp1 := map[string]interface{}{
"a": map[string]interface{}{
"c": "d",
},
"b": float64(1),
}
got1, err := util.RawExtension2Map(&r1)
assert.NoError(t, err)
assert.Equal(t, exp1, got1)
r2 := runtime.RawExtension{
Raw: nil,
Object: &unstructured.Unstructured{Object: map[string]interface{}{
"a": map[string]interface{}{
"c": "d",
},
"b": float64(1),
}},
}
got2, err := util.RawExtension2Map(&r2)
assert.NoError(t, err)
assert.Equal(t, exp1, got2)
}
func TestGenDefinitionNsFromCtx(t *testing.T) {
type testcase struct {
ctx context.Context
wantNs string
}
testcases := []testcase{
{ctx: context.TODO(), wantNs: "vela-system"},
{ctx: util.SetNamespaceInCtx(context.Background(), "vela-app"), wantNs: "vela-app"},
{ctx: util.SetNamespaceInCtx(context.Background(), ""), wantNs: "default"},
}
for _, ts := range testcases {
resNs := util.GetDefinitionNamespaceWithCtx(ts.ctx)
assert.Equal(t, ts.wantNs, resNs)
}
}
// TestGetDefinitionError is try to mock test when get an not existed definition in namespaced scope cluster
// will get an error that tpye is not found
func TestGetDefinitionError(t *testing.T) {
ctx := context.Background()
ctx = util.SetNamespaceInCtx(ctx, "vela-app")
errNotFound := apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "traitDefinition"}, "mock")
errNeedNamespace := fmt.Errorf("an empty namespace may not be set when a resource name is provided")
getFunc := func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
ns := key.Namespace
if ns != "" {
return errNotFound
} else {
return errNeedNamespace
}
}
client := test.MockClient{MockGet: getFunc}
td := new(v1alpha2.TraitDefinition)
got := util.GetDefinition(ctx, &client, td, "mock")
assert.Equal(t, errNotFound, got)
}
// TestGetDefinitionWithClusterScope is try to test compatibility of GetDefinition,
// GetDefinition try to search definition in system-level namespace firstly,
// if not found will search in app namespace, still cannot find it, try to search definition without namespace
func TestGetDefinitionWithClusterScope(t *testing.T) {
ctx := context.Background()
ctx = util.SetNamespaceInCtx(ctx, "vela-app")
// system-level definition
sys := v1alpha2.TraitDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "sysDefinition",
Namespace: "vela-system",
},
Spec: v1alpha2.TraitDefinitionSpec{
Reference: common.DefinitionReference{
Name: "definitionrefrence.core.oam.dev",
},
},
}
// app workload Definition
app := v1alpha2.TraitDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "appDefinition",
Namespace: "vela-app",
},
Spec: v1alpha2.TraitDefinitionSpec{
Reference: common.DefinitionReference{
Name: "definitionrefrence",
},
},
}
// old cluster workload trait scope definition crd is cluster scope, the namespace field is empty
noNs := v1alpha2.TraitDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "noNsDefinition",
},
Spec: v1alpha2.TraitDefinitionSpec{
Reference: common.DefinitionReference{
Name: "definitionrefrence",
},
},
}
tdList := []v1alpha2.TraitDefinition{app, sys, noNs}
mockIndexer := map[string]v1alpha2.TraitDefinition{}
for i := 0; i < len(tdList); i++ {
var key string
if tdList[i].Namespace != "" {
key = tdList[i].Namespace + "/" + tdList[i].Name
} else {
key = tdList[i].Name
}
mockIndexer[key] = tdList[i]
}
getFunc := func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
var namespacedName string
if key.Namespace != "" {
namespacedName = key.Namespace + "/" + key.Name
} else {
namespacedName = key.Name
}
td, ok := mockIndexer[namespacedName]
if ok {
obj, _ := obj.(*v1alpha2.TraitDefinition)
*obj = td
return nil
} else {
return apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "traitDefinition"}, namespacedName)
}
}
type want struct {
td *v1alpha2.TraitDefinition
err error
}
testcases := map[string]struct {
tdName string
want want
}{
"app namespace is first level": {
tdName: "appDefinition",
want: want{
err: nil,
td: &app,
},
},
"got sys namespace in system levle": {
tdName: "sysDefinition",
want: want{
err: nil,
td: &sys,
},
},
"old cluster traitdefinition crd is cluster scope": {
tdName: "noNsDefinition",
want: want{
err: nil,
td: &noNs,
},
},
"return err search not exsited definition": {
tdName: "notExistedDefinition",
want: want{
err: apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "traitDefinition"}, "notExistedDefinition"),
td: new(v1alpha2.TraitDefinition),
},
},
}
tclient := test.MockClient{MockGet: getFunc}
for name, tc := range testcases {
got := new(v1alpha2.TraitDefinition)
err := util.GetDefinition(ctx, &tclient, got, tc.tdName)
t.Log(fmt.Sprint("Running test: ", name))
assert.Equal(t, tc.want.err, err)
assert.Equal(t, tc.want.td, got)
}
}
func TestGetWorkloadDefinition(t *testing.T) {
// Test common variables
ctx := context.Background()
ctx = util.SetNamespaceInCtx(ctx, "vela-app")
// sys workload Definition
sysWorkloadDefinition := v1alpha2.WorkloadDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "mockdefinition",
Namespace: "vela-system",
},
Spec: v1alpha2.WorkloadDefinitionSpec{
Reference: common.DefinitionReference{
Name: "definitionrefrence.core.oam.dev",
},
},
}
// app workload Definition
appWorkloadDefinition := v1alpha2.WorkloadDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "mockdefinition.core.oam.dev",
Namespace: "vela-app",
},
Spec: v1alpha2.WorkloadDefinitionSpec{
Reference: common.DefinitionReference{
Name: "definitionrefrence.core.oam.dev",
},
},
}
type fields struct {
getFunc test.MockGetFn
}
type want struct {
wld v1alpha2.WorkloadDefinition
err error
}
cases := map[string]struct {
fields fields
want want
}{
"app defintion will overlay system definition": {
fields: fields{
getFunc: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
o := obj.(*v1alpha2.WorkloadDefinition)
if key.Namespace == "vela-system" {
*o = sysWorkloadDefinition
} else {
*o = appWorkloadDefinition
}
return nil
},
},
want: want{
wld: appWorkloadDefinition,
err: nil,
},
},
"return system definition when cannot find in app ns": {
fields: fields{
getFunc: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
if key.Namespace == "vela-system" {
o := obj.(*v1alpha2.WorkloadDefinition)
*o = sysWorkloadDefinition
return nil
}
return apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "workloadDefinition"}, key.Name)
},
},
want: want{
wld: sysWorkloadDefinition,
err: nil,
},
},
}
for name, tc := range cases {
tclient := test.MockClient{
MockGet: tc.fields.getFunc,
}
got := new(v1alpha2.WorkloadDefinition)
err := util.GetDefinition(ctx, &tclient, got, "mockdefinition")
t.Log(fmt.Sprint("Running test: ", name))
assert.Equal(t, tc.want.err, err)
assert.Equal(t, tc.want.wld, *got)
}
}
func TestGetTraitDefinition(t *testing.T) {
// Test common variables
ctx := context.Background()
ctx = util.SetNamespaceInCtx(ctx, "vela-app")
// sys workload Definition
sysTraitDefinition := v1alpha2.TraitDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "mockdefinition",
Namespace: "vela-system",
},
Spec: v1alpha2.TraitDefinitionSpec{
Reference: common.DefinitionReference{
Name: "definitionrefrence.core.oam.dev",
},
},
}
// app workload Definition
appTraitDefinition := v1alpha2.TraitDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "mockdefinition.core.oam.dev",
Namespace: "vela-app",
},
Spec: v1alpha2.TraitDefinitionSpec{
Reference: common.DefinitionReference{
Name: "definitionrefrence.core.oam.dev",
},
},
}
type fields struct {
getFunc test.MockGetFn
}
type want struct {
wld v1alpha2.TraitDefinition
err error
}
cases := map[string]struct {
fields fields
want want
}{
"app defintion will overlay system definition": {
fields: fields{
getFunc: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
o := obj.(*v1alpha2.TraitDefinition)
if key.Namespace == "vela-system" {
*o = sysTraitDefinition
} else {
*o = appTraitDefinition
}
return nil
},
},
want: want{
wld: appTraitDefinition,
err: nil,
},
},
"return system definition when cannot find in app ns": {
fields: fields{
getFunc: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
if key.Namespace == "vela-system" {
o := obj.(*v1alpha2.TraitDefinition)
*o = sysTraitDefinition
return nil
}
return apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "workloadDefinition"}, key.Name)
},
},
want: want{
wld: sysTraitDefinition,
err: nil,
},
},
}
for name, tc := range cases {
tclient := test.MockClient{
MockGet: tc.fields.getFunc,
}
got := new(v1alpha2.TraitDefinition)
err := util.GetDefinition(ctx, &tclient, got, "mockdefinition")
t.Log(fmt.Sprint("Running test: ", name))
assert.Equal(t, tc.want.err, err)
assert.Equal(t, tc.want.wld, *got)
}
}
func TestGetDefinition(t *testing.T) {
// Test common variables
env := "env-namespace"
// sys workload Definition
sysTraitDefinition := v1alpha2.TraitDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "mockdefinition",
Namespace: "vela-system",
},
}
// app workload Definition
appTraitDefinition := v1alpha2.TraitDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "mockdefinition",
Namespace: "vela-app",
},
}
// env workload Definition
envTraitDefinition := v1alpha2.TraitDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "mockdefinition",
Namespace: env,
},
}
cli := test.MockClient{MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
o := obj.(*v1alpha2.TraitDefinition)
switch key.Namespace {
case "vela-system":
*o = sysTraitDefinition
case "vela-app":
*o = appTraitDefinition
case env:
*o = envTraitDefinition
default:
return apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "traitDefinition"}, key.Name)
}
return nil
}}
ctx := context.Background()
ctx = util.SetNamespaceInCtx(ctx, "vela-app")
appTd := new(v1alpha2.TraitDefinition)
err := util.GetDefinition(ctx, &cli, appTd, "mockTrait")
assert.Equal(t, nil, err)
assert.Equal(t, &appTraitDefinition, appTd)
}
func TestGetScopeDefinition(t *testing.T) {
ctx := context.Background()
namespace := "vela-app"
ctx = util.SetNamespaceInCtx(ctx, namespace)
scopeDefinitionKind := "ScopeDefinition"
mockVerision := "core.oam.dev/v1alpha2"
scopeDefinitionName := "mockscopes.core.oam.dev"
scopeDefinitionRefName := "mockscopes.core.oam.dev"
scopeDefinitionWorkloadRefsPath := "spec.workloadRefs"
sysScopeDefinition := v1alpha2.ScopeDefinition{
TypeMeta: metav1.TypeMeta{
Kind: scopeDefinitionKind,
APIVersion: mockVerision,
},
ObjectMeta: metav1.ObjectMeta{
Name: scopeDefinitionName,
Namespace: "vela-system",
},
Spec: v1alpha2.ScopeDefinitionSpec{
Reference: common.DefinitionReference{
Name: scopeDefinitionRefName,
},
WorkloadRefsPath: scopeDefinitionWorkloadRefsPath,
AllowComponentOverlap: false,
},
}
appScopeDefinition := v1alpha2.ScopeDefinition{
TypeMeta: metav1.TypeMeta{
Kind: scopeDefinitionKind,
APIVersion: mockVerision,
},
ObjectMeta: metav1.ObjectMeta{
Name: scopeDefinitionName,
Namespace: namespace,
},
Spec: v1alpha2.ScopeDefinitionSpec{
Reference: common.DefinitionReference{
Name: scopeDefinitionRefName,
},
WorkloadRefsPath: scopeDefinitionWorkloadRefsPath,
AllowComponentOverlap: false,
},
}
type fields struct {
getFunc test.MockGetFn
}
type want struct {
spd *v1alpha2.ScopeDefinition
err error
}
cases := map[string]struct {
fields fields
want want
}{
"app defintion will overlay system definition": {
fields: fields{
getFunc: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
o := obj.(*v1alpha2.ScopeDefinition)
if key.Namespace == "vela-system" {
*o = sysScopeDefinition
} else {
*o = appScopeDefinition
}
return nil
},
},
want: want{
spd: &appScopeDefinition,
err: nil,
},
},
"return system definition when cannot find in app ns": {
fields: fields{
getFunc: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
if key.Namespace == "vela-system" {
o := obj.(*v1alpha2.ScopeDefinition)
*o = sysScopeDefinition
return nil
}
return apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "scopeDefinition"}, key.Name)
},
},
want: want{
spd: &sysScopeDefinition,
err: nil,
},
},
}
for name, tc := range cases {
tclient := test.MockClient{
MockGet: tc.fields.getFunc,
}
got := new(v1alpha2.ScopeDefinition)
err := util.GetDefinition(ctx, &tclient, got, "mockdefinition")
t.Log(fmt.Sprint("Running test: ", name))
assert.Equal(t, tc.want.err, err)
assert.Equal(t, tc.want.spd, got)
}
}
func TestConvertComponentDef2WorkloadDef(t *testing.T) {
var cd = v1beta1.ComponentDefinition{}
mapper := mock.NewMockDiscoveryMapper()
var componentDefWithWrongDefinition = `
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
name: worker
spec:
workload:
definition:
apiVersion: /apps/v1/
kind: Deployment
`
cd = v1beta1.ComponentDefinition{}
err := yaml.Unmarshal([]byte(componentDefWithWrongDefinition), &cd)
assert.Equal(t, nil, err)
err = util.ConvertComponentDef2WorkloadDef(mapper, &cd, &v1beta1.WorkloadDefinition{})
assert.Error(t, err)
mapper.MockRESTMapping = mock.NewMockRESTMapping("deployments")
var Template = `
schematic:
cue:
template: |
output: {
apiVersion: "apps/v1"
kind: "Deployment"
spec: {
selector: matchLabels: {
"app.oam.dev/component": context.name
}
template: {
metadata: labels: {
"app.oam.dev/component": context.name
}
spec: {
containers: [{
name: context.name
image: parameter.image
if parameter["cmd"] != _|_ {
command: parameter.cmd
}
}]
}
}
}
}
parameter: {
// +usage=Which image would you like to use for your service
// +short=i
image: string
// +usage=Commands to run in the container
cmd?: [...string]
}
`
var componentDefWithDefinition = `
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
name: worker
namespace: vela-system
labels:
env: test
annotations:
definition.oam.dev/description: "Describes long-running, scalable, containerized services that running at backend."
spec:
workload:
definition:
apiVersion: apps/v1
kind: Deployment
childResourceKinds:
- apiVersion: apps/v1
kind: Deployment
status:
healthPolicy: |
isHealth: (context.output.status.readyReplicas > 0) && (context.output.status.readyReplicas == context.output.status.replicas)` + Template
var expectWorkloadDef = `
apiVersion: core.oam.dev/v1beta1
kind: WorkloadDefinition
metadata:
name: worker
namespace: vela-system
labels:
env: test
annotations:
definition.oam.dev/description: "Describes long-running, scalable, containerized services that running at backend."
spec:
definitionRef:
name: deployments.apps
version: v1
childResourceKinds:
- apiVersion: apps/v1
kind: Deployment
status:
healthPolicy: |
isHealth: (context.output.status.readyReplicas > 0) && (context.output.status.readyReplicas == context.output.status.replicas)` + Template
cd = v1beta1.ComponentDefinition{}
wd := &v1beta1.WorkloadDefinition{}
err = yaml.Unmarshal([]byte(componentDefWithDefinition), &cd)
assert.NoError(t, err)
err = util.ConvertComponentDef2WorkloadDef(mapper, &cd, wd)
assert.NoError(t, err)
expectWd := v1beta1.WorkloadDefinition{}
err = yaml.Unmarshal([]byte(expectWorkloadDef), &expectWd)
assert.NoError(t, err)
assert.Equal(t, expectWd.Namespace, wd.Namespace)
assert.Equal(t, expectWd.Name, wd.Name)
assert.Equal(t, expectWd.Labels, wd.Labels)
assert.Equal(t, expectWd.Annotations, wd.Annotations)
assert.Equal(t, expectWd.Spec.Reference, wd.Spec.Reference)
assert.Equal(t, expectWd.Spec.ChildResourceKinds, wd.Spec.ChildResourceKinds)
assert.Equal(t, expectWd.Spec.Status, wd.Spec.Status)
assert.Equal(t, expectWd.Spec.Schematic, wd.Spec.Schematic)
}
func TestExtractRevisionNum(t *testing.T) {
testcases := []struct {
revName string
wantRevisionNum int
delimiter string
hasError bool
}{{
revName: "myapp-v1",
wantRevisionNum: 1,
delimiter: "-",
hasError: false,
}, {
revName: "new-app-v2",
wantRevisionNum: 2,
delimiter: "-",
hasError: false,
}, {
revName: "v1-v10",
wantRevisionNum: 10,
delimiter: "-",
hasError: false,
}, {
revName: "v10-v1-v1",
wantRevisionNum: 1,
delimiter: "-",
hasError: false,
}, {
revName: "myapp-v1-v2",
wantRevisionNum: 2,
delimiter: "-",
hasError: false,
}, {
revName: "myapp-v1-vv",
wantRevisionNum: 0,
delimiter: "-",
hasError: true,
}, {
revName: "v1",
wantRevisionNum: 0,
delimiter: "-",
hasError: true,
}, {
revName: "myapp-a1",
wantRevisionNum: 0,
delimiter: "-",
hasError: true,
}, {
revName: "worker@v1",
wantRevisionNum: 1,
delimiter: "@",
hasError: false,
}, {
revName: "worke@10r@v1",
wantRevisionNum: 1,
delimiter: "@",
hasError: false,
}, {
revName: "webservice@a10",
wantRevisionNum: 0,
delimiter: "@",
hasError: true,
}}
for _, tt := range testcases {
revision, err := util.ExtractRevisionNum(tt.revName, tt.delimiter)
hasError := err != nil
assert.Equal(t, tt.wantRevisionNum, revision)
assert.Equal(t, tt.hasError, hasError)
}
}
func TestConvertDefinitionRevName(t *testing.T) {
testcases := []struct {
defName string
wantRevName string
hasError bool
}{{
defName: "worker@v2",
wantRevName: "worker-v2",
hasError: false,
}, {
defName: "worker@v10",
wantRevName: "worker-v10",
hasError: false,
}, {
defName: "worker",
wantRevName: "worker",
hasError: false,
}, {
defName: "webservice@@v2",
hasError: true,
}, {
defName: "webservice@v10@v3",
hasError: true,
}, {
defName: "@v10",
hasError: true,
}}
for _, tt := range testcases {
revName, err := util.ConvertDefinitionRevName(tt.defName)
assert.Equal(t, tt.hasError, err != nil)
if !tt.hasError {
assert.Equal(t, tt.wantRevName, revName)
}
}
}