From 0de745334973b0ff50347fd9066e8fa215ea7cbf Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Mon, 6 Apr 2026 18:20:28 +0900 Subject: [PATCH] fix(tests): fix globalThis.fetch pollution between test files - install.test.ts: save and restore globalThis.fetch in beforeEach/afterEach to prevent leaking a mock fetch (without .preconnect) into subsequent test files - provider.test.ts: guard against originalFetch missing .preconnect when captured from a leaked mock (defensive null-safe binding) - discovery.test.ts: add writable:true to all Object.defineProperty fetch assignments so downstream plain assignments (globalThis.fetch = ...) are not silently ignored Root cause: install.test.ts set globalThis.fetch = mock(...) inside test bodies without restoring it, leaving a mock fetch (no .preconnect method) that caused provider.test.ts refresh tests to throw TypeError at the fetchMock construction --- src/cli/install.test.ts | 3 +++ src/features/mcp-oauth/discovery.test.ts | 16 ++++++++-------- src/features/mcp-oauth/provider.test.ts | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/cli/install.test.ts b/src/cli/install.test.ts index 94a891832..cf4b7f633 100644 --- a/src/cli/install.test.ts +++ b/src/cli/install.test.ts @@ -13,6 +13,7 @@ const mockConsoleError = mock(() => {}) describe("install CLI - binary check behavior", () => { let tempDir: string let originalEnv: string | undefined + let originalFetch: typeof globalThis.fetch let isOpenCodeInstalledSpy: ReturnType let getOpenCodeVersionSpy: ReturnType @@ -20,6 +21,7 @@ describe("install CLI - binary check behavior", () => { // given temporary config directory tempDir = join(tmpdir(), `omo-test-${Date.now()}-${Math.random().toString(36).slice(2)}`) mkdirSync(tempDir, { recursive: true }) + originalFetch = globalThis.fetch originalEnv = process.env.OPENCODE_CONFIG_DIR process.env.OPENCODE_CONFIG_DIR = tempDir @@ -46,6 +48,7 @@ describe("install CLI - binary check behavior", () => { isOpenCodeInstalledSpy?.mockRestore() getOpenCodeVersionSpy?.mockRestore() + globalThis.fetch = originalFetch }) test("non-TUI mode: should show warning but continue when OpenCode binary not found", async () => { diff --git a/src/features/mcp-oauth/discovery.test.ts b/src/features/mcp-oauth/discovery.test.ts index 5253b200e..5bbb464d2 100644 --- a/src/features/mcp-oauth/discovery.test.ts +++ b/src/features/mcp-oauth/discovery.test.ts @@ -9,7 +9,7 @@ describe("discoverOAuthServerMetadata", () => { }) afterEach(() => { - Object.defineProperty(globalThis, "fetch", { value: originalFetch, configurable: true }) + Object.defineProperty(globalThis, "fetch", { value: originalFetch, configurable: true, writable: true }) }) test("returns endpoints from PRM + AS discovery", () => { @@ -37,7 +37,7 @@ describe("discoverOAuthServerMetadata", () => { } return new Response("not found", { status: 404 }) } - Object.defineProperty(globalThis, "fetch", { value: fetchMock, configurable: true }) + Object.defineProperty(globalThis, "fetch", { value: fetchMock, configurable: true, writable: true }) // when return discoverOAuthServerMetadata(resource).then((result) => { @@ -75,7 +75,7 @@ describe("discoverOAuthServerMetadata", () => { } return new Response("not found", { status: 404 }) } - Object.defineProperty(globalThis, "fetch", { value: fetchMock, configurable: true }) + Object.defineProperty(globalThis, "fetch", { value: fetchMock, configurable: true, writable: true }) // when return discoverOAuthServerMetadata(resource).then((result) => { @@ -118,7 +118,7 @@ describe("discoverOAuthServerMetadata", () => { } return new Response("not found", { status: 404 }) } - Object.defineProperty(globalThis, "fetch", { value: fetchMock, configurable: true }) + Object.defineProperty(globalThis, "fetch", { value: fetchMock, configurable: true, writable: true }) // when return discoverOAuthServerMetadata(resource).then((result) => { @@ -144,7 +144,7 @@ describe("discoverOAuthServerMetadata", () => { } return new Response("not found", { status: 404 }) } - Object.defineProperty(globalThis, "fetch", { value: fetchMock, configurable: true }) + Object.defineProperty(globalThis, "fetch", { value: fetchMock, configurable: true, writable: true }) // when const result = discoverOAuthServerMetadata(resource) @@ -165,7 +165,7 @@ describe("discoverOAuthServerMetadata", () => { } return new Response("not found", { status: 404 }) } - Object.defineProperty(globalThis, "fetch", { value: fetchMock, configurable: true }) + Object.defineProperty(globalThis, "fetch", { value: fetchMock, configurable: true, writable: true }) // when const result = discoverOAuthServerMetadata(resource) @@ -192,7 +192,7 @@ describe("discoverOAuthServerMetadata", () => { } return new Response("not found", { status: 404 }) } - Object.defineProperty(globalThis, "fetch", { value: fetchMock, configurable: true }) + Object.defineProperty(globalThis, "fetch", { value: fetchMock, configurable: true, writable: true }) // when const result = discoverOAuthServerMetadata(resource) @@ -225,7 +225,7 @@ describe("discoverOAuthServerMetadata", () => { } return new Response("not found", { status: 404 }) } - Object.defineProperty(globalThis, "fetch", { value: fetchMock, configurable: true }) + Object.defineProperty(globalThis, "fetch", { value: fetchMock, configurable: true, writable: true }) // when return discoverOAuthServerMetadata(resource) diff --git a/src/features/mcp-oauth/provider.test.ts b/src/features/mcp-oauth/provider.test.ts index 98a9b3bdc..a8d783388 100644 --- a/src/features/mcp-oauth/provider.test.ts +++ b/src/features/mcp-oauth/provider.test.ts @@ -287,7 +287,7 @@ describe("McpOAuthProvider", () => { }) const fetchMock = Object.assign( async (...args: Parameters): ReturnType => fetchStub(...args), - { preconnect: originalFetch.preconnect.bind(originalFetch) }, + { preconnect: originalFetch?.preconnect?.bind(originalFetch) ?? (() => {}) }, ) satisfies typeof fetch globalThis.fetch = fetchMock