From fec5f6632a43b7a784653569e8871b76ef9c02d2 Mon Sep 17 00:00:00 2001 From: Haider Date: Fri, 24 Jul 2026 04:23:02 +0530 Subject: [PATCH] test(mcp): restore os.homedir spy after each test to prevent cross-file leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `beforeEach` in `test/mcp/lifecycle.test.ts` installs a `spyOn(os, "homedir")` that returns a fresh `mkdtempSync(... "mcp-lifecycle-home-...")` on every call. Without a matching restore, the spy persists past this file into subsequent files in the same bun worker process. Downstream tests that call `os.homedir()` then get a different random path on every invocation — most visibly the tilde and `$HOME` expansion assertions in `test/permission/next.test.ts`, which see two different `mkdtempSync` paths within the same `expect().toEqual()` call and fail 6 times. The failure was file-order dependent (passes in isolation, fails when the full suite runs in the wrong order), which manifested as a required `CI/TypeScript` check failing intermittently on PRs — including v0.9.3. Fix: add `afterEach(() => mock.restore())` in `lifecycle.test.ts` so the spy is torn down between tests and cannot leak past the file boundary. Blanket `mock.restore()` matches the pattern already used elsewhere in the suite and cleans up any other spies installed during a test. Closes #1042 --- packages/opencode/test/mcp/lifecycle.test.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/opencode/test/mcp/lifecycle.test.ts b/packages/opencode/test/mcp/lifecycle.test.ts index c771fee9d..a085274c3 100644 --- a/packages/opencode/test/mcp/lifecycle.test.ts +++ b/packages/opencode/test/mcp/lifecycle.test.ts @@ -2,7 +2,7 @@ import path from "node:path" import { mkdtempSync } from "node:fs" import os, { tmpdir } from "node:os" import { pathToFileURL } from "node:url" -import { expect, mock, beforeEach, spyOn } from "bun:test" +import { expect, mock, beforeEach, afterEach, spyOn } from "bun:test" import { ListRootsRequestSchema, ToolListChangedNotificationSchema } from "@modelcontextprotocol/sdk/types.js" import { Cause, Effect, Exit } from "effect" import type { MCP as MCPNS } from "../../src/mcp/index" @@ -257,6 +257,16 @@ beforeEach(() => { transportCloseCount = 0 }) +// Restore all spies (including the `os.homedir` spy installed above) after +// every test. Without this the spy persists past this file and pollutes any +// downstream test in the same worker that calls `os.homedir()` — most +// visibly the tilde/`$HOME`-expansion assertions in +// `test/permission/next.test.ts`, which then see two different random +// `mkdtempSync` paths within the same expect(). See issue #1042. +afterEach(() => { + mock.restore() +}) + // Import after mocks const { MCP } = await import("../../src/mcp/index") const { McpOAuthCallback } = await import("../../src/mcp/oauth-callback")