From 43dcad45e1a2c2850cdb8d728e66db7ace3fd6f0 Mon Sep 17 00:00:00 2001 From: Ilya Kiselev Date: Wed, 24 Jun 2026 01:56:37 +0300 Subject: [PATCH] test(cmd): cover SOURCE_DATE_EPOCH parsing in helm package CLI Adds CLI-level coverage for the SOURCE_DATE_EPOCH path in pkg/cmd/package.go: invalid (non-numeric, negative) values surface the expected error, and a valid value produces an archive whose tar entries are all stamped with the given epoch. Signed-off-by: Ilya Kiselev --- pkg/cmd/package_test.go | 66 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/pkg/cmd/package_test.go b/pkg/cmd/package_test.go index db4a2523a..2a96feabf 100644 --- a/pkg/cmd/package_test.go +++ b/pkg/cmd/package_test.go @@ -16,12 +16,16 @@ limitations under the License. package cmd import ( + "archive/tar" + "compress/gzip" "fmt" + "io" "os" "path/filepath" "regexp" "strings" "testing" + "time" "helm.sh/helm/v4/internal/test/ensure" chart "helm.sh/helm/v4/pkg/chart/v2" @@ -192,6 +196,68 @@ func TestSetAppVersion(t *testing.T) { } } +func TestPackageSourceDateEpoch(t *testing.T) { + chartToPackage := "testdata/testcharts/alpine" + + for _, tt := range []struct { + name string + envVal string + err string + }{ + {name: "non-numeric value", envVal: "not-a-number", err: "invalid SOURCE_DATE_EPOCH"}, + {name: "negative value", envVal: "-1", err: "invalid SOURCE_DATE_EPOCH"}, + } { + t.Run(tt.name, func(t *testing.T) { + dir := t.TempDir() + t.Setenv("SOURCE_DATE_EPOCH", tt.envVal) + cmd := fmt.Sprintf("package %s --destination=%s", chartToPackage, dir) + _, _, err := executeActionCommand(cmd) + if err == nil || !strings.Contains(err.Error(), tt.err) { + t.Fatalf("expected error containing %q, got %v", tt.err, err) + } + }) + } + + t.Run("valid value stamps archive entries", func(t *testing.T) { + dir := t.TempDir() + t.Setenv("SOURCE_DATE_EPOCH", "1700000000") + cmd := fmt.Sprintf("package %s --destination=%s", chartToPackage, dir) + _, output, err := executeActionCommand(cmd) + if err != nil { + t.Logf("Output: %s", output) + t.Fatal(err) + } + + chartPath := filepath.Join(dir, "alpine-0.1.0.tgz") + f, err := os.Open(chartPath) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + gr, err := gzip.NewReader(f) + if err != nil { + t.Fatal(err) + } + defer gr.Close() + + epoch := time.Unix(1700000000, 0).UTC() + tr := tar.NewReader(gr) + for { + hdr, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + t.Fatal(err) + } + if !epoch.Equal(hdr.ModTime) { + t.Errorf("entry %s: got ModTime %v, want %v", hdr.Name, hdr.ModTime, epoch) + } + } + }) +} + func TestPackageFileCompletion(t *testing.T) { checkFileCompletion(t, "package", true) checkFileCompletion(t, "package mypath", true) // Multiple paths can be given