From f2d339dfa46cd001c6613fd0732ee17f271e35a9 Mon Sep 17 00:00:00 2001 From: Maxime Grenu Date: Wed, 11 Mar 2026 11:34:13 +0100 Subject: [PATCH] test(util): add end-to-end and dependency tests for SOURCE_DATE_EPOCH Add TestSaveWithSourceDateEpoch to both v2 and v3 util packages to verify the full pipeline: parse the epoch, stamp the chart tree, save to a tar archive, then assert that every tar entry carries exactly the expected timestamp. This catches any regression where writeToTar might silently fall back to time.Now(). Also add TestApplySourceDateEpochDependencies to confirm that the recursive walk correctly stamps sub-chart entries while preserving non-zero ModTimes on the parent chart. Signed-off-by: Maxime Grenu --- internal/chart/v3/util/epoch_test.go | 93 +++++++++++++++++++++++++++ pkg/chart/v2/util/epoch_test.go | 95 ++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) diff --git a/internal/chart/v3/util/epoch_test.go b/internal/chart/v3/util/epoch_test.go index 0db37b3d9..7fd9f37ef 100644 --- a/internal/chart/v3/util/epoch_test.go +++ b/internal/chart/v3/util/epoch_test.go @@ -156,3 +156,96 @@ func TestApplySourceDateEpochZeroNoop(t *testing.T) { t.Errorf("Chart.ModTime = %v, want zero", c.ModTime) } } + +func TestApplySourceDateEpochDependencies(t *testing.T) { + epoch := time.Unix(1700000000, 0) + existing := time.Unix(1600000000, 0) + + dep := &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "dep", + Version: "0.1.0", + }, + Templates: []*common.File{ + {Name: "templates/dep.yaml"}, + }, + } + + c := &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "parent", + Version: "1.0.0", + }, + ModTime: existing, + Templates: []*common.File{ + {Name: "templates/main.yaml"}, + }, + } + c.AddDependency(dep) + + ApplySourceDateEpoch(c, epoch) + + // Parent chart already had a ModTime, so it should be preserved. + if !c.ModTime.Equal(existing) { + t.Errorf("parent Chart.ModTime = %v, want existing %v", c.ModTime, existing) + } + // Dependency had a zero ModTime, so it should be stamped. + if !dep.ModTime.Equal(epoch) { + t.Errorf("dep Chart.ModTime = %v, want %v", dep.ModTime, epoch) + } + for _, f := range dep.Templates { + if !f.ModTime.Equal(epoch) { + t.Errorf("dep Template %s ModTime = %v, want %v", f.Name, f.ModTime, epoch) + } + } +} + +func TestSaveWithSourceDateEpoch(t *testing.T) { + // End-to-end: parse SOURCE_DATE_EPOCH, apply to a chart with zero + // ModTimes, save as a tar archive, and verify every tar entry carries + // exactly the expected timestamp. + const epochStr = "1700000000" + want := time.Unix(1700000000, 0) + + t.Setenv("SOURCE_DATE_EPOCH", epochStr) + + epoch, err := ParseSourceDateEpoch() + if err != nil { + t.Fatalf("ParseSourceDateEpoch() error: %v", err) + } + + c := &chart.Chart{ + Metadata: &chart.Metadata{ + APIVersion: chart.APIVersionV3, + Name: "epoch-test", + Version: "0.1.0", + }, + Values: map[string]any{"key": "value"}, + Schema: []byte(`{"title": "Values"}`), + Files: []*common.File{{Name: "README.md", Data: []byte("# test")}}, + Templates: []*common.File{{Name: "templates/test.yaml", Data: []byte("apiVersion: v1")}}, + } + + ApplySourceDateEpoch(c, epoch) + + tmp := t.TempDir() + where, err := Save(c, tmp) + if err != nil { + t.Fatalf("Save() error: %v", err) + } + + headers, err := retrieveAllHeadersFromTar(where) + if err != nil { + t.Fatalf("failed to read tar: %v", err) + } + + if len(headers) == 0 { + t.Fatal("archive contains no entries") + } + + for _, h := range headers { + if !h.ModTime.Equal(want) { + t.Errorf("tar entry %q ModTime = %v, want %v", h.Name, h.ModTime, want) + } + } +} diff --git a/pkg/chart/v2/util/epoch_test.go b/pkg/chart/v2/util/epoch_test.go index 3df89ba07..5a47c2589 100644 --- a/pkg/chart/v2/util/epoch_test.go +++ b/pkg/chart/v2/util/epoch_test.go @@ -156,3 +156,98 @@ func TestApplySourceDateEpochZeroNoop(t *testing.T) { t.Errorf("Chart.ModTime = %v, want zero", c.ModTime) } } + +func TestApplySourceDateEpochDependencies(t *testing.T) { + epoch := time.Unix(1700000000, 0) + existing := time.Unix(1600000000, 0) + + dep := &chart.Chart{ + Metadata: &chart.Metadata{ + APIVersion: chart.APIVersionV2, + Name: "dep", + Version: "0.1.0", + }, + Templates: []*common.File{ + {Name: "templates/dep.yaml"}, + }, + } + + c := &chart.Chart{ + Metadata: &chart.Metadata{ + APIVersion: chart.APIVersionV2, + Name: "parent", + Version: "1.0.0", + }, + ModTime: existing, + Templates: []*common.File{ + {Name: "templates/main.yaml"}, + }, + } + c.AddDependency(dep) + + ApplySourceDateEpoch(c, epoch) + + // Parent chart already had a ModTime, so it should be preserved. + if !c.ModTime.Equal(existing) { + t.Errorf("parent Chart.ModTime = %v, want existing %v", c.ModTime, existing) + } + // Dependency had a zero ModTime, so it should be stamped. + if !dep.ModTime.Equal(epoch) { + t.Errorf("dep Chart.ModTime = %v, want %v", dep.ModTime, epoch) + } + for _, f := range dep.Templates { + if !f.ModTime.Equal(epoch) { + t.Errorf("dep Template %s ModTime = %v, want %v", f.Name, f.ModTime, epoch) + } + } +} + +func TestSaveWithSourceDateEpoch(t *testing.T) { + // End-to-end: parse SOURCE_DATE_EPOCH, apply to a chart with zero + // ModTimes, save as a tar archive, and verify every tar entry carries + // exactly the expected timestamp. + const epochStr = "1700000000" + want := time.Unix(1700000000, 0) + + t.Setenv("SOURCE_DATE_EPOCH", epochStr) + + epoch, err := ParseSourceDateEpoch() + if err != nil { + t.Fatalf("ParseSourceDateEpoch() error: %v", err) + } + + c := &chart.Chart{ + Metadata: &chart.Metadata{ + APIVersion: chart.APIVersionV2, + Name: "epoch-test", + Version: "0.1.0", + }, + Values: map[string]any{"key": "value"}, + Schema: []byte(`{"title": "Values"}`), + Files: []*common.File{{Name: "README.md", Data: []byte("# test")}}, + Templates: []*common.File{{Name: "templates/test.yaml", Data: []byte("apiVersion: v1")}}, + } + + ApplySourceDateEpoch(c, epoch) + + tmp := t.TempDir() + where, err := Save(c, tmp) + if err != nil { + t.Fatalf("Save() error: %v", err) + } + + headers, err := retrieveAllHeadersFromTar(where) + if err != nil { + t.Fatalf("failed to read tar: %v", err) + } + + if len(headers) == 0 { + t.Fatal("archive contains no entries") + } + + for _, h := range headers { + if !h.ModTime.Equal(want) { + t.Errorf("tar entry %q ModTime = %v, want %v", h.Name, h.ModTime, want) + } + } +}