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.
This commit is contained in:
can1357
2026-06-25 20:59:38 +02:00
parent 201b91199f
commit 9ffaace8bc
3 changed files with 18 additions and 4 deletions
@@ -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",
@@ -62,6 +62,7 @@ describe("MCPManager OAuth refresh failure", () => {
});
afterEach(() => {
authStorage.close();
vi.restoreAllMocks();
});
@@ -41,6 +41,7 @@ describe("per-profile MCP OAuth binding", () => {
});
afterEach(() => {
authStorage.close();
setProfile(originalProfile);
vi.restoreAllMocks();
});