Give mock.module its own automatic teardown - #584
Merged
TheGreatAxios merged 1 commit intoAug 23, 2026
Merged
Conversation
Bun runs every test file in one process, so a mock.module call without its own restore stays installed for the rest of the run and silently replaces the real module for other files. Route every call through a withMockedModule/withMockedModuleDuring helper that captures the real module and registers its own restore, and reject bare mock.module in test files with an eslint rule so the mistake cannot recur.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes CL-6967.
Summary
withMockedModule/withMockedModuleDuringintests/helpers/mock-module.ts, which capture a module's real exports, install the mock, and register their own restore (afterAll, or immediately after a scoped call) — so a mock can no longer outlive the file that installed it.mock.modulecall site to the helper:tests/unit/config.test.ts,tests/unit/tui/agent-tools.test.ts,src/tools/web-search.test.ts,src/auth/oauth-scope-check.test.ts,src/auth/codex/instructions.test.ts,src/tui/provider-setup-submit.test.ts,src/tui/mouse-reporting-disabled.test.ts,src/session/state.test.ts(8 files callmock.module, not the 6 originally reported;config.test.tsrestored correctly viatry/finallyrather than having no teardown at all, but it still benefited from moving onto the shared helper).no-restricted-syntaxrule scoped to**/*.test.tsthat rejects a baremock.modulecall outside the helper, with a message pointing at the replacement.noInlineConfigis already on for this repo, so there is no per-line escape hatch.AGENTS.md, next to the existing mock/restore guidance.Why
Bun runs every test file in one process. A
mock.modulecall with no restore of its own stays installed for the rest of the run and silently replaces the real module for every file that runs after it — producing failures in files the change never touched, invisible totscand to a per-file test run. This happened for real landing CL-5710 (5 failures in an untouched file, first misdiagnosed as an import cycle). Routing every mock through a helper that owns its own teardown removes the class of bug instead of relying on remembering to addafterAll.Proof
Eslint catches a bare
mock.modulecall:Removing the helper's own
afterAllrestore (simulating a leak that slipped past the lint rule) turns a clean full-suite run into 23 unrelated failures across files that never touch the mocked modules — confirming the helper's restore is what keepsmock.modulefrom leaking:Restoring the helper brings the suite back to green.
Verification
bun run lint,bun run typecheck, andbun run buildpassbun run test(bun test ./src ./tests ./evals) passes clean at 5345/5345 when run without contention from other concurrent processes on this machine; under heavy unrelated machine load some timing-sensitive TUI/shell/signal tests flake on timeouts, but every flake is in a file this PR does not touch and none involvemock.module