Skip to content

test(bundler): verify no-fs-scan contract and bundle determinism#5886

Merged
killagu merged 6 commits intonextfrom
split/15-bundler-no-fs-scan-test
May 1, 2026
Merged

test(bundler): verify no-fs-scan contract and bundle determinism#5886
killagu merged 6 commits intonextfrom
split/15-bundler-no-fs-scan-test

Conversation

@killagu
Copy link
Copy Markdown
Contributor

@killagu killagu commented Apr 21, 2026

Adds no-filesystem-scan.test.ts (7 tests: setBundleStore short-circuits disk, globFiles skips on cache hit, FileLoader ignores extra on-disk files) and deterministic.test.ts (3 tests: byte-identical across runs, independent clones, no outputDir path leaks). Fixes the ~60% deterministic test flake from #5863 by filtering .egg/ at fs.cp time.

Part of #5863 split. Tracking: #5871.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Added deterministic bundling tests to ensure repeated builds produce byte-identical outputs (aside from a validated timestamp) and to prevent absolute-path leakage; per-test temporary workspaces are created and cleaned up.
    • Added tests verifying manifest-backed deployments skip on-disk scanning and only load files listed in the manifest, with fallback behavior validated.
  • Chores
    • Simplified tsdown config export and changed unused.level from "warn" to "warning".

Copilot AI review requested due to automatic review settings April 21, 2026 15:20
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 21, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 93b0e9f7-eff7-4a60-a80e-125209f3a7d5

📥 Commits

Reviewing files that changed from the base of the PR and between bd631da and d57cbb1.

📒 Files selected for processing (1)
  • tools/egg-bundler/test/deterministic.test.ts

📝 Walkthrough

Walkthrough

Adds two new Vitest test suites for egg-bundler (deterministic bundling and no-filesystem-scan) and simplifies tsdown.config.ts export while changing unused.level from 'warn' to 'warning'. Tests use temporary workspaces and validate manifest-backed behavior and deterministic bundle outputs.

Changes

Cohort / File(s) Summary
Deterministic bundling tests
tools/egg-bundler/test/deterministic.test.ts
New Vitest suite that clones a minimal fixture into per-test temp dirs, writes a fixed .egg/manifest.json, injects a deterministic mocked pack.buildFunc that emits fixed artifacts, compares emitted files byte-for-byte (ignoring bundle-manifest.json.generatedAt), verifies SHA-256 equality across runs, and asserts no absolute output paths leak into emitted UTF-8 files.
No filesystem scan tests
tools/egg-bundler/test/no-filesystem-scan.test.ts
New Vitest suite validating ManifestStore/FileLoader behavior when a StartupManifest is registered: ManifestStore.load returns the registered bundle store without on-disk manifest, ManifestStore.globFiles uses manifest fileDiscovery and falls back only on cache miss, and FileLoader loads only manifest-enumerated files (ignores unrelated files).
Config export tweak
tools/egg-bundler/tsdown.config.ts
Replaced intermediate config constant with direct export default defineConfig(...) and changed unused.level from 'warn' to 'warning'.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

chore: gitAction

Suggested reviewers

  • gxkl
  • fengmk2
  • jerryliang64

Poem

🐰 I hopped through fixtures, tidy and bright,
I cloned the nests and checked each byte,
Manifests hummed and tests took flight,
No stray paths hiding in the night,
Bundles snug — a rabbit's delight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'test(bundler): verify no-fs-scan contract and bundle determinism' accurately reflects the main changes: two new test files specifically targeting no-filesystem-scan behavior and bundle determinism verification.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch split/15-bundler-no-fs-scan-test

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces two new test suites for the egg-bundler tool: deterministic.test.ts ensures that bundling produces byte-identical artifacts across different environments, and no-filesystem-scan.test.ts verifies that the application correctly uses the StartupManifest to bypass directory scans at boot. A review comment suggests refining the file filtering logic in the deterministic tests to prevent over-broad exclusion of files starting with ".egg".

Comment thread tools/egg-bundler/test/deterministic.test.ts
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds regression tests that lock in egg-bundler’s “no filesystem scan” runtime contract and verify bundle determinism, while preventing a known flake caused by .egg/ races when cloning fixtures.

Changes:

  • Add a contract test suite ensuring ManifestStore/FileLoader avoid disk scans when fileDiscovery is present.
  • Add determinism tests asserting byte-identical artifacts across runs and across independent fixture clones, with no outputDir path leakage.
  • Avoid the deterministic test flake by filtering .egg/ (and .egg-bundle/) during fixture cloning.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
tools/egg-bundler/test/no-filesystem-scan.test.ts Tests that bundled manifests short-circuit disk reads and prevent fallback globbing / directory scans.
tools/egg-bundler/test/deterministic.test.ts Tests bundle output determinism across runs/clones and prevents fixture-copy races by excluding .egg* directories.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 30, 2026

Deploying egg-v3 with  Cloudflare Pages  Cloudflare Pages

