kubevela/pkg/oam/util/helper_test.go

957 lines
24 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"
"fmt"
"hash/adler32"
"testing"
"time"
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
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"
"sigs.k8s.io/controller-runtime/pkg/client"
"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/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 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",
},
"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.NewClient(nil, nil).RESTMapper()
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.NewClient(nil, map[schema.GroupVersionResource][]schema.GroupVersionKind{
schema.GroupVersionResource{Group: "example.com", Resource: "abcs"}: {{Group: "example.com", Version: "v1", Kind: "Abc"}},
schema.GroupVersionResource{Group: "example.com", Resource: "abcs", Version: "v2"}: {{Group: "example.com", Version: "v2", Kind: "Abc"}},
}).RESTMapper()
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.NewClient(nil, map[schema.GroupVersionResource][]schema.GroupVersionKind{}).RESTMapper()
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)
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.NewMockSubResourcePatchFn(nil),
},
workload: &mock.Target{},
condition: []condition.Condition{},
},
expected: nil,
},
{
name: "condition is changed",
args: args{
ctx: context.Background(),
r: &test.MockClient{
MockStatusPatch: test.NewMockSubResourcePatchFn(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.NewMockSubResourcePatchFn(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.NewMockSubResourcePatchFn(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.NewMockSubResourcePatchFn(nil),
},
workload: &mock.Target{},
condition: []condition.Condition{
{},
},
},
expected: nil,
},
{
name: "fail",
args: args{
ctx: context.Background(),
r: &test.MockClient{
MockStatusPatch: test.NewMockSubResourcePatchFn(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 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 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(v1beta1.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 := v1beta1.TraitDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "sysDefinition",
Namespace: "vela-system",
},
Spec: v1beta1.TraitDefinitionSpec{
Reference: common.DefinitionReference{
Name: "definitionrefrence.core.oam.dev",
},
},
}
// app workload Definition
app := v1beta1.TraitDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "appDefinition",
Namespace: "vela-app",
},
Spec: v1beta1.TraitDefinitionSpec{
Reference: common.DefinitionReference{
Name: "definitionrefrence",
},
},
}
// old cluster workload trait scope definition crd is cluster scope, the namespace field is empty
noNs := v1beta1.TraitDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "noNsDefinition",
},
Spec: v1beta1.TraitDefinitionSpec{
Reference: common.DefinitionReference{
Name: "definitionrefrence",
},
},
}
tdList := []v1beta1.TraitDefinition{app, sys, noNs}
mockIndexer := map[string]v1beta1.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.(*v1beta1.TraitDefinition)
*obj = td
return nil
} else {
return apierrors.NewNotFound(schema.GroupResource{Group: "core.oma.dev", Resource: "traitDefinition"}, namespacedName)
}
}
type want struct {
td *v1beta1.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(v1beta1.TraitDefinition),
},
},
}
tclient := test.MockClient{MockGet: getFunc}
for name, tc := range testcases {
got := new(v1beta1.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 := v1beta1.WorkloadDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "mockdefinition",
Namespace: "vela-system",
},
Spec: v1beta1.WorkloadDefinitionSpec{
Reference: common.DefinitionReference{
Name: "definitionrefrence.core.oam.dev",
},
},
}
// app workload Definition
appWorkloadDefinition := v1beta1.WorkloadDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "mockdefinition.core.oam.dev",
Namespace: "vela-app",
},
Spec: v1beta1.WorkloadDefinitionSpec{
Reference: common.DefinitionReference{
Name: "definitionrefrence.core.oam.dev",
},
},
}
type fields struct {
getFunc test.MockGetFn
}
type want struct {
wld v1beta1.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.(*v1beta1.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.(*v1beta1.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(v1beta1.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 := v1beta1.TraitDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "mockdefinition",
Namespace: "vela-system",
},
Spec: v1beta1.TraitDefinitionSpec{
Reference: common.DefinitionReference{
Name: "definitionrefrence.core.oam.dev",
},
},
}
// app workload Definition
appTraitDefinition := v1beta1.TraitDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "mockdefinition.core.oam.dev",
Namespace: "vela-app",
},
Spec: v1beta1.TraitDefinitionSpec{
Reference: common.DefinitionReference{
Name: "definitionrefrence.core.oam.dev",
},
},
}
type fields struct {
getFunc test.MockGetFn
}
type want struct {
wld v1beta1.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.(*v1beta1.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.(*v1beta1.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(v1beta1.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 := v1beta1.TraitDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "mockdefinition",
Namespace: "vela-system",
},
}
// app workload Definition
appTraitDefinition := v1beta1.TraitDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "mockdefinition",
Namespace: "vela-app",
},
}
// env workload Definition
envTraitDefinition := v1beta1.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.(*v1beta1.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(v1beta1.TraitDefinition)
err := util.GetDefinition(ctx, &cli, appTd, "mockTrait")
assert.Equal(t, nil, err)
assert.Equal(t, &appTraitDefinition, appTd)
}
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)
}
}
}