helmfile/pkg/state/exact_issue_scenario_test.go

128 lines
4.0 KiB
Go

package state
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/yaml"
)
// TestExactIssueScenario tests the exact helmfile.yaml scenario described in issue #2182
// This validates that users can now safely use the pattern without needing deepCopy workarounds
func TestExactIssueScenario(t *testing.T) {
// Reproduce the exact helmfile.yaml structure from the issue:
// environments:
// default:
// values:
// - values.yaml
// ---
// releases:
// - name: foo
// chart: charts/foo
// valuesTemplate:
// - {{ .Values | get "foo" (dict) | mergeOverwrite .Values | toYaml | nindent 8 }}
// - name: bar
// chart: charts/bar
// valuesTemplate:
// - {{ .Values | get "bar" (dict) | mergeOverwrite .Values | toYaml | nindent 8 }}
fs := &filesystem.FileSystem{
Glob: func(pattern string) ([]string, error) {
return nil, nil
},
ReadFile: func(filename string) ([]byte, error) {
return nil, nil
},
}
// Simulate the values.yaml content from the issue
valuesYamlContent := map[string]any{
"commonSetting": "shared-value",
"environment": "production",
"foo": map[string]any{
"image": "foo:1.2.3",
"port": 8080,
},
"bar": map[string]any{
"image": "bar:2.1.0",
"port": 9090,
},
}
st := &HelmState{
fs: fs,
basePath: "/tmp",
FilePath: "helmfile.yaml",
RenderedValues: valuesYamlContent,
}
// Test both orders: foo then bar, and bar then foo
orders := [][]string{
{"foo", "bar"}, // Original order
{"bar", "foo"}, // Reversed order
}
var results [][][]byte
for _, order := range orders {
var releases []ReleaseSpec
// Build releases in the specified order
for _, name := range order {
release := ReleaseSpec{
Name: name,
Chart: "charts/" + name,
ValuesTemplate: []any{
`{{ .Values | get "` + name + `" (dict) | mergeOverwrite .Values | toYaml | nindent 8 }}`,
},
}
releases = append(releases, release)
}
st.Releases = releases
processedState, err := st.ExecuteTemplates()
require.NoError(t, err, "ExecuteTemplates should succeed for order %v", order)
// Collect the processed values for comparison
var orderResults [][]byte
for _, release := range processedState.Releases {
require.NotEmpty(t, release.Values, "release %s should have processed values", release.Name)
// Convert to bytes for comparison
serialized, err := yaml.Marshal(release.Values[0])
require.NoError(t, err, "should be able to marshal values")
orderResults = append(orderResults, serialized)
}
results = append(results, orderResults)
}
// Critical assertion: Both processing orders should yield identical results
require.Len(t, results, 2, "should have results for both orders")
require.Len(t, results[0], 2, "should have 2 releases in first order")
require.Len(t, results[1], 2, "should have 2 releases in second order")
// Since the order is different, we need to match by content, not position
// For order ["foo", "bar"]: results[0][0] is foo, results[0][1] is bar
// For order ["bar", "foo"]: results[1][0] is bar, results[1][1] is foo
fooFromFirstOrder := results[0][0] // foo processed first
barFromFirstOrder := results[0][1] // bar processed second
barFromSecondOrder := results[1][0] // bar processed first
fooFromSecondOrder := results[1][1] // foo processed second
require.Equal(t, fooFromFirstOrder, fooFromSecondOrder,
"foo release should produce identical values regardless of processing order")
require.Equal(t, barFromFirstOrder, barFromSecondOrder,
"bar release should produce identical values regardless of processing order")
// Verify original values remain untouched
originalValues := st.Values()
require.Equal(t, valuesYamlContent, originalValues,
"original values should remain unchanged after template processing")
t.Log("✅ Issue #2182 is FIXED: Release order no longer affects valuesTemplate results")
t.Log("✅ Users can now safely use mergeOverwrite in valuesTemplate without deepCopy workaround")
}