Latest commit: d57cbb1
Status: ✅  Deploy successful!
Preview URL: https://579112f1.egg-v3.pages.dev
Branch Preview URL: https://split-15-bundler-no-fs-scan.egg-v3.pages.dev

View logs

@killagu killagu force-pushed the split/14-bundler-orchestrator branch from bbf0363 to 6489611 Compare April 30, 2026 15:05
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 30, 2026

Deploying egg with  Cloudflare Pages  Cloudflare Pages

Latest commit: d57cbb1
Status: ✅  Deploy successful!
Preview URL: https://613ed572.egg-cci.pages.dev
Branch Preview URL: https://split-15-bundler-no-fs-scan.egg-cci.pages.dev

View logs

@killagu killagu force-pushed the split/15-bundler-no-fs-scan-test branch from 19cbf72 to 674dd95 Compare April 30, 2026 15:20
@killagu killagu force-pushed the split/14-bundler-orchestrator branch from 6489611 to 510cde1 Compare May 1, 2026 05:42
@killagu killagu force-pushed the split/15-bundler-no-fs-scan-test branch from 674dd95 to d31d8a0 Compare May 1, 2026 05:42
@killagu killagu force-pushed the split/14-bundler-orchestrator branch from 510cde1 to 2e3c087 Compare May 1, 2026 05:45
@killagu killagu force-pushed the split/15-bundler-no-fs-scan-test branch from d31d8a0 to bebc3f2 Compare May 1, 2026 05:45
@killagu killagu force-pushed the split/14-bundler-orchestrator branch from 2e3c087 to 66acb70 Compare May 1, 2026 05:49
Base automatically changed from split/14-bundler-orchestrator to next May 1, 2026 07:21
Copilot AI review requested due to automatic review settings May 1, 2026 07:26
@killagu killagu force-pushed the split/15-bundler-no-fs-scan-test branch from bebc3f2 to 94cec4d Compare May 1, 2026 07:26
@codecov
Copy link
Copy Markdown

codecov Bot commented May 1, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.53%. Comparing base (fccde5d) to head (d57cbb1).
⚠️ Report is 1 commits behind head on next.

Additional details and impacted files
@@           Coverage Diff           @@
##             next    #5886   +/-   ##
=======================================
  Coverage   85.53%   85.53%           
=======================================
  Files         662      662           
  Lines       18888    18888           
  Branches     3664     3664           
=======================================
+ Hits        16155    16156    +1     
+ Misses       2360     2359    -1     
  Partials      373      373           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread tools/egg-bundler/test/deterministic.test.ts Outdated
Comment thread tools/egg-bundler/test/deterministic.test.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tools/egg-bundler/test/deterministic.test.ts (1)

205-236: ⚡ Quick win

Use bundle() file outputs instead of top-level readdir() for drift checks.

Line 227–235 only compares top-level files, so nested outputs could drift unnoticed. You already have result.files + hashByOutputRel() to do a full comparison.

