From 8d060c23b41fc18c735c7094fba33f0980bec731 Mon Sep 17 00:00:00 2001 From: zirdev <3856578+Zireael@users.noreply.github.com> Date: Mon, 25 May 2026 22:51:39 +0200 Subject: [PATCH] test: fix EBUSY database lock failures on Windows during teardown The test suite intermittently fails on Windows with 'EBUSY: resource busy or locked' errors when attempting to clean up temporary directories. This happens because SQLite file handles take a moment to release after closing the database connections. Wrapped all rmSync calls in afterEach teardown hooks across the project in try/catch blocks that ignore the error on Windows, and added maxRetries: 10 and retryDelay: 100 to provide a grace period for handles to release. --- packages/cli/src/commands/migrate.test.ts | 8 ++++- packages/plugin/src/config/index.test.ts | 24 ++++++++++++--- packages/plugin/src/config/variable.test.ts | 6 +++- .../boundary-execution-cas-race.test.ts | 15 +++++++--- .../magic-context/compartment-lease.test.ts | 29 +++++++++++++++---- .../compartment-storage-v6.test.ts | 8 ++++- .../compression-depth-storage.test.ts | 6 +++- .../key-files/project-key-files.test.ts | 7 ++++- .../magic-context/memory/embedding.test.ts | 6 +++- .../magic-context/migrations-v11.test.ts | 10 ++++++- .../magic-context/migrations-v12.test.ts | 8 ++++- .../magic-context/migrations-v13.test.ts | 8 ++++- .../magic-context/migrations-v16.test.ts | 8 ++++- .../migrations-v18-pi-marker.test.ts | 8 ++++- .../magic-context/migrations-v20.test.ts | 6 +++- .../magic-context/migrations-v21.test.ts | 6 +++- .../project-embedding-registry.test.ts | 6 +++- .../resolve-subagent-fallback.test.ts | 6 +++- .../sticky-injection-cas-race.test.ts | 24 ++++++++++++--- .../storage-db-migration.test.ts | 6 +++- .../features/magic-context/storage-db.test.ts | 6 +++- .../features/magic-context/storage.test.ts | 6 +++- .../magic-context/tagger-recovery.test.ts | 6 +++- .../magic-context/tool-owner-backfill.test.ts | 6 +++- .../hooks/auto-update-checker/index.test.ts | 6 +++- .../apply-operations.tool-drop.test.ts | 6 +++- .../compaction-marker-consistency.test.ts | 6 +++- .../compaction-marker-manager.test.ts | 8 +++-- .../compartment-runner-drop-queue.test.ts | 6 +++- .../compartment-runner-timeout.test.ts | 6 +++- .../magic-context/compartment-runner.test.ts | 12 ++++++-- .../magic-context/compartment-trigger.test.ts | 6 +++- .../hooks/magic-context/event-handler.test.ts | 6 +++- .../historian-state-file.test.ts | 6 +++- .../src/hooks/magic-context/hook.test.ts | 6 +++- .../src/hooks/magic-context/nudger.test.ts | 6 +++- .../magic-context/read-session-chunk.test.ts | 6 +++- .../magic-context/read-session-db.test.ts | 6 +++- .../magic-context/system-prompt-hash.test.ts | 6 +++- .../tag-messages-collision.test.ts | 6 +++- .../tool-input-preservation.test.ts | 6 +++- .../transform-cache-busting-signals.test.ts | 6 +++- .../transform-compartment-phase.test.ts | 7 ++++- .../transform-context-state.test.ts | 6 +++- ...form-heuristic-cleanup-persistence.test.ts | 6 +++- .../transform-index-staleness.test.ts | 6 +++- .../transform-nudge-cache.test.ts | 6 +++- .../transform-operations.test.ts | 6 +++- .../transform-todo-state.test.ts | 7 ++++- .../src/hooks/magic-context/transform.test.ts | 6 +++- .../plugin/src/shared/announcement.test.ts | 3 +- .../src/shared/conflict-detector.test.ts | 17 +++++++++-- .../plugin/src/shared/conflict-fixer.test.ts | 6 +++- .../src/shared/models-dev-cache.test.ts | 6 +++- .../opencode-compaction-detector.test.ts | 12 ++++++-- packages/plugin/src/shared/rpc-client.test.ts | 6 +++- 56 files changed, 372 insertions(+), 73 deletions(-) diff --git a/packages/cli/src/commands/migrate.test.ts b/packages/cli/src/commands/migrate.test.ts index 67e3c414e..436427b0d 100644 --- a/packages/cli/src/commands/migrate.test.ts +++ b/packages/cli/src/commands/migrate.test.ts @@ -183,7 +183,13 @@ function makeCortexkitDb() { afterEach(() => { for (const db of databases.splice(0)) db.close(); - for (const dir of tempDirs.splice(0)) rmSync(dir, { recursive: true, force: true }); + for (const dir of tempDirs.splice(0)) { + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } + } }); describe("migrateOpenCodeSessionToPi", () => { diff --git a/packages/plugin/src/config/index.test.ts b/packages/plugin/src/config/index.test.ts index c7f406e4d..e5ec764a4 100644 --- a/packages/plugin/src/config/index.test.ts +++ b/packages/plugin/src/config/index.test.ts @@ -42,8 +42,16 @@ function loadWithUserConfig(configText: string, extraEnv: Record if (v === undefined) delete process.env[k]; else process.env[k] = v; } - rmSync(xdg, { recursive: true, force: true }); - rmSync(projectDir, { recursive: true, force: true }); + try { + rmSync(xdg, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } + try { + rmSync(projectDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } } @@ -81,8 +89,16 @@ function loadWithUserAndProjectConfig( if (v === undefined) delete process.env[k]; else process.env[k] = v; } - rmSync(xdg, { recursive: true, force: true }); - rmSync(projectDir, { recursive: true, force: true }); + try { + rmSync(xdg, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } + try { + rmSync(projectDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } } diff --git a/packages/plugin/src/config/variable.test.ts b/packages/plugin/src/config/variable.test.ts index cf52a70df..d7db1e6ab 100644 --- a/packages/plugin/src/config/variable.test.ts +++ b/packages/plugin/src/config/variable.test.ts @@ -14,7 +14,11 @@ describe("substituteConfigVariables", () => { }); afterEach(() => { - rmSync(tmpDir, { recursive: true, force: true }); + try { + rmSync(tmpDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } process.env = { ...ORIGINAL_ENV }; }); diff --git a/packages/plugin/src/features/magic-context/boundary-execution-cas-race.test.ts b/packages/plugin/src/features/magic-context/boundary-execution-cas-race.test.ts index 3391b310f..9a5c8769e 100644 --- a/packages/plugin/src/features/magic-context/boundary-execution-cas-race.test.ts +++ b/packages/plugin/src/features/magic-context/boundary-execution-cas-race.test.ts @@ -5,6 +5,7 @@ import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { Database } from "../../shared/sqlite"; +import { closeQuietly } from "../../shared/sqlite-helpers"; import { type DeferredExecutePayload, peekDeferredExecutePending, @@ -50,16 +51,22 @@ function payload(id: string): DeferredExecutePayload { describe("deferred execute CAS race", () => { it("15. one WAL handle wins set-if-absent and the other no-ops", () => { const dir = mkdtempSync(join(tmpdir(), "boundary-exec-race-")); + const path = join(dir, "context.db"); + const a = createRaceDb(path); + const b = createRaceDb(path); try { - const path = join(dir, "context.db"); - const a = createRaceDb(path); - const b = createRaceDb(path); const first = setDeferredExecutePendingIfAbsent(a, "s1", payload("a")); const second = setDeferredExecutePendingIfAbsent(b, "s1", payload("b")); expect([first, second].filter(Boolean)).toHaveLength(1); expect(peekDeferredExecutePending(a, "s1")?.id).toBe(first ? "a" : "b"); } finally { - rmSync(dir, { recursive: true, force: true }); + closeQuietly(a); + closeQuietly(b); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } } }); }); diff --git a/packages/plugin/src/features/magic-context/compartment-lease.test.ts b/packages/plugin/src/features/magic-context/compartment-lease.test.ts index 6dd1e2c65..676ad89a8 100644 --- a/packages/plugin/src/features/magic-context/compartment-lease.test.ts +++ b/packages/plugin/src/features/magic-context/compartment-lease.test.ts @@ -43,10 +43,12 @@ describe("compartment state lease", () => { const db = makeDb(); const first = acquireCompartmentLease(db, "ses", "holder-a"); expect(first).not.toBeNull(); + db.prepare("UPDATE compartment_state_lease SET expires_at = ? WHERE session_id = ?").run( Date.now() + 1_000, "ses", ); + const second = acquireCompartmentLease(db, "ses", "holder-a"); expect(second).not.toBeNull(); expect(second!.expiresAt).toBeGreaterThan(first!.acquiredAt + 1_000); @@ -56,10 +58,12 @@ describe("compartment state lease", () => { it("lets another holder reclaim an expired lease", () => { const db = makeDb(); expect(acquireCompartmentLease(db, "ses", "holder-a")).not.toBeNull(); + db.prepare("UPDATE compartment_state_lease SET expires_at = ? WHERE session_id = ?").run( Date.now() - 1, "ses", ); + expect(acquireCompartmentLease(db, "ses", "holder-b")).not.toBeNull(); expect(isCompartmentLeaseHeld(db, "ses", "holder-b")).toBe(true); expect(isCompartmentLeaseHeld(db, "ses", "holder-a")).toBe(false); @@ -70,6 +74,7 @@ describe("compartment state lease", () => { const db = makeDb(); expect(acquireCompartmentLease(db, "ses", "holder-a")).not.toBeNull(); expect(renewCompartmentLease(db, "ses", "holder-b")).toBe(false); + db.prepare("UPDATE compartment_state_lease SET expires_at = ? WHERE session_id = ?").run( Date.now() - 1, "ses", @@ -81,10 +86,12 @@ describe("compartment state lease", () => { it("release is a no-op after another holder reclaims the row", () => { const db = makeDb(); expect(acquireCompartmentLease(db, "ses", "holder-a")).not.toBeNull(); + db.prepare("UPDATE compartment_state_lease SET expires_at = ? WHERE session_id = ?").run( Date.now() - 1, "ses", ); + expect(acquireCompartmentLease(db, "ses", "holder-b")).not.toBeNull(); releaseCompartmentLease(db, "ses", "holder-a"); expect(isCompartmentLeaseHeld(db, "ses", "holder-b")).toBe(true); @@ -105,7 +112,11 @@ describe("compartment state lease", () => { } finally { closeQuietly(dbA); closeQuietly(dbB); - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } } }); @@ -114,10 +125,13 @@ describe("compartment state lease", () => { const path = join(dir, "context.db"); const setup = makeDb(path); closeQuietly(setup); + try { - const pluginRoot = process.cwd().endsWith("/packages/plugin") - ? process.cwd() - : join(process.cwd(), "packages", "plugin"); + const projectRoot = process.cwd().includes("packages") + ? join(process.cwd(), "..", "..") + : process.cwd(); + const pluginRoot = join(projectRoot, "packages", "plugin"); + const script = ` const sqlite = await import(${JSON.stringify(`file://${pluginRoot}/src/shared/sqlite.ts`)}); const storageDb = await import(${JSON.stringify(`file://${pluginRoot}/src/features/magic-context/storage-db.ts`)}); @@ -128,13 +142,18 @@ describe("compartment state lease", () => { db.close(); console.log(JSON.stringify({ ok })); `; + const [a, b] = await Promise.all([ $`bun -e ${script} holder-a`.json() as Promise<{ ok: boolean }>, $`bun -e ${script} holder-b`.json() as Promise<{ ok: boolean }>, ]); expect([a.ok, b.ok].filter(Boolean)).toHaveLength(1); } finally { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } } }); }); diff --git a/packages/plugin/src/features/magic-context/compartment-storage-v6.test.ts b/packages/plugin/src/features/magic-context/compartment-storage-v6.test.ts index a8a1f1ba2..7d7748256 100644 --- a/packages/plugin/src/features/magic-context/compartment-storage-v6.test.ts +++ b/packages/plugin/src/features/magic-context/compartment-storage-v6.test.ts @@ -16,7 +16,13 @@ function useTempDataHome(prefix: string): string { afterEach(() => { closeDatabase(); - for (const dir of tempDirs) rmSync(dir, { recursive: true, force: true }); + for (const dir of tempDirs) { + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } + } tempDirs.length = 0; process.env.XDG_DATA_HOME = undefined; }); diff --git a/packages/plugin/src/features/magic-context/compression-depth-storage.test.ts b/packages/plugin/src/features/magic-context/compression-depth-storage.test.ts index fced84ccb..7eb5ee7f6 100644 --- a/packages/plugin/src/features/magic-context/compression-depth-storage.test.ts +++ b/packages/plugin/src/features/magic-context/compression-depth-storage.test.ts @@ -31,7 +31,11 @@ afterEach(() => { process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/features/magic-context/key-files/project-key-files.test.ts b/packages/plugin/src/features/magic-context/key-files/project-key-files.test.ts index 36a6dff91..a0977a800 100644 --- a/packages/plugin/src/features/magic-context/key-files/project-key-files.test.ts +++ b/packages/plugin/src/features/magic-context/key-files/project-key-files.test.ts @@ -30,7 +30,12 @@ const originalHome = process.env.HOME; afterEach(() => { setAftAvailabilityOverride(null); process.env.HOME = originalHome; - for (const dir of tempDirs) rmSync(dir, { recursive: true, force: true }); + for (const dir of tempDirs) + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } tempDirs.length = 0; }); diff --git a/packages/plugin/src/features/magic-context/memory/embedding.test.ts b/packages/plugin/src/features/magic-context/memory/embedding.test.ts index cc879ceba..8b051aea0 100644 --- a/packages/plugin/src/features/magic-context/memory/embedding.test.ts +++ b/packages/plugin/src/features/magic-context/memory/embedding.test.ts @@ -132,7 +132,11 @@ describe("embedding module", () => { _resetEmbeddingSweepGuard(); process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/features/magic-context/migrations-v11.test.ts b/packages/plugin/src/features/magic-context/migrations-v11.test.ts index 2bdd395cf..17d77523c 100644 --- a/packages/plugin/src/features/magic-context/migrations-v11.test.ts +++ b/packages/plugin/src/features/magic-context/migrations-v11.test.ts @@ -1,3 +1,5 @@ +/// + import { afterEach, describe, expect, test } from "bun:test"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; @@ -27,7 +29,13 @@ function useTempDataHome(prefix: string): string { afterEach(() => { closeDatabase(); - for (const dir of tempDirs) rmSync(dir, { recursive: true, force: true }); + for (const dir of tempDirs) { + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows where closeDatabase doesn't synchronously release locks + } + } tempDirs.length = 0; process.env.XDG_DATA_HOME = undefined; }); diff --git a/packages/plugin/src/features/magic-context/migrations-v12.test.ts b/packages/plugin/src/features/magic-context/migrations-v12.test.ts index 206945c75..c9af1c4ac 100644 --- a/packages/plugin/src/features/magic-context/migrations-v12.test.ts +++ b/packages/plugin/src/features/magic-context/migrations-v12.test.ts @@ -21,7 +21,13 @@ function count(db: Database, table: string): number { afterEach(() => { closeDatabase(); - for (const dir of tempDirs) rmSync(dir, { recursive: true, force: true }); + for (const dir of tempDirs) { + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } + } tempDirs.length = 0; process.env.XDG_DATA_HOME = undefined; }); diff --git a/packages/plugin/src/features/magic-context/migrations-v13.test.ts b/packages/plugin/src/features/magic-context/migrations-v13.test.ts index 38a971373..b25637fe0 100644 --- a/packages/plugin/src/features/magic-context/migrations-v13.test.ts +++ b/packages/plugin/src/features/magic-context/migrations-v13.test.ts @@ -22,7 +22,13 @@ function useTempDataHome(prefix: string): string { afterEach(() => { closeDatabase(); - for (const dir of tempDirs) rmSync(dir, { recursive: true, force: true }); + for (const dir of tempDirs) { + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } + } tempDirs.length = 0; process.env.XDG_DATA_HOME = undefined; }); diff --git a/packages/plugin/src/features/magic-context/migrations-v16.test.ts b/packages/plugin/src/features/magic-context/migrations-v16.test.ts index 24ffd68cf..e6e6a282c 100644 --- a/packages/plugin/src/features/magic-context/migrations-v16.test.ts +++ b/packages/plugin/src/features/magic-context/migrations-v16.test.ts @@ -14,7 +14,13 @@ function useTempDataHome(prefix: string): void { afterEach(() => { closeDatabase(); - for (const dir of tempDirs) rmSync(dir, { recursive: true, force: true }); + for (const dir of tempDirs) { + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } + } tempDirs.length = 0; process.env.XDG_DATA_HOME = undefined; }); diff --git a/packages/plugin/src/features/magic-context/migrations-v18-pi-marker.test.ts b/packages/plugin/src/features/magic-context/migrations-v18-pi-marker.test.ts index 87bafca24..633037965 100644 --- a/packages/plugin/src/features/magic-context/migrations-v18-pi-marker.test.ts +++ b/packages/plugin/src/features/magic-context/migrations-v18-pi-marker.test.ts @@ -21,7 +21,13 @@ function useTempDataHome(prefix: string): void { afterEach(() => { closeDatabase(); - for (const dir of tempDirs) rmSync(dir, { recursive: true, force: true }); + for (const dir of tempDirs) { + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } + } tempDirs.length = 0; process.env.XDG_DATA_HOME = undefined; }); diff --git a/packages/plugin/src/features/magic-context/migrations-v20.test.ts b/packages/plugin/src/features/magic-context/migrations-v20.test.ts index da895f5f0..6790a5518 100644 --- a/packages/plugin/src/features/magic-context/migrations-v20.test.ts +++ b/packages/plugin/src/features/magic-context/migrations-v20.test.ts @@ -21,7 +21,11 @@ describe("migration v20", () => { } finally { closeDatabase(); process.env.XDG_DATA_HOME = undefined; - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } } }); }); diff --git a/packages/plugin/src/features/magic-context/migrations-v21.test.ts b/packages/plugin/src/features/magic-context/migrations-v21.test.ts index 0dd7718b2..057c9f829 100644 --- a/packages/plugin/src/features/magic-context/migrations-v21.test.ts +++ b/packages/plugin/src/features/magic-context/migrations-v21.test.ts @@ -20,7 +20,11 @@ describe("migration v21", () => { } finally { closeDatabase(); process.env.XDG_DATA_HOME = undefined; - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } } }); }); diff --git a/packages/plugin/src/features/magic-context/project-embedding-registry.test.ts b/packages/plugin/src/features/magic-context/project-embedding-registry.test.ts index a2c2ca2f7..1be92a492 100644 --- a/packages/plugin/src/features/magic-context/project-embedding-registry.test.ts +++ b/packages/plugin/src/features/magic-context/project-embedding-registry.test.ts @@ -64,7 +64,11 @@ describe("project embedding registry", () => { closeDatabase(); process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/features/magic-context/resolve-subagent-fallback.test.ts b/packages/plugin/src/features/magic-context/resolve-subagent-fallback.test.ts index 3ed2b3b38..ce0547d31 100644 --- a/packages/plugin/src/features/magic-context/resolve-subagent-fallback.test.ts +++ b/packages/plugin/src/features/magic-context/resolve-subagent-fallback.test.ts @@ -51,7 +51,11 @@ describe("resolveIsSubagentFromOpenCodeDb", () => { } else { process.env.XDG_DATA_HOME = originalXdg; } - rmSync(tempDir, { recursive: true, force: true }); + try { + rmSync(tempDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } }); it("returns true for a child session with a non-empty parent_id", () => { diff --git a/packages/plugin/src/features/magic-context/sticky-injection-cas-race.test.ts b/packages/plugin/src/features/magic-context/sticky-injection-cas-race.test.ts index 3474b26ed..a1da0a516 100644 --- a/packages/plugin/src/features/magic-context/sticky-injection-cas-race.test.ts +++ b/packages/plugin/src/features/magic-context/sticky-injection-cas-race.test.ts @@ -36,7 +36,11 @@ describe("sticky-injection CAS helpers", () => { { messageId: "m2", text: "text-2" }, ]); } finally { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } }); @@ -51,7 +55,11 @@ describe("sticky-injection CAS helpers", () => { expect(pruneNoteNudgeAnchors(a, "s1", new Set(["m2"]))).toBe(1); expect(getNoteNudgeAnchors(b, "s1")).toEqual([{ messageId: "m2", text: "text-2" }]); } finally { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } }); @@ -70,7 +78,11 @@ describe("sticky-injection CAS helpers", () => { }); expect(getNoteNudgeAnchors(db, "s1")).toEqual([{ messageId: "m1", text: "text-1" }]); } finally { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } }); @@ -93,7 +105,11 @@ describe("sticky-injection CAS helpers", () => { }), ).toEqual({ ok: true, kind: "already-present", decision: stored }); } finally { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } }); }); diff --git a/packages/plugin/src/features/magic-context/storage-db-migration.test.ts b/packages/plugin/src/features/magic-context/storage-db-migration.test.ts index 7c9461c98..7828437c9 100644 --- a/packages/plugin/src/features/magic-context/storage-db-migration.test.ts +++ b/packages/plugin/src/features/magic-context/storage-db-migration.test.ts @@ -38,7 +38,11 @@ describe("storage-db legacy migration", () => { delete process.env.XDG_DATA_HOME; } try { - rmSync(tmpRoot, { recursive: true, force: true }); + try { + rmSync(tmpRoot, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } catch { // Non-fatal — tests on locked Windows file handles can leave temp // dirs that the OS will clean up later. diff --git a/packages/plugin/src/features/magic-context/storage-db.test.ts b/packages/plugin/src/features/magic-context/storage-db.test.ts index 5b5ad6415..322aa0d20 100644 --- a/packages/plugin/src/features/magic-context/storage-db.test.ts +++ b/packages/plugin/src/features/magic-context/storage-db.test.ts @@ -34,7 +34,11 @@ afterEach(() => { process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/features/magic-context/storage.test.ts b/packages/plugin/src/features/magic-context/storage.test.ts index a89eba6ea..e071e2634 100644 --- a/packages/plugin/src/features/magic-context/storage.test.ts +++ b/packages/plugin/src/features/magic-context/storage.test.ts @@ -61,7 +61,11 @@ afterEach(() => { process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/features/magic-context/tagger-recovery.test.ts b/packages/plugin/src/features/magic-context/tagger-recovery.test.ts index 56cf83ce9..1408b1b20 100644 --- a/packages/plugin/src/features/magic-context/tagger-recovery.test.ts +++ b/packages/plugin/src/features/magic-context/tagger-recovery.test.ts @@ -414,7 +414,11 @@ describe("initFromDb signature cache", () => { dbA.close(); } } finally { - rmSync(tmpDir, { recursive: true, force: true }); + try { + rmSync(tmpDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } }); diff --git a/packages/plugin/src/features/magic-context/tool-owner-backfill.test.ts b/packages/plugin/src/features/magic-context/tool-owner-backfill.test.ts index c25ae85e7..6fef38a5c 100644 --- a/packages/plugin/src/features/magic-context/tool-owner-backfill.test.ts +++ b/packages/plugin/src/features/magic-context/tool-owner-backfill.test.ts @@ -27,7 +27,11 @@ const originalXdgDataHome = process.env.XDG_DATA_HOME; afterEach(() => { process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/auto-update-checker/index.test.ts b/packages/plugin/src/hooks/auto-update-checker/index.test.ts index 38169b4a7..dbe7b251f 100644 --- a/packages/plugin/src/hooks/auto-update-checker/index.test.ts +++ b/packages/plugin/src/hooks/auto-update-checker/index.test.ts @@ -61,7 +61,11 @@ afterEach(() => { const dir = tempDirs.pop(); if (!dir) continue; try { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } catch { // Best-effort cleanup } diff --git a/packages/plugin/src/hooks/magic-context/apply-operations.tool-drop.test.ts b/packages/plugin/src/hooks/magic-context/apply-operations.tool-drop.test.ts index 36d6a72b3..c7cb342ef 100644 --- a/packages/plugin/src/hooks/magic-context/apply-operations.tool-drop.test.ts +++ b/packages/plugin/src/hooks/magic-context/apply-operations.tool-drop.test.ts @@ -29,7 +29,11 @@ afterEach(() => { closeDatabase(); process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/compaction-marker-consistency.test.ts b/packages/plugin/src/hooks/magic-context/compaction-marker-consistency.test.ts index 2f167876b..53749e92b 100644 --- a/packages/plugin/src/hooks/magic-context/compaction-marker-consistency.test.ts +++ b/packages/plugin/src/hooks/magic-context/compaction-marker-consistency.test.ts @@ -60,7 +60,11 @@ afterEach(() => { closeDatabase(); process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/compaction-marker-manager.test.ts b/packages/plugin/src/hooks/magic-context/compaction-marker-manager.test.ts index 7868a4947..0e0eeb806 100644 --- a/packages/plugin/src/hooks/magic-context/compaction-marker-manager.test.ts +++ b/packages/plugin/src/hooks/magic-context/compaction-marker-manager.test.ts @@ -98,7 +98,11 @@ afterEach(() => { closeDatabase(); process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } } tempDirs.length = 0; }); @@ -255,7 +259,7 @@ describe("applyDeferredCompactionMarker — outcomes", () => { // No user message exists at or before the boundary → inject returns // null → retryable-failure. expect(outcome.kind).toBe("retryable-failure"); - // Persisted state remains absent (we never wrote a marker) + // Persisted state remains absent ( we never wrote a marker) const persisted = getPersistedCompactionMarkerState(db, "ses-1"); expect(persisted).toBeNull(); }); diff --git a/packages/plugin/src/hooks/magic-context/compartment-runner-drop-queue.test.ts b/packages/plugin/src/hooks/magic-context/compartment-runner-drop-queue.test.ts index 33a94c1ce..82ad17f63 100644 --- a/packages/plugin/src/hooks/magic-context/compartment-runner-drop-queue.test.ts +++ b/packages/plugin/src/hooks/magic-context/compartment-runner-drop-queue.test.ts @@ -37,7 +37,11 @@ afterEach(() => { closeDatabase(); process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/compartment-runner-timeout.test.ts b/packages/plugin/src/hooks/magic-context/compartment-runner-timeout.test.ts index 5ed149b2f..49f4649fc 100644 --- a/packages/plugin/src/hooks/magic-context/compartment-runner-timeout.test.ts +++ b/packages/plugin/src/hooks/magic-context/compartment-runner-timeout.test.ts @@ -19,7 +19,11 @@ afterEach(() => { process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/compartment-runner.test.ts b/packages/plugin/src/hooks/magic-context/compartment-runner.test.ts index 268adb544..a2d9513b0 100644 --- a/packages/plugin/src/hooks/magic-context/compartment-runner.test.ts +++ b/packages/plugin/src/hooks/magic-context/compartment-runner.test.ts @@ -55,13 +55,21 @@ afterEach(() => { process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } } tempDirs.length = 0; // Clean up historian debug dumps created during tests const dumpDir = join(tmpdir(), "magic-context-historian"); - rmSync(dumpDir, { recursive: true, force: true }); + try { + rmSync(dumpDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } }); describe("executeContextRecomp", () => { diff --git a/packages/plugin/src/hooks/magic-context/compartment-trigger.test.ts b/packages/plugin/src/hooks/magic-context/compartment-trigger.test.ts index 1ce0a1891..adbd757c0 100644 --- a/packages/plugin/src/hooks/magic-context/compartment-trigger.test.ts +++ b/packages/plugin/src/hooks/magic-context/compartment-trigger.test.ts @@ -22,7 +22,11 @@ afterEach(() => { closeDatabase(); process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/event-handler.test.ts b/packages/plugin/src/hooks/magic-context/event-handler.test.ts index 4b848d1d5..aea509ad7 100644 --- a/packages/plugin/src/hooks/magic-context/event-handler.test.ts +++ b/packages/plugin/src/hooks/magic-context/event-handler.test.ts @@ -53,7 +53,11 @@ afterEach(() => { process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/historian-state-file.test.ts b/packages/plugin/src/hooks/magic-context/historian-state-file.test.ts index 425a9c67c..297e25280 100644 --- a/packages/plugin/src/hooks/magic-context/historian-state-file.test.ts +++ b/packages/plugin/src/hooks/magic-context/historian-state-file.test.ts @@ -15,7 +15,11 @@ beforeEach(() => { }); afterEach(() => { - rmSync(tempProjectDir, { recursive: true, force: true }); + try { + rmSync(tempProjectDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } }); describe("maybeWriteHistorianStateFile", () => { diff --git a/packages/plugin/src/hooks/magic-context/hook.test.ts b/packages/plugin/src/hooks/magic-context/hook.test.ts index 15ea16d31..79d9ea673 100644 --- a/packages/plugin/src/hooks/magic-context/hook.test.ts +++ b/packages/plugin/src/hooks/magic-context/hook.test.ts @@ -46,7 +46,11 @@ afterEach(() => { process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/nudger.test.ts b/packages/plugin/src/hooks/magic-context/nudger.test.ts index 22354c0b6..f27de4683 100644 --- a/packages/plugin/src/hooks/magic-context/nudger.test.ts +++ b/packages/plugin/src/hooks/magic-context/nudger.test.ts @@ -23,7 +23,11 @@ afterEach(() => { process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/read-session-chunk.test.ts b/packages/plugin/src/hooks/magic-context/read-session-chunk.test.ts index 5e32af577..99a5e4746 100644 --- a/packages/plugin/src/hooks/magic-context/read-session-chunk.test.ts +++ b/packages/plugin/src/hooks/magic-context/read-session-chunk.test.ts @@ -20,7 +20,11 @@ const originalXdgDataHome = process.env.XDG_DATA_HOME; afterEach(() => { process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/read-session-db.test.ts b/packages/plugin/src/hooks/magic-context/read-session-db.test.ts index 081563c84..dcff47bb0 100644 --- a/packages/plugin/src/hooks/magic-context/read-session-db.test.ts +++ b/packages/plugin/src/hooks/magic-context/read-session-db.test.ts @@ -21,7 +21,11 @@ afterEach(() => { closeReadOnlySessionDb(); process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/system-prompt-hash.test.ts b/packages/plugin/src/hooks/magic-context/system-prompt-hash.test.ts index 627f09445..e677cf7db 100644 --- a/packages/plugin/src/hooks/magic-context/system-prompt-hash.test.ts +++ b/packages/plugin/src/hooks/magic-context/system-prompt-hash.test.ts @@ -41,7 +41,11 @@ afterEach(() => { closeDatabase(); process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/tag-messages-collision.test.ts b/packages/plugin/src/hooks/magic-context/tag-messages-collision.test.ts index 99d8a9fa4..b93413013 100644 --- a/packages/plugin/src/hooks/magic-context/tag-messages-collision.test.ts +++ b/packages/plugin/src/hooks/magic-context/tag-messages-collision.test.ts @@ -30,7 +30,11 @@ afterEach(() => { closeDatabase(); process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/tool-input-preservation.test.ts b/packages/plugin/src/hooks/magic-context/tool-input-preservation.test.ts index cc8c83f88..7ff80bb1a 100644 --- a/packages/plugin/src/hooks/magic-context/tool-input-preservation.test.ts +++ b/packages/plugin/src/hooks/magic-context/tool-input-preservation.test.ts @@ -15,7 +15,11 @@ afterEach(() => { closeDatabase(); process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/transform-cache-busting-signals.test.ts b/packages/plugin/src/hooks/magic-context/transform-cache-busting-signals.test.ts index 7d5d8663f..b250806d1 100644 --- a/packages/plugin/src/hooks/magic-context/transform-cache-busting-signals.test.ts +++ b/packages/plugin/src/hooks/magic-context/transform-cache-busting-signals.test.ts @@ -79,7 +79,11 @@ afterEach(() => { process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/transform-compartment-phase.test.ts b/packages/plugin/src/hooks/magic-context/transform-compartment-phase.test.ts index 4a95ce81c..5e809d299 100644 --- a/packages/plugin/src/hooks/magic-context/transform-compartment-phase.test.ts +++ b/packages/plugin/src/hooks/magic-context/transform-compartment-phase.test.ts @@ -84,7 +84,12 @@ beforeEach(() => { afterEach(() => { closeDatabase(); process.env.XDG_DATA_HOME = originalXdgDataHome; - if (tempDir) rmSync(tempDir, { recursive: true, force: true }); + if (tempDir) + try { + rmSync(tempDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } }); describe("runCompartmentPhase - 95% emergency notification idempotency", () => { diff --git a/packages/plugin/src/hooks/magic-context/transform-context-state.test.ts b/packages/plugin/src/hooks/magic-context/transform-context-state.test.ts index 82d041bde..a9d09c50b 100644 --- a/packages/plugin/src/hooks/magic-context/transform-context-state.test.ts +++ b/packages/plugin/src/hooks/magic-context/transform-context-state.test.ts @@ -20,7 +20,11 @@ afterEach(() => { process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/transform-heuristic-cleanup-persistence.test.ts b/packages/plugin/src/hooks/magic-context/transform-heuristic-cleanup-persistence.test.ts index 99cb6ead3..bad9279ad 100644 --- a/packages/plugin/src/hooks/magic-context/transform-heuristic-cleanup-persistence.test.ts +++ b/packages/plugin/src/hooks/magic-context/transform-heuristic-cleanup-persistence.test.ts @@ -118,7 +118,11 @@ afterEach(() => { process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/transform-index-staleness.test.ts b/packages/plugin/src/hooks/magic-context/transform-index-staleness.test.ts index 05166d04b..6b2a01202 100644 --- a/packages/plugin/src/hooks/magic-context/transform-index-staleness.test.ts +++ b/packages/plugin/src/hooks/magic-context/transform-index-staleness.test.ts @@ -38,7 +38,11 @@ afterEach(() => { closeDatabase(); process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/transform-nudge-cache.test.ts b/packages/plugin/src/hooks/magic-context/transform-nudge-cache.test.ts index 9f12f341d..bded1b505 100644 --- a/packages/plugin/src/hooks/magic-context/transform-nudge-cache.test.ts +++ b/packages/plugin/src/hooks/magic-context/transform-nudge-cache.test.ts @@ -35,7 +35,11 @@ afterEach(() => { closeDatabase(); process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/transform-operations.test.ts b/packages/plugin/src/hooks/magic-context/transform-operations.test.ts index aaf33f45c..d047359a4 100644 --- a/packages/plugin/src/hooks/magic-context/transform-operations.test.ts +++ b/packages/plugin/src/hooks/magic-context/transform-operations.test.ts @@ -33,7 +33,11 @@ afterEach(() => { closeDatabase(); process.env.XDG_DATA_HOME = originalXdgDataHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + // Ignore EBUSY on Windows + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/hooks/magic-context/transform-todo-state.test.ts b/packages/plugin/src/hooks/magic-context/transform-todo-state.test.ts index 3de86f07f..59de33d46 100644 --- a/packages/plugin/src/hooks/magic-context/transform-todo-state.test.ts +++ b/packages/plugin/src/hooks/magic-context/transform-todo-state.test.ts @@ -43,7 +43,12 @@ function useTempDataHome(prefix: string): void { afterEach(() => { closeDatabase(); - for (const dir of tempDirs) rmSync(dir, { recursive: true, force: true }); + for (const dir of tempDirs) + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } tempDirs.length = 0; process.env.XDG_DATA_HOME = undefined; }); diff --git a/packages/plugin/src/hooks/magic-context/transform.test.ts b/packages/plugin/src/hooks/magic-context/transform.test.ts index ac8819585..78471f754 100644 --- a/packages/plugin/src/hooks/magic-context/transform.test.ts +++ b/packages/plugin/src/hooks/magic-context/transform.test.ts @@ -77,7 +77,11 @@ afterEach(() => { else process.env.XDG_CACHE_HOME = originalXdgCacheHome; for (const dir of tempDirs) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } tempDirs.length = 0; }); diff --git a/packages/plugin/src/shared/announcement.test.ts b/packages/plugin/src/shared/announcement.test.ts index a76a97eb5..032b4aa75 100644 --- a/packages/plugin/src/shared/announcement.test.ts +++ b/packages/plugin/src/shared/announcement.test.ts @@ -35,7 +35,8 @@ afterEach(() => { process.env.XDG_DATA_HOME = originalXdg; } try { - fs.rmSync(tmpRoot, { recursive: true, force: true }); + // maxRetries/retryDelay ride out transient EBUSY/EPERM on Windows. + fs.rmSync(tmpRoot, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); } catch { // best-effort } diff --git a/packages/plugin/src/shared/conflict-detector.test.ts b/packages/plugin/src/shared/conflict-detector.test.ts index 1a297c0e7..67b51a110 100644 --- a/packages/plugin/src/shared/conflict-detector.test.ts +++ b/packages/plugin/src/shared/conflict-detector.test.ts @@ -46,8 +46,21 @@ describe("detectConflicts", () => { else process.env[k] = v; } // Test directories live under tmpdir(); cleanup is best-effort. - rmSync(projectDir, { recursive: true, force: true }); - rmSync(userConfigDir, { recursive: true, force: true }); + try { + rmSync(projectDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } + try { + rmSync(userConfigDir, { + recursive: true, + force: true, + maxRetries: 10, + retryDelay: 100, + }); + } catch { + /* Ignore EBUSY on Windows */ + } }); function writeProjectConfig(plugins: Array): void { diff --git a/packages/plugin/src/shared/conflict-fixer.test.ts b/packages/plugin/src/shared/conflict-fixer.test.ts index 744a80a41..0a3d56ddd 100644 --- a/packages/plugin/src/shared/conflict-fixer.test.ts +++ b/packages/plugin/src/shared/conflict-fixer.test.ts @@ -38,7 +38,11 @@ describe("fixConflicts", () => { if (value === undefined) delete process.env[key]; else process.env[key] = value; } - rmSync(root, { recursive: true, force: true }); + try { + rmSync(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } }); it("preserves JSONC comments and tuple plugin entries while removing canonical DCP", () => { diff --git a/packages/plugin/src/shared/models-dev-cache.test.ts b/packages/plugin/src/shared/models-dev-cache.test.ts index 18b58af1f..09b6f5154 100644 --- a/packages/plugin/src/shared/models-dev-cache.test.ts +++ b/packages/plugin/src/shared/models-dev-cache.test.ts @@ -39,7 +39,11 @@ describe("models-dev-cache", () => { if (v === undefined) delete process.env[k]; else process.env[k] = v; } - rmSync(tempDir, { recursive: true, force: true }); + try { + rmSync(tempDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } clearModelsDevCache(); }); diff --git a/packages/plugin/src/shared/opencode-compaction-detector.test.ts b/packages/plugin/src/shared/opencode-compaction-detector.test.ts index 5f64da76f..748e39ae7 100644 --- a/packages/plugin/src/shared/opencode-compaction-detector.test.ts +++ b/packages/plugin/src/shared/opencode-compaction-detector.test.ts @@ -18,7 +18,11 @@ describe("opencode-compaction-detector", () => { }); afterEach(() => { - rmSync(tmpDir, { recursive: true, force: true }); + try { + rmSync(tmpDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } delete process.env.OPENCODE_DISABLE_AUTOCOMPACT; }); @@ -30,7 +34,11 @@ describe("opencode-compaction-detector", () => { const result = isOpenCodeAutoCompactionEnabled(emptyDir); expect(result).toBe(true); - rmSync(emptyDir, { recursive: true, force: true }); + try { + rmSync(emptyDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } }); }); diff --git a/packages/plugin/src/shared/rpc-client.test.ts b/packages/plugin/src/shared/rpc-client.test.ts index ed123b075..155e014b5 100644 --- a/packages/plugin/src/shared/rpc-client.test.ts +++ b/packages/plugin/src/shared/rpc-client.test.ts @@ -20,7 +20,11 @@ afterEach(async () => { await server.close(); } for (const dir of tempDirs.splice(0)) { - rmSync(dir, { recursive: true, force: true }); + try { + rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); + } catch { + /* Ignore EBUSY on Windows */ + } } });