Skip to content

test: stub user-preference fetches in global setup to stop a cross-test ECONNREFUSED leak#60

Merged
DavidBabinec merged 1 commit into
mainfrom
fix/test-preference-fetch-stub
Jun 13, 2026
Merged

test: stub user-preference fetches in global setup to stop a cross-test ECONNREFUSED leak#60
DavidBabinec merged 1 commit into
mainfrom
fix/test-preference-fetch-stub

Conversation

@DavidBabinec

Copy link
Copy Markdown
Contributor

Follow-up to #58/#59. Final root cause of the canvas-test flake that was failing the release Verify step.

Root cause

Admin surfaces load user preferences on mount — e.g. module-inserter favourites via useModuleInserterPreferencegetUserPreference → a real fetch to /admin/api/cms/me/preferences/<key>. In tests that hits localhost and rejects with ECONNREFUSED.

The preference store is a module-level singleton whose load promise can outlive the component that triggered it. So the rejection isn't caught by the originating test — it surfaces during a later test's cleanup() (when React unmounts the leftover tree inside act), bundled as an AggregateError. That's why the failure landed on canvasFrameMounting's first test even though that test doesn't fetch anything.

It was masked while canvas frames mounted slowly (the breakpoint tests timed out before the preference fetch settled) and reappeared once #58 made frames mount synchronously — a pure render-timing change exposing a pre-existing leak.

Fix

One global stub in the test preload (src/__tests__/setup.ts): return the server's "never set" envelope ({ value: null }) for preference GETs, so the load resolves cleanly to each caller's own default with no network call. Suite-wide, there's no rejection left to leak.

  • Tests that need specific preference data still override globalThis.fetch in their own setup (verified: the dedicated module-inserter preference tests pass).
  • getUserPreference callers that inject a fetchImpl bypass this entirely.

Verification

  • Proven the stub intercepts: an unmocked preference GET now returns 200 {value:null} and getUserPreference('module-inserter') resolves to null.
  • Dedicated preference + canvas tests pass.
  • Full suite: bun test 5432 pass / 0 fail; bun run build + bun run lint clean.

🤖 Generated with Claude Code

…s-test leak

Admin surfaces load user preferences on mount (e.g. module-inserter
favourites via useModuleInserterPreference → getUserPreference), firing a
real fetch to /admin/api/cms/me/preferences/<key>. In tests that hits
localhost and rejects with ECONNREFUSED. The preference store is a
module-level singleton whose load promise can outlive the component that
triggered it, so the rejection surfaces during a LATER test's cleanup() as
an AggregateError — a render-timing-dependent cross-test flake.

It was masked while canvas frames mounted slowly (the tests timed out before
the preference fetch settled) and reappeared once frames mount synchronously
(#58), failing the release Verify step on canvasFrameMounting's first test.

Return the server's "never set" envelope ({ value: null }) for preference
GETs in the global test preload, so the load resolves cleanly to each
caller's own default with no network call. Tests that need specific
preference data still override globalThis.fetch in their own setup;
getUserPreference callers that inject a fetchImpl bypass this entirely.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@DavidBabinec DavidBabinec merged commit 5e309bf into main Jun 13, 2026
5 checks passed
@DavidBabinec DavidBabinec deleted the fix/test-preference-fetch-stub branch June 13, 2026 09:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant