From 9ffaace8bc2f725f659bce23bcbdabeb17bf11e8 Mon Sep 17 00:00:00 2001 From: can1357 Date: Thu, 25 Jun 2026 20:59:38 +0200 Subject: [PATCH] test(coding-agent): prevented sqlite database handle leaks in tests - Track and explicitly close in-memory SQLite database handles in MCP auth tests. - Resolved Bun GC-related crashes occurring when parallel tests finalize dangling database handles. --- .../test/mcp-command-reauth.test.ts | 20 +++++++++++++++---- .../test/mcp-manager-oauth-refresh.test.ts | 1 + .../test/mcp-profile-auth-binding.test.ts | 1 + 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/packages/coding-agent/test/mcp-command-reauth.test.ts b/packages/coding-agent/test/mcp-command-reauth.test.ts index a27725a0b..7050f1055 100644 --- a/packages/coding-agent/test/mcp-command-reauth.test.ts +++ b/packages/coding-agent/test/mcp-command-reauth.test.ts @@ -73,6 +73,17 @@ describe("/mcp auth commands", () => { let agentDir = ""; let configPath = ""; let originalMcpHost: string | undefined; + // Track every in-memory auth store so afterEach can close the underlying + // bun:sqlite Database. Leaked Database handles are JSDestructibleObjects that + // JSC otherwise finalizes during an arbitrary later GC sweep — under + // `bun test --parallel` that sweep can run mid-suite on the shared VM and + // trip a Bun GC crash (SIGABRT "Pure virtual function called"). + const openAuthStores: AuthStorage[] = []; + function freshAuthStorage(): AuthStorage { + const storage = new AuthStorage(new SqliteAuthCredentialStore(new Database(":memory:"))); + openAuthStores.push(storage); + return storage; + } beforeAll(() => { initTheme(); @@ -105,6 +116,7 @@ describe("/mcp auth commands", () => { }); afterEach(async () => { + while (openAuthStores.length > 0) openAuthStores.pop()?.close(); vi.restoreAllMocks(); restoreEnvValue("MCP_HOST", originalMcpHost); setProjectDir(originalProjectDir); @@ -119,7 +131,7 @@ describe("/mcp auth commands", () => { }); test("stores definition-only OAuth credentials under the expanded URL key", async () => { - const authStorage = new AuthStorage(new SqliteAuthCredentialStore(new Database(":memory:"))); + const authStorage = freshAuthStorage(); await authStorage.reload(); const connectToServer = vi.spyOn(mcpClient, "connectToServer").mockRejectedValue(AUTH_ERROR); vi.spyOn(oauthFlow.MCPOAuthFlow.prototype, "login").mockResolvedValue({ @@ -156,7 +168,7 @@ describe("/mcp auth commands", () => { }); test("reuses embedded DCR client secret during reauth token exchange", async () => { - const authStorage = new AuthStorage(new SqliteAuthCredentialStore(new Database(":memory:"))); + const authStorage = freshAuthStorage(); await authStorage.reload(); await authStorage.set(oauthFlow.mcpOAuthCredentialId(EXPANDED_SERVER_URL), { type: "oauth", @@ -201,7 +213,7 @@ describe("/mcp auth commands", () => { }); test("clears both expanded and stale raw URL-keyed credentials on unauth", async () => { - const authStorage = new AuthStorage(new SqliteAuthCredentialStore(new Database(":memory:"))); + const authStorage = freshAuthStorage(); await authStorage.reload(); await authStorage.set(oauthFlow.mcpOAuthCredentialId(EXPANDED_SERVER_URL), { type: "oauth", @@ -230,7 +242,7 @@ describe("/mcp auth commands", () => { }); test("clears url-keyed auth for discovered definition-only servers", async () => { - const authStorage = new AuthStorage(new SqliteAuthCredentialStore(new Database(":memory:"))); + const authStorage = freshAuthStorage(); await authStorage.reload(); await authStorage.set(oauthFlow.mcpOAuthCredentialId(EXPANDED_SERVER_URL), { type: "oauth", diff --git a/packages/coding-agent/test/mcp-manager-oauth-refresh.test.ts b/packages/coding-agent/test/mcp-manager-oauth-refresh.test.ts index 2a6936137..8d19764ea 100644 --- a/packages/coding-agent/test/mcp-manager-oauth-refresh.test.ts +++ b/packages/coding-agent/test/mcp-manager-oauth-refresh.test.ts @@ -62,6 +62,7 @@ describe("MCPManager OAuth refresh failure", () => { }); afterEach(() => { + authStorage.close(); vi.restoreAllMocks(); }); diff --git a/packages/coding-agent/test/mcp-profile-auth-binding.test.ts b/packages/coding-agent/test/mcp-profile-auth-binding.test.ts index 173556de2..7e3c5a901 100644 --- a/packages/coding-agent/test/mcp-profile-auth-binding.test.ts +++ b/packages/coding-agent/test/mcp-profile-auth-binding.test.ts @@ -41,6 +41,7 @@ describe("per-profile MCP OAuth binding", () => { }); afterEach(() => { + authStorage.close(); setProfile(originalProfile); vi.restoreAllMocks(); });