Proposed diff
-    await bundle({
+    const resultA = await bundle({
       baseDir: baseDirA,
       outputDir: outA,
       pack: { buildFunc: makeDeterministicMockBuild(outA) },
     });
-    await bundle({
+    const resultB = await bundle({
       baseDir: baseDirB,
       outputDir: outB,
       pack: { buildFunc: makeDeterministicMockBuild(outB) },
     });
@@
-    const drift: string[] = [];
-    const namesInA = (await fs.readdir(outA)).sort();
-    const namesInB = (await fs.readdir(outB)).sort();
-    expect(namesInA).toEqual(namesInB);
-    for (const name of namesInA) {
+    const drift: string[] = [];
+    const hashesA = await hashByOutputRel(resultA.files, outA);
+    const hashesB = await hashByOutputRel(resultB.files, outB);
+    expect(Object.keys(hashesA).sort()).toEqual(Object.keys(hashesB).sort());
+    for (const name of Object.keys(hashesA)) {
       if (name === 'bundle-manifest.json') continue; // carries baseDir + generatedAt
-      const hashA = await sha256(path.join(outA, name));
-      const hashB = await sha256(path.join(outB, name));
+      const hashA = hashesA[name];
+      const hashB = hashesB[name];
       if (hashA !== hashB) drift.push(name);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/egg-bundler/test/deterministic.test.ts` around lines 205 - 236, The
test currently only compares top-level output files by using
fs.readdir(outA)/outB (namesInA/namesInB) and thus can miss nested drift;
instead use the bundle() return value (result.files) and the helper
hashByOutputRel() to compute and compare hashes for every output path. Update
the two bundle() calls to capture their results (e.g. resultA/resultB), iterate
resultA.files' relative paths, skip "bundle-manifest.json", compute hashes with
hashByOutputRel(resultA, rel) and hashByOutputRel(resultB, rel) (or use
result.hashByOutputRel if available), and collect any mismatches into drift,
then assert drift is empty.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tools/egg-bundler/test/deterministic.test.ts`:
- Around line 189-193: The test only validates bmA.generatedAt's type and
parseability but never checks bmB.generatedAt; update the test to also assert
typeof bmB.generatedAt === 'string' and that new Date(bmB.generatedAt as
string).toString() is not 'Invalid Date' before deleting both fields so both
manifests' timestamps are validated (referencing bmA, bmB, and generatedAt in
the test).

---

Nitpick comments:
In `@tools/egg-bundler/test/deterministic.test.ts`:
- Around line 205-236: The test currently only compares top-level output files
by using fs.readdir(outA)/outB (namesInA/namesInB) and thus can miss nested
drift; instead use the bundle() return value (result.files) and the helper
hashByOutputRel() to compute and compare hashes for every output path. Update
the two bundle() calls to capture their results (e.g. resultA/resultB), iterate
resultA.files' relative paths, skip "bundle-manifest.json", compute hashes with
hashByOutputRel(resultA, rel) and hashByOutputRel(resultB, rel) (or use
result.hashByOutputRel if available), and collect any mismatches into drift,
then assert drift is empty.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 74d35f43-72c1-4865-8b02-502b3842bcf3

📥 Commits

Reviewing files that changed from the base of the PR and between 3609ed6 and b04db9b.

📒 Files selected for processing (1)
  • tools/egg-bundler/test/deterministic.test.ts

Comment thread tools/egg-bundler/test/deterministic.test.ts
Copilot AI review requested due to automatic review settings May 1, 2026 08:42
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tools/egg-bundler/test/deterministic.test.ts (1)

229-236: ⚡ Quick win

Use bundle() result files for recursive clone-output comparison

This block hashes only top-level readdir() entries. If pack later emits nested artifacts, this can fail noisily (EISDIR) and weakens drift diagnostics. Prefer reusing result.files + hashByOutputRel() (same pattern as the first test) so comparison stays recursive and contract-aligned.

Proposed diff
-    await bundle({
+    const resultA = await bundle({
       baseDir: baseDirA,
       outputDir: outA,
       pack: { buildFunc: makeDeterministicMockBuild(outA) },
     });
-    await bundle({
+    const resultB = await bundle({
       baseDir: baseDirB,
       outputDir: outB,
       pack: { buildFunc: makeDeterministicMockBuild(outB) },
     });
@@
-    const drift: string[] = [];
-    const namesInA = (await fs.readdir(outA)).sort();
-    const namesInB = (await fs.readdir(outB)).sort();
-    expect(namesInA).toEqual(namesInB);
-    for (const name of namesInA) {
+    const drift: string[] = [];
+    const hashesA = await hashByOutputRel(resultA.files, outA);
+    const hashesB = await hashByOutputRel(resultB.files, outB);
+    const namesInA = Object.keys(hashesA).sort();
+    const namesInB = Object.keys(hashesB).sort();
+    expect(namesInA).toEqual(namesInB);
+    for (const name of namesInA) {
       if (name === 'bundle-manifest.json') continue; // carries baseDir + generatedAt
-      const hashA = await sha256(path.join(outA, name));
-      const hashB = await sha256(path.join(outB, name));
+      const hashA = hashesA[name];
+      const hashB = hashesB[name];
       if (hashA !== hashB) drift.push(name);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/egg-bundler/test/deterministic.test.ts` around lines 229 - 236, The
test currently compares only top-level readdir() entries and calls sha256 on
those paths (namesInA, namesInB, sha256), which fails for nested directories;
change the comparison to reuse the pack/bundle() results (use the result.files
arrays) and the hashByOutputRel() helper used in the first test to compute
recursive, relative-hash maps for each output, then compare the two maps and
produce drift while still ignoring "bundle-manifest.json"; locate the existing
namesInA/namesInB loop and replace it with building hash maps from resultA.files
and resultB.files via hashByOutputRel(result.files) and compare those maps for
differences.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tools/egg-bundler/test/deterministic.test.ts`:
- Around line 229-236: The test currently compares only top-level readdir()
entries and calls sha256 on those paths (namesInA, namesInB, sha256), which
fails for nested directories; change the comparison to reuse the pack/bundle()
results (use the result.files arrays) and the hashByOutputRel() helper used in
the first test to compute recursive, relative-hash maps for each output, then
compare the two maps and produce drift while still ignoring
"bundle-manifest.json"; locate the existing namesInA/namesInB loop and replace
it with building hash maps from resultA.files and resultB.files via
hashByOutputRel(result.files) and compare those maps for differences.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1be6712d-33e0-4fd5-b630-8368bb6db140

📥 Commits

Reviewing files that changed from the base of the PR and between b04db9b and bd631da.

📒 Files selected for processing (1)
  • tools/egg-bundler/test/deterministic.test.ts

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread tools/egg-bundler/test/deterministic.test.ts Outdated
@killagu killagu merged commit 8dd810d into next May 1, 2026
36 of 37 checks passed
@killagu killagu deleted the split/15-bundler-no-fs-scan-test branch May 1, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants