Skip to content

test(file-suggestions): stop cross-spawn mock leaking into later suites#1667

Merged
kevincodex1 merged 1 commit into
Gitlawb:mainfrom
0xfandom:fix/filesuggestions-crossspawn-mock-leak
Jun 16, 2026
Merged

test(file-suggestions): stop cross-spawn mock leaking into later suites#1667
kevincodex1 merged 1 commit into
Gitlawb:mainfrom
0xfandom:fix/filesuggestions-crossspawn-mock-leak

Conversation

@0xfandom

@0xfandom 0xfandom commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Problem

src/commands/lsp/lsp.test.ts/lsp recommend > falls back to filesystem scanning when git cannot enumerate workspace files fails intermittently in smoke-and-tests with:

TypeError: child.kill is not a function. (In 'child.kill()', 'child.kill' is undefined)
  at .../src/commands/lsp/lsp.ts (discoverWorkspaceExtensions → execFileNoThrow timeout path)

It reproduces locally on a full bun test src/ run (and on main), but passes in isolation — a classic order-dependent test-isolation bug.

Root cause

src/hooks/fileSuggestions.test.ts mocks cross-spawn via mock.module(...). bun's mock.restore() resets spies but does not undo mock.module — the module mock persists process-wide for the rest of the run. Its interception was gated on options.spawnScenario, captured in the factory closure at install time, so after this suite finished the persisted mock kept returning a fake child (a bare EventEmitter, no kill()) for any git command.

bun runs test files sequentially in one process, so a later suite's real git ls-files (the /lsp recommend filesystem-scan fallback) hit the leaked fake child, which never emits close. execFileNoThrow waited out its 5s timeout, then called child.kill()TypeError, and the 5s wait also blew the test's own timeout.

Fix

  • Gate the cross-spawn interception on a module-level activeSpawnScenario, set only while one of this suite's spawn-scenario tests runs and cleared in afterEach. Once cleared, the persisted mock falls through to the real spawn, so later suites' git commands are untouched.
  • Give the fake child a kill() that emits close, so any stray caller terminates cleanly instead of throwing.

Verification

  • fileSuggestions.test.ts in isolation: 9/9 pass.
  • Full bun test src/ green across repeated runs (previously intermittently red on the /lsp test).
  • tsc --noEmit clean.

Summary by CodeRabbit

  • Tests
    • Improved test infrastructure for file suggestion mocking to ensure consistent behavior and proper cleanup across test scenarios.

No user-facing changes in this release.

fileSuggestions.test.ts installs a cross-spawn mock via mock.module, which
bun does NOT undo on mock.restore() — it persists process-wide. The mock's
interception was gated on a closure captured at install time, so after this
suite the persisted mock kept returning a fake child (with no kill()) for any
git command. Test files run sequentially in one process, so a later suite's
real `git ls-files` (e.g. /lsp recommend's filesystem-scan fallback) hit the
fake child, hung to its 5s timeout, and crashed on child.kill() — an
order-dependent failure in smoke-and-tests.

Gate the interception on a module-level activeSpawnScenario that is set only
while one of this suite's spawn-scenario tests runs and cleared in afterEach,
so the persisted mock falls through to the real spawn afterward. Also give the
fake child a kill() that emits close, so any stray caller terminates cleanly
instead of throwing.

Verified the full src suite is green across repeated runs (was intermittently
red on the /lsp filesystem-scan test).
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2b4d5d22-4ec3-468a-b53a-72f3513993c7

📥 Commits

Reviewing files that changed from the base of the PR and between 7be9dce and 23f6a2c.

📒 Files selected for processing (1)
  • src/hooks/fileSuggestions.test.ts
📜 Recent review details
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: typecheck
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx,py}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Keep comments useful and concise in code

Files:

  • src/hooks/fileSuggestions.test.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Apply the OpenClaude maintainer review rubric from AGENTS.md. Review the current diff, not stale discussion context. Separate real blockers from suggestions. Do not request changes for vague style churn. Treat approval as merge-ready from CodeRabbit's side, pending required human review and GitHub Checks. If checks are failing or unavailable, say so clearly instead of implying the PR is fully ready.

Files:

  • src/hooks/fileSuggestions.test.ts
{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}

⚙️ CodeRabbit configuration file

