mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 10:00:06 +00:00
Feat(test): Enhance unit test coverage for webhook, workflow, VELAQL, and monitor packages (#6895)
* feat(monitor): Add unit tests for application metrics watcher This commit introduces a new test file with comprehensive unit tests for the application metrics watcher functionality in pkg/monitor/watcher. Key additions include: - Test cases for the application metrics watcher's inc() method covering add, delete, and update operations - Test cases for report() method that verifies dirty flags are cleared - Test cases for helper functions getPhase() and getApp() These additions improve the overall test coverage and ensure the correctness of the application metrics monitoring functionality. Signed-off-by: Ashvin Bambhaniya <ashvin.bambhaniya@improwised.com> * feat(velaql): Add comprehensive unit tests for ParseVelaQLFromPath This commit introduces new unit tests for the ParseVelaQLFromPath function in pkg/velaql, along with test data files to improve test coverage and ensure correctness. Key additions include: - `pkg/velaql/parse_test.go`: Adds TestParseVelaQLFromPath function with comprehensive test cases covering: * Valid CUE files with and without export fields * Nonexistent and empty file paths * Invalid CUE content * Files with invalid export types - Test data files in pkg/velaql/testdata/: * simple-valid.cue: Valid CUE file with export field * simple-no-export.cue: Valid CUE file without export field * empty.cue: Empty CUE file * invalid-cue-content.cue: CUE file with invalid syntax * invalid-export.cue: CUE file with invalid export type These additions improve the overall test coverage and ensure the robustness of the VELAQL parsing functionality. Signed-off-by: Ashvin Bambhaniya <ashvin.bambhaniya@improwised.com> * feat(webhook): Add unit tests for ValidateDefinitionRevision function This commit introduces new unit tests for the ValidateDefinitionRevision function in pkg/webhook/utils to improve test coverage and ensure correctness of definition revision validation. Key additions include: - `pkg/webhook/utils/utils_test.go`: Adds TestValidateDefinitionRevision function with comprehensive test cases covering: * Success scenarios with matching definition revisions * Success scenarios when definition revision does not exist * Failure scenarios with revision hash mismatches * Failure scenarios with spec mismatches * Failure scenarios with invalid definition revision names These additions improve the overall test coverage and ensure the robustness of the webhook utility functions for validating definition revisions. Signed-off-by: Ashvin Bambhaniya <ashvin.bambhaniya@improwised.com> * feat(workflow): Add unit tests for OAM apply and query utilities This commit introduces new unit tests for workflow provider functions in pkg/workflow/providers to improve test coverage and ensure correctness. Key additions include: - `pkg/workflow/providers/oam/apply_test.go`: Adds TestRenderComponent function with comprehensive test cases for component rendering - `pkg/workflow/providers/query/utils_test.go`: Adds: * TestBuildResourceArray function with comprehensive test cases covering simple, nested, and complex resource tree scenarios * TestBuildResourceItem function with test cases for resources with and without annotations These additions improve the overall test coverage and ensure the robustness of the workflow provider functions for OAM applications. Signed-off-by: Ashvin Bambhaniya <ashvin.bambhaniya@improwised.com> * fix(velaql): Improve error handling in ParseVelaQLFromPath test This commit addresses an issue in the TestParseVelaQLFromPath function where file read errors were being silently ignored. The changes include: - Removing the unused expectedView field from test cases - Replacing conditional error checking with require.NoError to ensure file read operations are properly validated - Ensuring that test failures are properly reported when file reading fails This fix improves the reliability of the test suite by making sure that any file I/O errors are properly caught and reported rather than silently ignored. Signed-off-by: Ashvin Bambhaniya <ashvin.bambhaniya@improwised.com> * feat: Apply cross-cutting test improvements Signed-off-by: Ashvin Bambhaniya <ashvin.bambhaniya@improwised.com> * feat: Enhance test coverage with file-specific suggestions This commit applies file-specific suggestions to enhance the test suite's coverage and robustness. Key changes include: - **`pkg/monitor/watcher/application_test.go`**: - Added a test case for a multi-step workflow with mixed phases to validate `stepPhaseCounter` aggregation. - Added a test for idempotence by calling `inc` twice. - Added test cases for an empty workflow and an unknown application phase. - Strengthened the `report` test to assert that counters are not cleared. - **`pkg/velaql/parse_test.go`**: - Added a test case for `ParseVelaQLFromPath` to handle files with leading/trailing whitespace. - Added a test case to ensure consistent error messages for relative paths. - **`pkg/webhook/utils/utils_test.go`**: - Added a test case to `TestValidateCueTemplate` for a malformed CUE template. Signed-off-by: Ashvin Bambhaniya <ashvin.bambhaniya@improwised.com> --------- Signed-off-by: Ashvin Bambhaniya <ashvin.bambhaniya@improwised.com>
This commit is contained in:
committed by
GitHub
parent
d627ecea2a
commit
44ac92d1ba
261
pkg/monitor/watcher/application_test.go
Normal file
261
pkg/monitor/watcher/application_test.go
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
Copyright 2022 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 watcher
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
)
|
||||
|
||||
func TestApplicationMetricsWatcher(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
appRunning := &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "app-running"},
|
||||
Status: common.AppStatus{
|
||||
Phase: common.ApplicationRunning,
|
||||
},
|
||||
}
|
||||
appRendering := &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "app-rendering"},
|
||||
Status: common.AppStatus{
|
||||
Phase: common.ApplicationRendering,
|
||||
},
|
||||
}
|
||||
appWithWorkflow := &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "app-with-workflow"},
|
||||
Status: common.AppStatus{
|
||||
Phase: common.ApplicationRunning,
|
||||
Workflow: &common.WorkflowStatus{
|
||||
Steps: []workflowv1alpha1.WorkflowStepStatus{
|
||||
{
|
||||
StepStatus: workflowv1alpha1.StepStatus{
|
||||
Name: "step1",
|
||||
Type: "apply-component",
|
||||
Phase: workflowv1alpha1.WorkflowStepPhaseSucceeded,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
appWithMixedWorkflow := &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "app-with-mixed-workflow"},
|
||||
Status: common.AppStatus{
|
||||
Phase: common.ApplicationRunning,
|
||||
Workflow: &common.WorkflowStatus{
|
||||
Steps: []workflowv1alpha1.WorkflowStepStatus{
|
||||
{
|
||||
StepStatus: workflowv1alpha1.StepStatus{
|
||||
Name: "step1",
|
||||
Type: "apply-component",
|
||||
Phase: workflowv1alpha1.WorkflowStepPhaseSucceeded,
|
||||
},
|
||||
},
|
||||
{
|
||||
StepStatus: workflowv1alpha1.StepStatus{
|
||||
Name: "step2",
|
||||
Type: "apply-component",
|
||||
Phase: workflowv1alpha1.WorkflowStepPhaseFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
StepStatus: workflowv1alpha1.StepStatus{
|
||||
Name: "step3",
|
||||
Type: "suspend",
|
||||
Phase: workflowv1alpha1.WorkflowStepPhaseRunning,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testCases := map[string]struct {
|
||||
app *v1beta1.Application
|
||||
op int
|
||||
wantPC map[string]int
|
||||
wantSC map[string]int
|
||||
wantPD map[string]struct{}
|
||||
wantSD map[string]struct{}
|
||||
}{
|
||||
"Add an application": {
|
||||
app: appRunning,
|
||||
op: 1,
|
||||
wantPC: map[string]int{"running": 1},
|
||||
wantSC: map[string]int{},
|
||||
wantPD: map[string]struct{}{"running": {}},
|
||||
wantSD: map[string]struct{}{},
|
||||
},
|
||||
"Add an application with workflow": {
|
||||
app: appWithWorkflow,
|
||||
op: 1,
|
||||
wantPC: map[string]int{"running": 1},
|
||||
wantSC: map[string]int{"apply-component/succeeded#": 1},
|
||||
wantPD: map[string]struct{}{"running": {}},
|
||||
wantSD: map[string]struct{}{"apply-component/succeeded#": {}},
|
||||
},
|
||||
"Delete an application": {
|
||||
app: appRunning,
|
||||
op: -1,
|
||||
wantPC: map[string]int{"running": -1},
|
||||
wantSC: map[string]int{},
|
||||
wantPD: map[string]struct{}{"running": {}},
|
||||
wantSD: map[string]struct{}{},
|
||||
},
|
||||
"Update an application": {
|
||||
app: appRendering,
|
||||
op: -1,
|
||||
wantPC: map[string]int{"rendering": -1},
|
||||
wantSC: map[string]int{},
|
||||
wantPD: map[string]struct{}{"rendering": {}},
|
||||
wantSD: map[string]struct{}{},
|
||||
},
|
||||
"Nil app status": {
|
||||
app: &v1beta1.Application{},
|
||||
op: 1,
|
||||
wantPC: map[string]int{"-": 1},
|
||||
wantSC: map[string]int{},
|
||||
wantPD: map[string]struct{}{"-": {}},
|
||||
wantSD: map[string]struct{}{},
|
||||
},
|
||||
"Add an application with mixed workflow": {
|
||||
app: appWithMixedWorkflow,
|
||||
op: 1,
|
||||
wantPC: map[string]int{"running": 1},
|
||||
wantSC: map[string]int{
|
||||
"apply-component/succeeded#": 1,
|
||||
"apply-component/failed#": 1,
|
||||
"suspend/running#": 1,
|
||||
},
|
||||
wantPD: map[string]struct{}{"running": {}},
|
||||
wantSD: map[string]struct{}{
|
||||
"apply-component/succeeded#": {},
|
||||
"apply-component/failed#": {},
|
||||
"suspend/running#": {},
|
||||
},
|
||||
},
|
||||
"Empty workflow steps": {
|
||||
app: &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "app-empty-workflow"},
|
||||
Status: common.AppStatus{
|
||||
Phase: common.ApplicationRunning,
|
||||
Workflow: &common.WorkflowStatus{
|
||||
Steps: []workflowv1alpha1.WorkflowStepStatus{},
|
||||
},
|
||||
},
|
||||
},
|
||||
op: 1,
|
||||
wantPC: map[string]int{"running": 1},
|
||||
wantSC: map[string]int{},
|
||||
wantPD: map[string]struct{}{"running": {}},
|
||||
wantSD: map[string]struct{}{},
|
||||
},
|
||||
"Unknown phase": {
|
||||
app: &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "app-unknown-phase"},
|
||||
Status: common.AppStatus{
|
||||
Phase: "unknown",
|
||||
},
|
||||
},
|
||||
op: 1,
|
||||
wantPC: map[string]int{"unknown": 1},
|
||||
wantSC: map[string]int{},
|
||||
wantPD: map[string]struct{}{"unknown": {}},
|
||||
wantSD: map[string]struct{}{},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
watcher := &applicationMetricsWatcher{
|
||||
phaseCounter: map[string]int{},
|
||||
stepPhaseCounter: map[string]int{},
|
||||
phaseDirty: map[string]struct{}{},
|
||||
stepPhaseDirty: map[string]struct{}{},
|
||||
}
|
||||
watcher.inc(tc.app, tc.op)
|
||||
assert.Equal(t, tc.wantPC, watcher.phaseCounter)
|
||||
assert.Equal(t, tc.wantSC, watcher.stepPhaseCounter)
|
||||
assert.Equal(t, tc.wantPD, watcher.phaseDirty)
|
||||
assert.Equal(t, tc.wantSD, watcher.stepPhaseDirty)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("Idempotence", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
watcher := &applicationMetricsWatcher{
|
||||
phaseCounter: map[string]int{},
|
||||
stepPhaseCounter: map[string]int{},
|
||||
phaseDirty: map[string]struct{}{},
|
||||
stepPhaseDirty: map[string]struct{}{},
|
||||
}
|
||||
watcher.inc(appRunning, 1)
|
||||
watcher.inc(appRunning, 1)
|
||||
assert.Equal(t, map[string]int{"running": 2}, watcher.phaseCounter)
|
||||
assert.Equal(t, map[string]struct{}{"running": {}}, watcher.phaseDirty)
|
||||
})
|
||||
|
||||
t.Run("Report should clear dirty flags", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
watcher := &applicationMetricsWatcher{
|
||||
phaseCounter: map[string]int{"running": 1},
|
||||
stepPhaseCounter: map[string]int{"apply-component/succeeded#": 1},
|
||||
phaseDirty: map[string]struct{}{"running": {}},
|
||||
stepPhaseDirty: map[string]struct{}{"apply-component/succeeded#": {}},
|
||||
}
|
||||
watcher.report()
|
||||
assert.Empty(t, watcher.phaseDirty)
|
||||
assert.Empty(t, watcher.stepPhaseDirty)
|
||||
assert.Equal(t, map[string]int{"running": 1}, watcher.phaseCounter)
|
||||
assert.Equal(t, map[string]int{"apply-component/succeeded#": 1}, watcher.stepPhaseCounter)
|
||||
})
|
||||
|
||||
t.Run("getPhase helper function", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
watcher := &applicationMetricsWatcher{}
|
||||
assert.Equal(t, "-", watcher.getPhase(""))
|
||||
assert.Equal(t, "running", watcher.getPhase("running"))
|
||||
})
|
||||
|
||||
t.Run("getApp helper function", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
watcher := &applicationMetricsWatcher{}
|
||||
inputApp := &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app",
|
||||
Namespace: "test-ns",
|
||||
},
|
||||
Status: common.AppStatus{
|
||||
Phase: common.ApplicationRunning,
|
||||
},
|
||||
}
|
||||
resultApp := watcher.getApp(inputApp)
|
||||
assert.NotNil(t, resultApp)
|
||||
assert.Equal(t, "test-app", resultApp.Name)
|
||||
assert.Equal(t, "test-ns", resultApp.Namespace)
|
||||
assert.Equal(t, common.ApplicationRunning, resultApp.Status.Phase)
|
||||
})
|
||||
}
|
||||
@@ -17,13 +17,19 @@
|
||||
package velaql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseVelaQL(t *testing.T) {
|
||||
t.Parallel()
|
||||
testcases := []struct {
|
||||
ql string
|
||||
query QueryView
|
||||
@@ -67,19 +73,22 @@ func TestParseVelaQL(t *testing.T) {
|
||||
err: nil,
|
||||
}}
|
||||
|
||||
for _, testcase := range testcases {
|
||||
q, err := ParseVelaQL(testcase.ql)
|
||||
assert.Equal(t, testcase.err != nil, err != nil)
|
||||
if err == nil {
|
||||
assert.Equal(t, testcase.query.View, q.View)
|
||||
assert.Equal(t, testcase.query.Export, q.Export)
|
||||
} else {
|
||||
assert.Equal(t, testcase.err.Error(), err.Error())
|
||||
}
|
||||
for i, testcase := range testcases {
|
||||
t.Run(fmt.Sprintf("testcase-%d", i), func(t *testing.T) {
|
||||
q, err := ParseVelaQL(testcase.ql)
|
||||
if testcase.err != nil {
|
||||
assert.EqualError(t, testcase.err, err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testcase.query.View, q.View)
|
||||
assert.Equal(t, testcase.query.Export, q.Export)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseParameter(t *testing.T) {
|
||||
t.Parallel()
|
||||
testcases := []struct {
|
||||
parameter string
|
||||
parameterMap map[string]interface{}
|
||||
@@ -122,15 +131,114 @@ func TestParseParameter(t *testing.T) {
|
||||
err: nil,
|
||||
}}
|
||||
|
||||
for _, testcase := range testcases {
|
||||
result, err := ParseParameter(testcase.parameter)
|
||||
assert.Equal(t, testcase.err != nil, err != nil)
|
||||
if err == nil {
|
||||
for k, v := range result {
|
||||
assert.Equal(t, testcase.parameterMap[k], v)
|
||||
for i, testcase := range testcases {
|
||||
t.Run(fmt.Sprintf("testcase-%d", i), func(t *testing.T) {
|
||||
result, err := ParseParameter(testcase.parameter)
|
||||
if testcase.err != nil {
|
||||
assert.EqualError(t, testcase.err, err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
for k, v := range result {
|
||||
assert.Equal(t, testcase.parameterMap[k], v)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert.Equal(t, testcase.err.Error(), err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseVelaQLFromPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
|
||||
testdataDir := "testdata"
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
path string
|
||||
expectedExport string
|
||||
expectError bool
|
||||
errorContains string
|
||||
}{
|
||||
{
|
||||
name: "Simple valid CUE file with export field",
|
||||
path: filepath.Join(testdataDir, "simple-valid.cue"),
|
||||
expectedExport: "output.message",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Simple valid CUE file without export field",
|
||||
path: filepath.Join(testdataDir, "simple-no-export.cue"),
|
||||
expectedExport: DefaultExportValue,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Nonexistent file path",
|
||||
path: filepath.Join(testdataDir, "nonexistent.cue"),
|
||||
expectError: true,
|
||||
errorContains: "read view file from",
|
||||
},
|
||||
{
|
||||
name: "Empty file path",
|
||||
path: "",
|
||||
expectError: true,
|
||||
errorContains: "read view file from",
|
||||
},
|
||||
{
|
||||
name: "Invalid CUE content",
|
||||
path: filepath.Join(testdataDir, "invalid-cue-content.cue"),
|
||||
expectError: true,
|
||||
errorContains: "error when parsing view",
|
||||
},
|
||||
{
|
||||
name: "File with invalid export type - should fallback to default",
|
||||
path: filepath.Join(testdataDir, "invalid-export.cue"),
|
||||
expectedExport: DefaultExportValue,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Empty CUE file",
|
||||
path: filepath.Join(testdataDir, "empty.cue"),
|
||||
expectedExport: DefaultExportValue,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "File with leading/trailing whitespace",
|
||||
path: filepath.Join(testdataDir, "whitespace.cue"),
|
||||
expectedExport: "output.message",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Relative path",
|
||||
path: "testdata/nonexistent.cue",
|
||||
expectError: true,
|
||||
errorContains: "read view file from",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
result, err := ParseVelaQLFromPath(ctx, tc.path)
|
||||
|
||||
if tc.expectError {
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, result)
|
||||
if tc.errorContains != "" {
|
||||
assert.Contains(t, err.Error(), tc.errorContains)
|
||||
}
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
|
||||
if tc.path != "" {
|
||||
expectedContent, readErr := os.ReadFile(tc.path)
|
||||
require.NoError(t, readErr)
|
||||
assert.Equal(t, string(expectedContent), result.View)
|
||||
}
|
||||
|
||||
assert.Equal(t, tc.expectedExport, result.Export)
|
||||
assert.Nil(t, result.Parameter)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
0
pkg/velaql/testdata/empty.cue
vendored
Normal file
0
pkg/velaql/testdata/empty.cue
vendored
Normal file
4
pkg/velaql/testdata/invalid-cue-content.cue
vendored
Normal file
4
pkg/velaql/testdata/invalid-cue-content.cue
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
invalid cue syntax [
|
||||
missing: colon
|
||||
invalid: brackets
|
||||
}
|
||||
9
pkg/velaql/testdata/invalid-export.cue
vendored
Normal file
9
pkg/velaql/testdata/invalid-export.cue
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
parameter: {
|
||||
name: "test"
|
||||
}
|
||||
|
||||
output: {
|
||||
message: "hello " + parameter.name
|
||||
}
|
||||
|
||||
export: 123 // Invalid export type (should be string)
|
||||
7
pkg/velaql/testdata/simple-no-export.cue
vendored
Normal file
7
pkg/velaql/testdata/simple-no-export.cue
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
parameter: {
|
||||
name: "test"
|
||||
}
|
||||
|
||||
output: {
|
||||
message: "hello " + parameter.name
|
||||
}
|
||||
9
pkg/velaql/testdata/simple-valid.cue
vendored
Normal file
9
pkg/velaql/testdata/simple-valid.cue
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
parameter: {
|
||||
name: "test"
|
||||
}
|
||||
|
||||
output: {
|
||||
message: "hello " + parameter.name
|
||||
}
|
||||
|
||||
export: "output.message"
|
||||
12
pkg/velaql/testdata/whitespace.cue
vendored
Normal file
12
pkg/velaql/testdata/whitespace.cue
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
|
||||
parameter: {
|
||||
name: "test"
|
||||
}
|
||||
|
||||
output: {
|
||||
message: "hello " + parameter.name
|
||||
}
|
||||
|
||||
export: "output.message"
|
||||
|
||||
@@ -29,12 +29,132 @@ import (
|
||||
dynamicfake "k8s.io/client-go/dynamic/fake"
|
||||
|
||||
"cuelang.org/go/cue/errors"
|
||||
"github.com/crossplane/crossplane-runtime/pkg/test"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1beta1/core"
|
||||
)
|
||||
|
||||
func TestValidateDefinitionRevision(t *testing.T) {
|
||||
t.Parallel()
|
||||
scheme := runtime.NewScheme()
|
||||
v1beta1.AddToScheme(scheme)
|
||||
|
||||
baseCompDef := &v1beta1.ComponentDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-def",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v1beta1.ComponentDefinitionSpec{
|
||||
Workload: common.WorkloadTypeDescriptor{
|
||||
Definition: common.WorkloadGVK{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
Schematic: &common.Schematic{
|
||||
CUE: &common.CUE{
|
||||
Template: `
|
||||
output: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
metadata: name: context.name
|
||||
}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expectedDefRev, _, err := core.GatherRevisionInfo(baseCompDef)
|
||||
assert.NoError(t, err, "Setup: failed to gather revision info")
|
||||
expectedDefRev.Name = "test-def-v1"
|
||||
expectedDefRev.Namespace = "default"
|
||||
|
||||
mismatchedHashDefRev := expectedDefRev.DeepCopy()
|
||||
mismatchedHashDefRev.Spec.RevisionHash = "different-hash"
|
||||
|
||||
mismatchedSpecDefRev := expectedDefRev.DeepCopy()
|
||||
mismatchedSpecDefRev.Spec.ComponentDefinition.Spec.Workload.Definition.Kind = "StatefulSet"
|
||||
|
||||
// tweakedCompDef := baseCompDef.DeepCopy()
|
||||
// tweakedCompDef.Spec.Schematic.CUE.Template = `
|
||||
// output: {
|
||||
// apiVersion: "apps/v1"
|
||||
// kind: "Deployment"
|
||||
// metadata: name: context.name
|
||||
// // a tweak
|
||||
// }`
|
||||
testCases := map[string]struct {
|
||||
def runtime.Object
|
||||
defRevName types.NamespacedName
|
||||
existingObjs []runtime.Object
|
||||
expectErr bool
|
||||
expectedErrContains string
|
||||
}{
|
||||
"Success with matching definition revision": {
|
||||
def: baseCompDef,
|
||||
defRevName: types.NamespacedName{Name: "test-def-v1", Namespace: "default"},
|
||||
existingObjs: []runtime.Object{expectedDefRev},
|
||||
expectErr: false,
|
||||
},
|
||||
"Success when definition revision does not exist": {
|
||||
def: baseCompDef,
|
||||
defRevName: types.NamespacedName{Name: "test-def-v1", Namespace: "default"},
|
||||
existingObjs: []runtime.Object{},
|
||||
expectErr: false,
|
||||
},
|
||||
"Failure with revision hash mismatch": {
|
||||
def: baseCompDef,
|
||||
defRevName: types.NamespacedName{Name: "test-def-v1", Namespace: "default"},
|
||||
existingObjs: []runtime.Object{mismatchedHashDefRev},
|
||||
expectErr: true,
|
||||
expectedErrContains: "the definition's spec is different with existing definitionRevision's spec",
|
||||
},
|
||||
"Failure with spec mismatch (DeepEqual)": {
|
||||
def: baseCompDef,
|
||||
defRevName: types.NamespacedName{Name: "test-def-v1", Namespace: "default"},
|
||||
existingObjs: []runtime.Object{mismatchedSpecDefRev},
|
||||
expectErr: true,
|
||||
expectedErrContains: "the definition's spec is different with existing definitionRevision's spec",
|
||||
},
|
||||
"Failure with invalid definition revision name": {
|
||||
def: baseCompDef,
|
||||
defRevName: types.NamespacedName{Name: "invalid!name", Namespace: "default"},
|
||||
existingObjs: []runtime.Object{},
|
||||
expectErr: true,
|
||||
expectedErrContains: "invalid definitionRevision name",
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
cli := fake.NewClientBuilder().
|
||||
WithScheme(scheme).
|
||||
WithRuntimeObjects(tc.existingObjs...).
|
||||
Build()
|
||||
|
||||
err := ValidateDefinitionRevision(context.Background(), cli, tc.def, tc.defRevName)
|
||||
|
||||
if tc.expectErr {
|
||||
assert.Error(t, err)
|
||||
if tc.expectedErrContains != "" {
|
||||
assert.Contains(t, err.Error(), tc.expectedErrContains)
|
||||
}
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateCueTemplate(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := map[string]struct {
|
||||
cueTemplate string
|
||||
want error
|
||||
@@ -66,19 +186,31 @@ func TestValidateCueTemplate(t *testing.T) {
|
||||
}`,
|
||||
want: errors.New("output.hello: reference \"world\" not found"),
|
||||
},
|
||||
"emptyCueTemp": {
|
||||
cueTemplate: "",
|
||||
want: nil,
|
||||
},
|
||||
"malformedCueTemp": {
|
||||
cueTemplate: "output: { metadata: { name: context.name, label: context.label, annotation: \"default\" }, hello: world ",
|
||||
want: errors.New("expected '}', found 'EOF'"),
|
||||
},
|
||||
}
|
||||
|
||||
for caseName, cs := range cases {
|
||||
t.Run(caseName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := ValidateCueTemplate(cs.cueTemplate)
|
||||
if diff := cmp.Diff(cs.want, err, test.EquateErrors()); diff != "" {
|
||||
t.Errorf("\n%s\nValidateCueTemplate: -want , +got \n%s\n", cs.want, diff)
|
||||
if cs.want != nil {
|
||||
assert.EqualError(t, cs.want, err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateCuexTemplate(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := map[string]struct {
|
||||
cueTemplate string
|
||||
want error
|
||||
@@ -164,15 +296,19 @@ func TestValidateCuexTemplate(t *testing.T) {
|
||||
|
||||
for caseName, cs := range cases {
|
||||
t.Run(caseName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := ValidateCuexTemplate(context.Background(), cs.cueTemplate)
|
||||
if diff := cmp.Diff(cs.want, err, test.EquateErrors()); diff != "" {
|
||||
t.Errorf("\n%s\nValidateCueTemplate: -want , +got \n%s\n", cs.want, diff)
|
||||
if cs.want != nil {
|
||||
assert.Equal(t, cs.want.Error(), err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateSemanticVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := map[string]struct {
|
||||
version string
|
||||
want error
|
||||
@@ -192,18 +328,20 @@ func TestValidateSemanticVersion(t *testing.T) {
|
||||
}
|
||||
for caseName, cs := range cases {
|
||||
t.Run(caseName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := ValidateSemanticVersion(cs.version)
|
||||
if cs.want != nil {
|
||||
assert.Equal(t, err.Error(), cs.want.Error())
|
||||
assert.Error(t, err)
|
||||
assert.EqualError(t, cs.want, err.Error())
|
||||
} else {
|
||||
assert.Equal(t, err, cs.want)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateMultipleDefVersionsNotPresent(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := map[string]struct {
|
||||
version string
|
||||
revisionName string
|
||||
@@ -227,11 +365,13 @@ func TestValidateMultipleDefVersionsNotPresent(t *testing.T) {
|
||||
}
|
||||
for caseName, cs := range cases {
|
||||
t.Run(caseName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := ValidateMultipleDefVersionsNotPresent(cs.version, cs.revisionName, "ComponentDefinition")
|
||||
if cs.want != nil {
|
||||
assert.Equal(t, err.Error(), cs.want.Error())
|
||||
assert.Error(t, err)
|
||||
assert.EqualError(t, cs.want, err.Error())
|
||||
} else {
|
||||
assert.Equal(t, err, cs.want)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
@@ -48,6 +48,7 @@ func setupClient(ctx context.Context, t *testing.T) client.Client {
|
||||
}
|
||||
|
||||
func TestParser(t *testing.T) {
|
||||
t.Parallel()
|
||||
r := require.New(t)
|
||||
ctx := context.Background()
|
||||
act := &mock.Action{}
|
||||
@@ -106,7 +107,67 @@ func TestParser(t *testing.T) {
|
||||
r.Equal(act.Phase, "Wait")
|
||||
}
|
||||
|
||||
func TestRenderComponent(t *testing.T) {
|
||||
t.Parallel()
|
||||
r := require.New(t)
|
||||
ctx := context.Background()
|
||||
cuectx := cuecontext.New()
|
||||
cli := setupClient(ctx, t)
|
||||
|
||||
v := cuectx.CompileString(`$params: {
|
||||
value: {
|
||||
name: "test-render",
|
||||
type: "webservice",
|
||||
}
|
||||
}`)
|
||||
r.NoError(v.Err())
|
||||
|
||||
mockComponentRender := func(ctx context.Context, comp common.ApplicationComponent, patcher *cue.Value, clusterName string, overrideNamespace string) (*unstructured.Unstructured, []*unstructured.Unstructured, error) {
|
||||
r.Equal("test-render", comp.Name)
|
||||
workload := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "test-workload",
|
||||
},
|
||||
},
|
||||
}
|
||||
trait := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Service",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "test-trait",
|
||||
"labels": map[string]interface{}{
|
||||
"trait.oam.dev/resource": "mytrait",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return workload, []*unstructured.Unstructured{trait}, nil
|
||||
}
|
||||
|
||||
res, err := RenderComponent(ctx, &oamprovidertypes.Params[cue.Value]{
|
||||
Params: v,
|
||||
RuntimeParams: oamprovidertypes.RuntimeParams{
|
||||
KubeClient: cli,
|
||||
ComponentRender: mockComponentRender,
|
||||
},
|
||||
})
|
||||
r.NoError(err)
|
||||
|
||||
output, err := res.LookupPath(cue.ParsePath("$returns.output.metadata.name")).String()
|
||||
r.NoError(err)
|
||||
r.Equal("test-workload", output)
|
||||
|
||||
outputs, err := res.LookupPath(cue.ParsePath("$returns.outputs.mytrait.metadata.name")).String()
|
||||
r.NoError(err)
|
||||
r.Equal("test-workload", outputs)
|
||||
}
|
||||
|
||||
func TestLoadComponent(t *testing.T) {
|
||||
t.Parallel()
|
||||
r := require.New(t)
|
||||
ctx := context.Background()
|
||||
act := &mock.Action{}
|
||||
@@ -171,6 +232,7 @@ func TestLoadComponent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadComponentInOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
r := require.New(t)
|
||||
ctx := context.Background()
|
||||
act := &mock.Action{}
|
||||
|
||||
@@ -15,3 +15,317 @@
|
||||
*/
|
||||
|
||||
package query
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
querytypes "github.com/oam-dev/kubevela/pkg/utils/types"
|
||||
)
|
||||
|
||||
func TestBuildResourceArray(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Define common objects used across tests
|
||||
pod1 := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "pod1",
|
||||
},
|
||||
},
|
||||
}
|
||||
pod2 := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "pod2",
|
||||
"annotations": map[string]interface{}{
|
||||
oam.AnnotationPublishVersion: "v2.0.0-pod",
|
||||
oam.AnnotationDeployVersion: "rev2-pod",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
deployment := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "my-app",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Define common tree nodes
|
||||
parentWorkloadNode := &querytypes.ResourceTreeNode{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "my-app",
|
||||
Namespace: "default",
|
||||
Object: deployment,
|
||||
}
|
||||
|
||||
pod1Node := &querytypes.ResourceTreeNode{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
Name: "pod1",
|
||||
Namespace: "default",
|
||||
Object: pod1,
|
||||
}
|
||||
|
||||
pod2Node := &querytypes.ResourceTreeNode{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
Name: "pod2",
|
||||
Namespace: "default",
|
||||
Object: pod2,
|
||||
}
|
||||
|
||||
replicaSetNode := &querytypes.ResourceTreeNode{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "ReplicaSet",
|
||||
Name: "my-app-rs",
|
||||
Namespace: "default",
|
||||
LeafNodes: []*querytypes.ResourceTreeNode{pod1Node, pod2Node},
|
||||
}
|
||||
|
||||
// Define test cases
|
||||
testCases := map[string]struct {
|
||||
res querytypes.AppliedResource
|
||||
parent *querytypes.ResourceTreeNode
|
||||
node *querytypes.ResourceTreeNode
|
||||
kind string
|
||||
apiVersion string
|
||||
expected []querytypes.ResourceItem
|
||||
}{
|
||||
"simple case with one matching pod": {
|
||||
res: querytypes.AppliedResource{
|
||||
Cluster: "local",
|
||||
Component: "my-comp",
|
||||
PublishVersion: "v1.0.0",
|
||||
DeployVersion: "rev1",
|
||||
},
|
||||
parent: parentWorkloadNode,
|
||||
node: pod1Node,
|
||||
kind: "Pod",
|
||||
apiVersion: "v1",
|
||||
expected: []querytypes.ResourceItem{
|
||||
{
|
||||
Cluster: "local",
|
||||
Component: "my-comp",
|
||||
Workload: querytypes.Workload{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "my-app",
|
||||
Namespace: "default",
|
||||
},
|
||||
Object: pod1,
|
||||
PublishVersion: "v1.0.0",
|
||||
DeployVersion: "rev1",
|
||||
},
|
||||
},
|
||||
},
|
||||
"nested case with multiple matching pods": {
|
||||
res: querytypes.AppliedResource{
|
||||
Cluster: "remote",
|
||||
Component: "my-comp-2",
|
||||
PublishVersion: "v2.0.0",
|
||||
DeployVersion: "rev2",
|
||||
},
|
||||
parent: parentWorkloadNode,
|
||||
node: replicaSetNode,
|
||||
kind: "Pod",
|
||||
apiVersion: "v1",
|
||||
expected: []querytypes.ResourceItem{
|
||||
{
|
||||
Cluster: "remote",
|
||||
Component: "my-comp-2",
|
||||
Workload: querytypes.Workload{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "ReplicaSet",
|
||||
Name: "my-app-rs",
|
||||
Namespace: "default",
|
||||
},
|
||||
Object: pod1,
|
||||
PublishVersion: "v2.0.0",
|
||||
DeployVersion: "rev2",
|
||||
},
|
||||
{
|
||||
Cluster: "remote",
|
||||
Component: "my-comp-2",
|
||||
Workload: querytypes.Workload{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "ReplicaSet",
|
||||
Name: "my-app-rs",
|
||||
Namespace: "default",
|
||||
},
|
||||
Object: pod2,
|
||||
PublishVersion: "v2.0.0-pod", // From annotation
|
||||
DeployVersion: "rev2-pod", // From annotation
|
||||
},
|
||||
},
|
||||
},
|
||||
"no matching nodes": {
|
||||
res: querytypes.AppliedResource{},
|
||||
parent: parentWorkloadNode,
|
||||
node: pod1Node,
|
||||
kind: "Service",
|
||||
apiVersion: "v1",
|
||||
expected: nil,
|
||||
},
|
||||
"empty node": {
|
||||
res: querytypes.AppliedResource{},
|
||||
parent: parentWorkloadNode,
|
||||
node: &querytypes.ResourceTreeNode{},
|
||||
kind: "Pod",
|
||||
apiVersion: "v1",
|
||||
expected: nil,
|
||||
},
|
||||
"complex tree with mixed resources": {
|
||||
res: querytypes.AppliedResource{
|
||||
Cluster: "local",
|
||||
Component: "my-comp",
|
||||
PublishVersion: "v1.0.0",
|
||||
DeployVersion: "rev1",
|
||||
},
|
||||
parent: parentWorkloadNode,
|
||||
node: &querytypes.ResourceTreeNode{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "ReplicaSet",
|
||||
Name: "my-app-rs",
|
||||
Namespace: "default",
|
||||
LeafNodes: []*querytypes.ResourceTreeNode{
|
||||
pod1Node,
|
||||
{
|
||||
APIVersion: "v1",
|
||||
Kind: "Service",
|
||||
Name: "my-service",
|
||||
},
|
||||
pod2Node,
|
||||
},
|
||||
},
|
||||
kind: "Pod",
|
||||
apiVersion: "v1",
|
||||
expected: []querytypes.ResourceItem{
|
||||
{
|
||||
Cluster: "local",
|
||||
Component: "my-comp",
|
||||
Workload: querytypes.Workload{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "ReplicaSet",
|
||||
Name: "my-app-rs",
|
||||
Namespace: "default",
|
||||
},
|
||||
Object: pod1,
|
||||
PublishVersion: "v1.0.0",
|
||||
DeployVersion: "rev1",
|
||||
},
|
||||
{
|
||||
Cluster: "local",
|
||||
Component: "my-comp",
|
||||
Workload: querytypes.Workload{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "ReplicaSet",
|
||||
Name: "my-app-rs",
|
||||
Namespace: "default",
|
||||
},
|
||||
Object: pod2,
|
||||
PublishVersion: "v2.0.0-pod",
|
||||
DeployVersion: "rev2-pod",
|
||||
},
|
||||
},
|
||||
},
|
||||
"case-insensitive matching": {
|
||||
res: querytypes.AppliedResource{
|
||||
Cluster: "local",
|
||||
Component: "my-comp",
|
||||
PublishVersion: "v1.0.0",
|
||||
DeployVersion: "rev1",
|
||||
},
|
||||
parent: parentWorkloadNode,
|
||||
node: pod1Node,
|
||||
kind: "pod",
|
||||
apiVersion: "V1",
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
result := buildResourceArray(tc.res, tc.parent, tc.node, tc.kind, tc.apiVersion)
|
||||
assert.ElementsMatch(t, tc.expected, result, "The returned resource items should match the expected ones")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildResourceItem(t *testing.T) {
|
||||
t.Parallel()
|
||||
pod := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "test-pod",
|
||||
},
|
||||
},
|
||||
}
|
||||
podWithAnnotations := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "test-pod-annotated",
|
||||
"annotations": map[string]interface{}{
|
||||
oam.AnnotationPublishVersion: "v2.0.0-annotated",
|
||||
oam.AnnotationDeployVersion: "rev2-annotated",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
res := querytypes.AppliedResource{
|
||||
Cluster: "test-cluster",
|
||||
Component: "test-comp",
|
||||
PublishVersion: "v1.0.0-res",
|
||||
DeployVersion: "rev1-res",
|
||||
}
|
||||
workload := querytypes.Workload{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "test-workload",
|
||||
Namespace: "test-ns",
|
||||
}
|
||||
|
||||
t.Run("without annotations", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
item := buildResourceItem(res, workload, pod)
|
||||
assert.Equal(t, "test-cluster", item.Cluster)
|
||||
assert.Equal(t, "test-comp", item.Component)
|
||||
assert.Equal(t, workload, item.Workload)
|
||||
assert.Equal(t, pod, item.Object)
|
||||
assert.Equal(t, "v1.0.0-res", item.PublishVersion)
|
||||
assert.Equal(t, "rev1-res", item.DeployVersion)
|
||||
})
|
||||
|
||||
t.Run("with annotations", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
item := buildResourceItem(res, workload, podWithAnnotations)
|
||||
assert.Equal(t, "test-cluster", item.Cluster)
|
||||
assert.Equal(t, "test-comp", item.Component)
|
||||
assert.Equal(t, workload, item.Workload)
|
||||
assert.Equal(t, podWithAnnotations, item.Object)
|
||||
assert.Equal(t, "v2.0.0-annotated", item.PublishVersion)
|
||||
assert.Equal(t, "rev2-annotated", item.DeployVersion)
|
||||
})
|
||||
|
||||
t.Run("annotation override", func(t *testing.T) {
|
||||
item := buildResourceItem(res, workload, podWithAnnotations)
|
||||
assert.Equal(t, "v2.0.0-annotated", item.PublishVersion)
|
||||
assert.Equal(t, "rev2-annotated", item.DeployVersion)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user