Merge pull request #31058 from farazkhawaja/get-values-test

Add test coverage for get_values/metadata.go
pull/31124/head
Joe Julian 1 month ago committed by GitHub
commit c6f9fd0fe0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -17,13 +17,606 @@ limitations under the License.
package action
import (
"errors"
"io"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
chart "helm.sh/helm/v4/pkg/chart/v2"
kubefake "helm.sh/helm/v4/pkg/kube/fake"
release "helm.sh/helm/v4/pkg/release/v1"
helmtime "helm.sh/helm/v4/pkg/time"
)
// unreachableKubeClient is a test client that always returns an error for IsReachable
type unreachableKubeClient struct {
kubefake.PrintingKubeClient
}
func (u *unreachableKubeClient) IsReachable() error {
return errors.New("connection refused")
}
func TestNewGetMetadata(t *testing.T) {
cfg := actionConfigFixture(t)
client := NewGetMetadata(cfg)
assert.NotNil(t, client)
assert.Equal(t, cfg, client.cfg)
assert.Equal(t, 0, client.Version)
}
func TestGetMetadata_Run_BasicMetadata(t *testing.T) {
cfg := actionConfigFixture(t)
client := NewGetMetadata(cfg)
releaseName := "test-release"
deployedTime := helmtime.Now()
rel := &release.Release{
Name: releaseName,
Info: &release.Info{
Status: release.StatusDeployed,
LastDeployed: deployedTime,
},
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "test-chart",
Version: "1.0.0",
AppVersion: "v1.2.3",
},
},
Version: 1,
Namespace: "default",
}
cfg.Releases.Create(rel)
result, err := client.Run(releaseName)
require.NoError(t, err)
assert.Equal(t, releaseName, result.Name)
assert.Equal(t, "test-chart", result.Chart)
assert.Equal(t, "1.0.0", result.Version)
assert.Equal(t, "v1.2.3", result.AppVersion)
assert.Equal(t, "default", result.Namespace)
assert.Equal(t, 1, result.Revision)
assert.Equal(t, "deployed", result.Status)
assert.Equal(t, deployedTime.Format(time.RFC3339), result.DeployedAt)
assert.Empty(t, result.Dependencies)
assert.Empty(t, result.Annotations)
}
func TestGetMetadata_Run_WithDependencies(t *testing.T) {
cfg := actionConfigFixture(t)
client := NewGetMetadata(cfg)
releaseName := "test-release"
deployedTime := helmtime.Now()
dependencies := []*chart.Dependency{
{
Name: "mysql",
Version: "8.0.25",
Repository: "https://charts.bitnami.com/bitnami",
},
{
Name: "redis",
Version: "6.2.4",
Repository: "https://charts.bitnami.com/bitnami",
},
}
rel := &release.Release{
Name: releaseName,
Info: &release.Info{
Status: release.StatusDeployed,
LastDeployed: deployedTime,
},
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "test-chart",
Version: "1.0.0",
AppVersion: "v1.2.3",
Dependencies: dependencies,
},
},
Version: 1,
Namespace: "default",
}
cfg.Releases.Create(rel)
result, err := client.Run(releaseName)
require.NoError(t, err)
assert.Equal(t, releaseName, result.Name)
assert.Equal(t, "test-chart", result.Chart)
assert.Equal(t, "1.0.0", result.Version)
assert.Equal(t, dependencies, result.Dependencies)
assert.Len(t, result.Dependencies, 2)
assert.Equal(t, "mysql", result.Dependencies[0].Name)
assert.Equal(t, "redis", result.Dependencies[1].Name)
}
func TestGetMetadata_Run_WithDependenciesAliases(t *testing.T) {
cfg := actionConfigFixture(t)
client := NewGetMetadata(cfg)
releaseName := "test-release"
deployedTime := helmtime.Now()
dependencies := []*chart.Dependency{
{
Name: "mysql",
Version: "8.0.25",
Repository: "https://charts.bitnami.com/bitnami",
Alias: "database",
},
{
Name: "redis",
Version: "6.2.4",
Repository: "https://charts.bitnami.com/bitnami",
Alias: "cache",
},
}
rel := &release.Release{
Name: releaseName,
Info: &release.Info{
Status: release.StatusDeployed,
LastDeployed: deployedTime,
},
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "test-chart",
Version: "1.0.0",
AppVersion: "v1.2.3",
Dependencies: dependencies,
},
},
Version: 1,
Namespace: "default",
}
cfg.Releases.Create(rel)
result, err := client.Run(releaseName)
require.NoError(t, err)
assert.Equal(t, releaseName, result.Name)
assert.Equal(t, "test-chart", result.Chart)
assert.Equal(t, "1.0.0", result.Version)
assert.Equal(t, dependencies, result.Dependencies)
assert.Len(t, result.Dependencies, 2)
assert.Equal(t, "mysql", result.Dependencies[0].Name)
assert.Equal(t, "database", result.Dependencies[0].Alias)
assert.Equal(t, "redis", result.Dependencies[1].Name)
assert.Equal(t, "cache", result.Dependencies[1].Alias)
}
func TestGetMetadata_Run_WithMixedDependencies(t *testing.T) {
cfg := actionConfigFixture(t)
client := NewGetMetadata(cfg)
releaseName := "test-release"
deployedTime := helmtime.Now()
dependencies := []*chart.Dependency{
{
Name: "mysql",
Version: "8.0.25",
Repository: "https://charts.bitnami.com/bitnami",
Alias: "database",
},
{
Name: "nginx",
Version: "1.20.0",
Repository: "https://charts.bitnami.com/bitnami",
},
{
Name: "redis",
Version: "6.2.4",
Repository: "https://charts.bitnami.com/bitnami",
Alias: "cache",
},
{
Name: "postgresql",
Version: "11.0.0",
Repository: "https://charts.bitnami.com/bitnami",
},
}
rel := &release.Release{
Name: releaseName,
Info: &release.Info{
Status: release.StatusDeployed,
LastDeployed: deployedTime,
},
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "test-chart",
Version: "1.0.0",
AppVersion: "v1.2.3",
Dependencies: dependencies,
},
},
Version: 1,
Namespace: "default",
}
cfg.Releases.Create(rel)
result, err := client.Run(releaseName)
require.NoError(t, err)
assert.Equal(t, releaseName, result.Name)
assert.Equal(t, "test-chart", result.Chart)
assert.Equal(t, "1.0.0", result.Version)
assert.Equal(t, dependencies, result.Dependencies)
assert.Len(t, result.Dependencies, 4)
// Verify dependencies with aliases
assert.Equal(t, "mysql", result.Dependencies[0].Name)
assert.Equal(t, "database", result.Dependencies[0].Alias)
assert.Equal(t, "redis", result.Dependencies[2].Name)
assert.Equal(t, "cache", result.Dependencies[2].Alias)
// Verify dependencies without aliases
assert.Equal(t, "nginx", result.Dependencies[1].Name)
assert.Equal(t, "", result.Dependencies[1].Alias)
assert.Equal(t, "postgresql", result.Dependencies[3].Name)
assert.Equal(t, "", result.Dependencies[3].Alias)
}
func TestGetMetadata_Run_WithAnnotations(t *testing.T) {
cfg := actionConfigFixture(t)
client := NewGetMetadata(cfg)
releaseName := "test-release"
deployedTime := helmtime.Now()
annotations := map[string]string{
"helm.sh/hook": "pre-install",
"helm.sh/hook-weight": "5",
"custom.annotation": "test-value",
}
rel := &release.Release{
Name: releaseName,
Info: &release.Info{
Status: release.StatusDeployed,
LastDeployed: deployedTime,
},
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "test-chart",
Version: "1.0.0",
AppVersion: "v1.2.3",
Annotations: annotations,
},
},
Version: 1,
Namespace: "default",
}
cfg.Releases.Create(rel)
result, err := client.Run(releaseName)
require.NoError(t, err)
assert.Equal(t, releaseName, result.Name)
assert.Equal(t, "test-chart", result.Chart)
assert.Equal(t, annotations, result.Annotations)
assert.Equal(t, "pre-install", result.Annotations["helm.sh/hook"])
assert.Equal(t, "5", result.Annotations["helm.sh/hook-weight"])
assert.Equal(t, "test-value", result.Annotations["custom.annotation"])
}
func TestGetMetadata_Run_SpecificVersion(t *testing.T) {
cfg := actionConfigFixture(t)
client := NewGetMetadata(cfg)
client.Version = 2
releaseName := "test-release"
deployedTime := helmtime.Now()
rel1 := &release.Release{
Name: releaseName,
Info: &release.Info{
Status: release.StatusSuperseded,
LastDeployed: helmtime.Time{Time: deployedTime.Time.Add(-time.Hour)},
},
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "test-chart",
Version: "1.0.0",
AppVersion: "v1.0.0",
},
},
Version: 1,
Namespace: "default",
}
rel2 := &release.Release{
Name: releaseName,
Info: &release.Info{
Status: release.StatusDeployed,
LastDeployed: deployedTime,
},
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "test-chart",
Version: "1.1.0",
AppVersion: "v1.1.0",
},
},
Version: 2,
Namespace: "default",
}
cfg.Releases.Create(rel1)
cfg.Releases.Create(rel2)
result, err := client.Run(releaseName)
require.NoError(t, err)
assert.Equal(t, releaseName, result.Name)
assert.Equal(t, "test-chart", result.Chart)
assert.Equal(t, "1.1.0", result.Version)
assert.Equal(t, "v1.1.0", result.AppVersion)
assert.Equal(t, 2, result.Revision)
assert.Equal(t, "deployed", result.Status)
}
func TestGetMetadata_Run_DifferentStatuses(t *testing.T) {
cfg := actionConfigFixture(t)
client := NewGetMetadata(cfg)
testCases := []struct {
name string
status release.Status
expected string
}{
{"deployed", release.StatusDeployed, "deployed"},
{"failed", release.StatusFailed, "failed"},
{"uninstalled", release.StatusUninstalled, "uninstalled"},
{"pending-install", release.StatusPendingInstall, "pending-install"},
{"pending-upgrade", release.StatusPendingUpgrade, "pending-upgrade"},
{"pending-rollback", release.StatusPendingRollback, "pending-rollback"},
{"superseded", release.StatusSuperseded, "superseded"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
releaseName := "test-release-" + tc.name
deployedTime := helmtime.Now()
rel := &release.Release{
Name: releaseName,
Info: &release.Info{
Status: tc.status,
LastDeployed: deployedTime,
},
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "test-chart",
Version: "1.0.0",
AppVersion: "v1.0.0",
},
},
Version: 1,
Namespace: "default",
}
cfg.Releases.Create(rel)
result, err := client.Run(releaseName)
require.NoError(t, err)
assert.Equal(t, tc.expected, result.Status)
})
}
}
func TestGetMetadata_Run_UnreachableKubeClient(t *testing.T) {
cfg := actionConfigFixture(t)
cfg.KubeClient = &unreachableKubeClient{
PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard},
}
client := NewGetMetadata(cfg)
_, err := client.Run("test-release")
assert.Error(t, err)
assert.Contains(t, err.Error(), "connection refused")
}
func TestGetMetadata_Run_ReleaseNotFound(t *testing.T) {
cfg := actionConfigFixture(t)
client := NewGetMetadata(cfg)
_, err := client.Run("non-existent-release")
assert.Error(t, err)
assert.Contains(t, err.Error(), "not found")
}
func TestGetMetadata_Run_EmptyAppVersion(t *testing.T) {
cfg := actionConfigFixture(t)
client := NewGetMetadata(cfg)
releaseName := "test-release"
deployedTime := helmtime.Now()
rel := &release.Release{
Name: releaseName,
Info: &release.Info{
Status: release.StatusDeployed,
LastDeployed: deployedTime,
},
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "test-chart",
Version: "1.0.0",
AppVersion: "", // Empty app version
},
},
Version: 1,
Namespace: "default",
}
cfg.Releases.Create(rel)
result, err := client.Run(releaseName)
require.NoError(t, err)
assert.Equal(t, "", result.AppVersion)
}
func TestMetadata_FormattedDepNames(t *testing.T) {
testCases := []struct {
name string
dependencies []*chart.Dependency
expected string
}{
{
name: "no dependencies",
dependencies: []*chart.Dependency{},
expected: "",
},
{
name: "single dependency",
dependencies: []*chart.Dependency{
{Name: "mysql"},
},
expected: "mysql",
},
{
name: "multiple dependencies sorted",
dependencies: []*chart.Dependency{
{Name: "redis"},
{Name: "mysql"},
{Name: "nginx"},
},
expected: "mysql,nginx,redis",
},
{
name: "already sorted dependencies",
dependencies: []*chart.Dependency{
{Name: "apache"},
{Name: "mysql"},
{Name: "zookeeper"},
},
expected: "apache,mysql,zookeeper",
},
{
name: "duplicate names",
dependencies: []*chart.Dependency{
{Name: "mysql"},
{Name: "redis"},
{Name: "mysql"},
},
expected: "mysql,mysql,redis",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
metadata := &Metadata{
Dependencies: tc.dependencies,
}
result := metadata.FormattedDepNames()
assert.Equal(t, tc.expected, result)
})
}
}
func TestMetadata_FormattedDepNames_WithComplexDependencies(t *testing.T) {
dependencies := []*chart.Dependency{
{
Name: "zookeeper",
Version: "10.0.0",
Repository: "https://charts.bitnami.com/bitnami",
Condition: "zookeeper.enabled",
},
{
Name: "apache",
Version: "9.0.0",
Repository: "https://charts.bitnami.com/bitnami",
},
{
Name: "mysql",
Version: "8.0.25",
Repository: "https://charts.bitnami.com/bitnami",
Condition: "mysql.enabled",
},
}
metadata := &Metadata{
Dependencies: dependencies,
}
result := metadata.FormattedDepNames()
assert.Equal(t, "apache,mysql,zookeeper", result)
}
func TestMetadata_FormattedDepNames_WithAliases(t *testing.T) {
testCases := []struct {
name string
dependencies []*chart.Dependency
expected string
}{
{
name: "dependencies with aliases",
dependencies: []*chart.Dependency{
{Name: "mysql", Alias: "database"},
{Name: "redis", Alias: "cache"},
},
expected: "mysql,redis",
},
{
name: "mixed dependencies with and without aliases",
dependencies: []*chart.Dependency{
{Name: "mysql", Alias: "database"},
{Name: "nginx"},
{Name: "redis", Alias: "cache"},
},
expected: "mysql,nginx,redis",
},
{
name: "empty alias should use name",
dependencies: []*chart.Dependency{
{Name: "mysql", Alias: ""},
{Name: "redis", Alias: "cache"},
},
expected: "mysql,redis",
},
{
name: "sorted by name not alias",
dependencies: []*chart.Dependency{
{Name: "zookeeper", Alias: "a-service"},
{Name: "apache", Alias: "z-service"},
},
expected: "apache,zookeeper",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
metadata := &Metadata{
Dependencies: tc.dependencies,
}
result := metadata.FormattedDepNames()
assert.Equal(t, tc.expected, result)
})
}
}
func TestGetMetadata_Labels(t *testing.T) {
rel := releaseStub()
rel.Info.Status = release.StatusDeployed

@ -0,0 +1,218 @@
/*
Copyright The Helm 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 action
import (
"io"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
chart "helm.sh/helm/v4/pkg/chart/v2"
kubefake "helm.sh/helm/v4/pkg/kube/fake"
release "helm.sh/helm/v4/pkg/release/v1"
)
func TestNewGetValues(t *testing.T) {
cfg := actionConfigFixture(t)
client := NewGetValues(cfg)
assert.NotNil(t, client)
assert.Equal(t, cfg, client.cfg)
assert.Equal(t, 0, client.Version)
assert.Equal(t, false, client.AllValues)
}
func TestGetValues_Run_UserConfigOnly(t *testing.T) {
cfg := actionConfigFixture(t)
client := NewGetValues(cfg)
releaseName := "test-release"
userConfig := map[string]interface{}{
"database": map[string]interface{}{
"host": "localhost",
"port": 5432,
},
"app": map[string]interface{}{
"name": "my-app",
"replicas": 3,
},
}
rel := &release.Release{
Name: releaseName,
Info: &release.Info{
Status: release.StatusDeployed,
},
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "test-chart",
Version: "1.0.0",
},
Values: map[string]interface{}{
"defaultKey": "defaultValue",
"app": map[string]interface{}{
"name": "default-app",
"timeout": 30,
},
},
},
Config: userConfig,
Version: 1,
Namespace: "default",
}
cfg.Releases.Create(rel)
result, err := client.Run(releaseName)
require.NoError(t, err)
assert.Equal(t, userConfig, result)
}
func TestGetValues_Run_AllValues(t *testing.T) {
cfg := actionConfigFixture(t)
client := NewGetValues(cfg)
client.AllValues = true
releaseName := "test-release"
userConfig := map[string]interface{}{
"database": map[string]interface{}{
"host": "localhost",
"port": 5432,
},
"app": map[string]interface{}{
"name": "my-app",
},
}
chartDefaultValues := map[string]interface{}{
"defaultKey": "defaultValue",
"app": map[string]interface{}{
"name": "default-app",
"timeout": 30,
},
}
rel := &release.Release{
Name: releaseName,
Info: &release.Info{
Status: release.StatusDeployed,
},
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "test-chart",
Version: "1.0.0",
},
Values: chartDefaultValues,
},
Config: userConfig,
Version: 1,
Namespace: "default",
}
cfg.Releases.Create(rel)
result, err := client.Run(releaseName)
require.NoError(t, err)
assert.Equal(t, "my-app", result["app"].(map[string]interface{})["name"])
assert.Equal(t, 30, result["app"].(map[string]interface{})["timeout"])
assert.Equal(t, "defaultValue", result["defaultKey"])
assert.Equal(t, "localhost", result["database"].(map[string]interface{})["host"])
assert.Equal(t, 5432, result["database"].(map[string]interface{})["port"])
}
func TestGetValues_Run_EmptyValues(t *testing.T) {
cfg := actionConfigFixture(t)
client := NewGetValues(cfg)
releaseName := "test-release"
rel := &release.Release{
Name: releaseName,
Info: &release.Info{
Status: release.StatusDeployed,
},
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "test-chart",
Version: "1.0.0",
},
},
Config: map[string]interface{}{},
Version: 1,
Namespace: "default",
}
cfg.Releases.Create(rel)
result, err := client.Run(releaseName)
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{}, result)
}
func TestGetValues_Run_UnreachableKubeClient(t *testing.T) {
cfg := actionConfigFixture(t)
cfg.KubeClient = &unreachableKubeClient{
PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard},
}
client := NewGetValues(cfg)
_, err := client.Run("test-release")
assert.Error(t, err)
assert.Contains(t, err.Error(), "connection refused")
}
func TestGetValues_Run_ReleaseNotFound(t *testing.T) {
cfg := actionConfigFixture(t)
client := NewGetValues(cfg)
_, err := client.Run("non-existent-release")
assert.Error(t, err)
assert.Contains(t, err.Error(), "not found")
}
func TestGetValues_Run_NilConfig(t *testing.T) {
cfg := actionConfigFixture(t)
client := NewGetValues(cfg)
releaseName := "test-release"
rel := &release.Release{
Name: releaseName,
Info: &release.Info{
Status: release.StatusDeployed,
},
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "test-chart",
Version: "1.0.0",
},
},
Config: nil,
Version: 1,
Namespace: "default",
}
cfg.Releases.Create(rel)
result, err := client.Run(releaseName)
require.NoError(t, err)
assert.Nil(t, result)
}
Loading…
Cancel
Save