{src/**/*.test.ts,src/**/*.test.tsx,tests/**,scripts/**/*.test.ts,vscode-extension/**/*.test.js}: Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state, async cleanup, fake timers, provider profile leaks, and Windows-compatible assumptions. Block when risky runtime changes lack focused regression coverage or tests assert implementation details while missing the user-visible behavior.

Files:

  • src/hooks/fileSuggestions.test.ts
**

⚙️ CodeRabbit configuration file

**: # Contributing to OpenClaude

Thanks for contributing.

OpenClaude is a fast-moving open-source coding-agent CLI with support for multiple providers, local backends, MCP, and a terminal-first workflow. The best contributions here are focused, well-tested, and easy to review.

Before You Start

  • Search existing issues and discussions before opening a new thread.
  • Check open pull requests for work that overlaps with your contribution. If a PR already exists that addresses the same change, open an issue or discussion first to align on direction — duplicate PRs may be closed without review.
  • Use issues for confirmed bugs and actionable feature work.
  • Use discussions for setup help, ideas, and general community conversation.
  • For larger changes, open an issue first so the scope is clear before implementation.
  • For security reports, follow SECURITY.md.

Pull Requests

Every PR needs a reason. Your PR description must include:

  • what changed and why
  • the user or developer impact
  • the exact checks you ran
  • a linked issue when one exists, using Fixes fix: skip assertMinVersion for third-party providers #123, `Closes `#123, or another clear link
  • screenshots when the PR touches UI, terminal presentation, or the VS Code extension
  • which provider path was tested when the PR changes provider behavior

The PR author is responsible for ensuring their PR is merge-ready. PRs with merge conflicts will not be reviewed or approved until the conflicts are resolved.

Issues are the recommended starting point for anything non-trivial — opening one first helps avoid wasted effort if the change is out of scope or already being worked on. Small fixes, doc corrections, and obvious improvements can stand on their own without a linked issue, as long as the PR description explains the intent.

What Gets Closed Without Review

PRs may be closed without review...

Files:

  • src/hooks/fileSuggestions.test.ts
🔇 Additional comments (1)
src/hooks/fileSuggestions.test.ts (1)

14-14: LGTM!

Also applies to: 32-38, 47-49, 70-77, 130-130, 134-134, 142-142, 146-146, 174-174


📝 Walkthrough

Walkthrough

The fileSuggestions.test.ts cross-spawn mock is refactored to use a module-level activeSpawnScenario variable instead of per-call options.spawnScenario. The fake child process gains a kill() method that emits a close event. Mock interception is now gated to active git commands only, and afterEach clears the state.

Changes

cross-spawn mock scoping and fake child process fix

Layer / File(s) Summary
FakeChildProcess contract and activeSpawnScenario state
src/hooks/fileSuggestions.test.ts
FakeChildProcess adds a kill() method signature; a module-level activeSpawnScenario is declared with comments about Bun's persistent mock behavior; afterEach clears it; loadFileSuggestionsModule assigns it from options.spawnScenario.
Mock interception logic and kill() implementation
src/hooks/fileSuggestions.test.ts
createFakeChildProcess defines kill() to microtask-emit a SIGTERM close event; the mock's default and spawn functions check activeSpawnScenario and isGitCommand before routing to the fake child or the real cross-spawn.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related PRs

  • Gitlawb/openclaude#1074: Directly modifies the same cross-spawn mock setup in src/hooks/fileSuggestions.test.ts, overlapping with the spawn scenario scoping and fake child behavior changes here.

Suggested reviewers

  • kevincodex1
  • jatmn
  • Vasanthdev2004
🚥 Pre-merge checks | ✅ 6 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: fixing cross-spawn mock persistence affecting later test suites.
Description check ✅ Passed The description comprehensively covers the problem, root cause, fix, and verification, though it diverges from the template structure.
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.
Risk Surface Disclosed ✅ Passed PR is test-only infrastructure fix—modifies mocking and test isolation in fileSuggestions.test.ts. No production code or risk surface areas (auth, permissions, network, startup, CI, plugins, etc.)...
No Hidden Policy Change ✅ Passed PR modifies only test file (fileSuggestions.test.ts) with no policy-related changes. Zero mentions of product, trust-model, routing-default, telemetry/network, or permission-policy in code. Changes...

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@jatmn jatmn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for the contribution. I do not see any actionable issues from my review.

@kevincodex1 LGTM

@kevincodex1 kevincodex1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lgtm

@kevincodex1 kevincodex1 merged commit 8f88608 into Gitlawb:main Jun 16, 2026
4 checks passed
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.

3 participants