From 8b09ab3afcfd21b92a4231b96689677e39d15c90 Mon Sep 17 00:00:00 2001 From: PJ Date: Sun, 3 May 2026 11:10:54 +0700 Subject: [PATCH] test(bundler): lock down deterministic output across builds The review flagged map-iteration nondeterminism as a possible cause of unstable bundle SHAs. Empirically esbuild's Define handling is order- independent (parallel substitution rules), so output is already stable. Add a regression test that builds 10x with multiple Defines and asserts SHA equality so any future change that introduces ordering surfaces. --- internal/bundler/web_bundle_test.go | 39 +++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/internal/bundler/web_bundle_test.go b/internal/bundler/web_bundle_test.go index bd939cb..0863101 100644 --- a/internal/bundler/web_bundle_test.go +++ b/internal/bundler/web_bundle_test.go @@ -83,6 +83,45 @@ func TestBundleWeb_RejectsMissingEntry(t *testing.T) { } } +// TestBundleWeb_IsDeterministic builds the same source repeatedly with multiple +// Defines entries and asserts the SHA matches. This guards against any future +// change that introduces map-iteration ordering into the bundle output. +func TestBundleWeb_IsDeterministic(t *testing.T) { + directory := t.TempDir() + runtimePath := filepath.Join(directory, "web-runtime.ts") + specPath := filepath.Join(directory, "spec.ts") + if err := os.WriteFile(runtimePath, []byte(fakeRuntime), 0o644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(specPath, []byte(fakeSpec), 0o644); err != nil { + t.Fatal(err) + } + + options := WebOptions{ + EntryFile: specPath, + WebRuntimeFile: runtimePath, + Defines: map[string]string{ + "API_BASE": "https://example.com", + "FEATURE": "true", + "VERSION": "1.2.3", + "TENANT": "alpha", + }, + } + first, err := BundleWeb(options) + if err != nil { + t.Fatalf("first build: %v", err) + } + for attempt := range 10 { + repeat, err := BundleWeb(options) + if err != nil { + t.Fatalf("attempt %d: %v", attempt, err) + } + if repeat.SHA256 != first.SHA256 { + t.Fatalf("attempt %d: SHA changed (%s vs %s)", attempt, repeat.SHA256, first.SHA256) + } + } +} + func head(text string, max int) string { if len(text) <= max { return text