441 lines
10 KiB
Go
441 lines
10 KiB
Go
package app
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/helmfile/vals"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gopkg.in/yaml.v3"
|
|
|
|
ffs "github.com/helmfile/helmfile/pkg/filesystem"
|
|
"github.com/helmfile/helmfile/pkg/helmexec"
|
|
"github.com/helmfile/helmfile/pkg/testhelper"
|
|
"github.com/helmfile/helmfile/pkg/testutil"
|
|
)
|
|
|
|
func TestPrintEnv_SingleHelmfile_YAML(t *testing.T) {
|
|
files := map[string]string{
|
|
"/path/to/helmfile.yaml": `
|
|
environments:
|
|
production:
|
|
kubeContext: prod-cluster
|
|
values:
|
|
- region: us-east-1
|
|
environment: production
|
|
debug: false
|
|
---
|
|
releases: []
|
|
`,
|
|
}
|
|
|
|
app := createTestApp(t, files, "production")
|
|
cfg := configImpl{output: "yaml"}
|
|
|
|
out, err := testutil.CaptureStdout(func() {
|
|
err := app.PrintEnv(cfg)
|
|
assert.NoError(t, err)
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Parse YAML output
|
|
var result map[string]any
|
|
err = yaml.Unmarshal([]byte(out), &result)
|
|
require.NoError(t, err)
|
|
|
|
// Verify structure
|
|
assert.Equal(t, "production", result["name"])
|
|
assert.Equal(t, "prod-cluster", result["kubeContext"])
|
|
assert.Contains(t, result["filePath"], "helmfile.yaml")
|
|
|
|
// Verify values
|
|
values, ok := result["values"].(map[string]any)
|
|
require.True(t, ok, "values should be a map")
|
|
assert.Equal(t, "us-east-1", values["region"])
|
|
assert.Equal(t, "production", values["environment"])
|
|
assert.Equal(t, false, values["debug"])
|
|
}
|
|
|
|
func TestPrintEnv_SingleHelmfile_JSON(t *testing.T) {
|
|
files := map[string]string{
|
|
"/path/to/helmfile.yaml": `
|
|
environments:
|
|
staging:
|
|
kubeContext: staging-cluster
|
|
values:
|
|
- database:
|
|
host: db.staging.local
|
|
port: 5432
|
|
---
|
|
releases: []
|
|
`,
|
|
}
|
|
|
|
app := createTestApp(t, files, "staging")
|
|
cfg := configImpl{output: "json"}
|
|
|
|
out, err := testutil.CaptureStdout(func() {
|
|
err := app.PrintEnv(cfg)
|
|
assert.NoError(t, err)
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Parse JSON output (should be an array)
|
|
var results []map[string]any
|
|
err = json.Unmarshal([]byte(out), &results)
|
|
require.NoError(t, err)
|
|
require.Len(t, results, 1, "should have one environment")
|
|
|
|
result := results[0]
|
|
assert.Equal(t, "staging", result["name"])
|
|
assert.Equal(t, "staging-cluster", result["kubeContext"])
|
|
assert.Contains(t, result["filePath"], "helmfile.yaml")
|
|
|
|
// Verify nested values
|
|
values, ok := result["values"].(map[string]any)
|
|
require.True(t, ok)
|
|
database, ok := values["database"].(map[string]any)
|
|
require.True(t, ok)
|
|
assert.Equal(t, "db.staging.local", database["host"])
|
|
assert.Equal(t, float64(5432), database["port"]) // JSON numbers are float64
|
|
}
|
|
|
|
func TestPrintEnv_MultipleHelmfiles_YAML(t *testing.T) {
|
|
files := map[string]string{
|
|
"/path/to/helmfile.yaml": `
|
|
environments:
|
|
dev:
|
|
kubeContext: main-context
|
|
values:
|
|
- source: main
|
|
sharedValue: from-main
|
|
---
|
|
helmfiles:
|
|
- path: sub/helmfile.yaml
|
|
releases: []
|
|
`,
|
|
"/path/to/sub/helmfile.yaml": `
|
|
environments:
|
|
dev:
|
|
kubeContext: sub-context
|
|
values:
|
|
- source: sub
|
|
subValue: from-sub
|
|
---
|
|
releases: []
|
|
`,
|
|
}
|
|
|
|
app := createTestApp(t, files, "dev")
|
|
cfg := configImpl{output: "yaml"}
|
|
|
|
out, err := testutil.CaptureStdout(func() {
|
|
err := app.PrintEnv(cfg)
|
|
assert.NoError(t, err)
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Split by --- to get individual documents
|
|
docs := strings.Split(out, "---\n")
|
|
// Filter out empty documents
|
|
var nonEmptyDocs []string
|
|
for _, doc := range docs {
|
|
trimmed := strings.TrimSpace(doc)
|
|
if trimmed != "" {
|
|
nonEmptyDocs = append(nonEmptyDocs, trimmed)
|
|
}
|
|
}
|
|
|
|
assert.GreaterOrEqual(t, len(nonEmptyDocs), 2, "should have at least 2 environment documents")
|
|
|
|
// Verify each document is valid YAML
|
|
for i, doc := range nonEmptyDocs {
|
|
var result map[string]any
|
|
err := yaml.Unmarshal([]byte(doc), &result)
|
|
require.NoError(t, err, "document %d should be valid YAML", i)
|
|
assert.Equal(t, "dev", result["name"], "document %d should have correct name", i)
|
|
assert.Contains(t, result, "kubeContext", "document %d should have kubeContext", i)
|
|
assert.Contains(t, result, "values", "document %d should have values", i)
|
|
assert.Contains(t, result, "filePath", "document %d should have filePath", i)
|
|
}
|
|
}
|
|
|
|
func TestPrintEnv_MultipleHelmfiles_JSON(t *testing.T) {
|
|
files := map[string]string{
|
|
"/path/to/helmfile.yaml": `
|
|
environments:
|
|
test:
|
|
kubeContext: main-kube
|
|
values:
|
|
- mainKey: mainValue
|
|
---
|
|
helmfiles:
|
|
- path: child1/helmfile.yaml
|
|
- path: child2/helmfile.yaml
|
|
releases: []
|
|
`,
|
|
"/path/to/child1/helmfile.yaml": `
|
|
environments:
|
|
test:
|
|
kubeContext: child1-kube
|
|
values:
|
|
- child1Key: child1Value
|
|
---
|
|
releases: []
|
|
`,
|
|
"/path/to/child2/helmfile.yaml": `
|
|
environments:
|
|
test:
|
|
kubeContext: child2-kube
|
|
values:
|
|
- child2Key: child2Value
|
|
---
|
|
releases: []
|
|
`,
|
|
}
|
|
|
|
app := createTestApp(t, files, "test")
|
|
cfg := configImpl{output: "json"}
|
|
|
|
out, err := testutil.CaptureStdout(func() {
|
|
err := app.PrintEnv(cfg)
|
|
assert.NoError(t, err)
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Parse JSON array
|
|
var results []map[string]any
|
|
err = json.Unmarshal([]byte(out), &results)
|
|
require.NoError(t, err)
|
|
assert.GreaterOrEqual(t, len(results), 3, "should have at least 3 environments")
|
|
|
|
// Verify all have correct structure
|
|
for i, result := range results {
|
|
assert.Equal(t, "test", result["name"], "result %d should have name 'test'", i)
|
|
assert.Contains(t, result, "kubeContext", "result %d should have kubeContext", i)
|
|
assert.Contains(t, result, "values", "result %d should have values", i)
|
|
assert.Contains(t, result, "filePath", "result %d should have filePath", i)
|
|
}
|
|
}
|
|
|
|
func TestPrintEnv_WithDefaults(t *testing.T) {
|
|
files := map[string]string{
|
|
"/path/to/helmfile.yaml": `
|
|
environments:
|
|
default:
|
|
values:
|
|
- color: blue
|
|
prod:
|
|
values:
|
|
- color: red
|
|
size: large
|
|
---
|
|
releases: []
|
|
`,
|
|
}
|
|
|
|
app := createTestApp(t, files, "prod")
|
|
cfg := configImpl{output: "json"}
|
|
|
|
out, err := testutil.CaptureStdout(func() {
|
|
err := app.PrintEnv(cfg)
|
|
assert.NoError(t, err)
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
var results []map[string]any
|
|
err = json.Unmarshal([]byte(out), &results)
|
|
require.NoError(t, err)
|
|
require.Len(t, results, 1)
|
|
|
|
values, ok := results[0]["values"].(map[string]any)
|
|
require.True(t, ok)
|
|
|
|
// Should have values from prod environment
|
|
assert.Equal(t, "red", values["color"])
|
|
assert.Equal(t, "large", values["size"])
|
|
}
|
|
|
|
func TestPrintEnv_InvalidOutputFormat(t *testing.T) {
|
|
files := map[string]string{
|
|
"/path/to/helmfile.yaml": `
|
|
environments:
|
|
dev:
|
|
values:
|
|
- test: value
|
|
---
|
|
releases: []
|
|
`,
|
|
}
|
|
|
|
app := createTestApp(t, files, "dev")
|
|
cfg := configImpl{output: "xml"} // Invalid format
|
|
|
|
_, err := testutil.CaptureStdout(func() {
|
|
err := app.PrintEnv(cfg)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "unsupported output format")
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestPrintEnv_EmptyValues(t *testing.T) {
|
|
files := map[string]string{
|
|
"/path/to/helmfile.yaml": `
|
|
environments:
|
|
minimal:
|
|
kubeContext: minimal-cluster
|
|
---
|
|
releases: []
|
|
`,
|
|
}
|
|
|
|
app := createTestApp(t, files, "minimal")
|
|
cfg := configImpl{output: "json"}
|
|
|
|
out, err := testutil.CaptureStdout(func() {
|
|
err := app.PrintEnv(cfg)
|
|
assert.NoError(t, err)
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
var results []map[string]any
|
|
err = json.Unmarshal([]byte(out), &results)
|
|
require.NoError(t, err)
|
|
require.Len(t, results, 1)
|
|
|
|
result := results[0]
|
|
assert.Equal(t, "minimal", result["name"])
|
|
assert.Equal(t, "minimal-cluster", result["kubeContext"])
|
|
|
|
// Values should exist but be empty
|
|
values, ok := result["values"].(map[string]any)
|
|
require.True(t, ok)
|
|
assert.Empty(t, values)
|
|
}
|
|
|
|
func TestPrintEnv_NoKubeContext(t *testing.T) {
|
|
files := map[string]string{
|
|
"/path/to/helmfile.yaml": `
|
|
environments:
|
|
local:
|
|
values:
|
|
- app: myapp
|
|
---
|
|
releases: []
|
|
`,
|
|
}
|
|
|
|
app := createTestApp(t, files, "local")
|
|
cfg := configImpl{output: "yaml"}
|
|
|
|
out, err := testutil.CaptureStdout(func() {
|
|
err := app.PrintEnv(cfg)
|
|
assert.NoError(t, err)
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
err = yaml.Unmarshal([]byte(out), &result)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, "local", result["name"])
|
|
// kubeContext should NOT be present when empty (omitted from output)
|
|
_, exists := result["kubeContext"]
|
|
assert.False(t, exists)
|
|
}
|
|
|
|
func TestPrintEnv_DefaultOutput(t *testing.T) {
|
|
// When output is empty string, should default to YAML
|
|
files := map[string]string{
|
|
"/path/to/helmfile.yaml": `
|
|
environments:
|
|
dev:
|
|
values:
|
|
- key: value
|
|
---
|
|
releases: []
|
|
`,
|
|
}
|
|
|
|
app := createTestApp(t, files, "dev")
|
|
cfg := configImpl{output: ""} // Empty output should default to yaml
|
|
|
|
out, err := testutil.CaptureStdout(func() {
|
|
err := app.PrintEnv(cfg)
|
|
assert.NoError(t, err)
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Should be valid YAML
|
|
var result map[string]any
|
|
err = yaml.Unmarshal([]byte(out), &result)
|
|
require.NoError(t, err, "empty output format should default to YAML")
|
|
}
|
|
|
|
func TestPrintEnv_UndefinedEnvironment(t *testing.T) {
|
|
// Test behavior with undefined environment
|
|
files := map[string]string{
|
|
"/path/to/helmfile.yaml": `
|
|
environments:
|
|
production:
|
|
values:
|
|
- env: prod
|
|
---
|
|
releases: []
|
|
`,
|
|
}
|
|
|
|
app := createTestApp(t, files, "staging") // staging is not defined
|
|
cfg := configImpl{output: "json"}
|
|
|
|
out, err := testutil.CaptureStdout(func() {
|
|
err := app.PrintEnv(cfg)
|
|
// The behavior depends on helmfile's environment handling
|
|
// It may succeed with empty values or fail
|
|
if err != nil {
|
|
assert.Contains(t, err.Error(), "environment")
|
|
}
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// If no error, output should be valid JSON (potentially empty array)
|
|
if out != "" {
|
|
var results []map[string]any
|
|
err = json.Unmarshal([]byte(out), &results)
|
|
// Should either be valid JSON or empty
|
|
if err != nil {
|
|
assert.Equal(t, "", out, "if not valid JSON, output should be empty")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper function to create test app with common setup
|
|
func createTestApp(t *testing.T, files map[string]string, environment string) *App {
|
|
t.Helper()
|
|
|
|
valsRuntime, err := vals.New(vals.Options{CacheSize: 32})
|
|
require.NoError(t, err)
|
|
|
|
var buffer bytes.Buffer
|
|
syncWriter := testhelper.NewSyncWriter(&buffer)
|
|
logger := helmexec.NewLogger(syncWriter, "warn")
|
|
|
|
app := appWithFs(&App{
|
|
OverrideHelmBinary: DefaultHelmBinary,
|
|
fs: ffs.DefaultFileSystem(),
|
|
OverrideKubeContext: "default",
|
|
Env: environment,
|
|
Logger: logger,
|
|
valsRuntime: valsRuntime,
|
|
FileOrDir: "helmfile.yaml",
|
|
}, files)
|
|
|
|
expectNoCallsToHelm(app)
|
|
|
|
return app
|
|
}
|