Bläddra i källkod

test(subagent): close persistence handle before deleting temp root

The continuation spec cleaned each mkdtemp root with rmSync in afterEach
without closing the JsonlSessionPersistence handle first. On Windows,
rmSync recursive over a directory whose file handle is still open fails
with EPERM, surfacing intermittently in native complete as
dsh-subagent-continuation-* cleanup failures. Close the persistence
fiber before rmSync (mirrors jsonl.spec.ts), preserving per-root
dispose-then-delete order.
Chinesezjc 1 månad sedan
förälder
incheckning
ea0906a99d
1 ändrade filer med 21 tillägg och 6 borttagningar
  1. 21 6
      packages/subagent/subagent/tests/continuation.spec.ts

+ 21 - 6
packages/subagent/subagent/tests/continuation.spec.ts

@@ -51,9 +51,14 @@ class GatedAdapter extends LlmAdapter {
   }
 }
 
-const roots: string[] = []
-afterEach(() => {
-  for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 })
+// Each persistence-backed temp root has a cleanup that closes its handle before
+// removing the directory. On Windows, rmSync over a directory containing a file
+// whose handle is still open fails with EPERM (the intermittent continuation /
+// skill-filesystem native flakes); jsonl.spec.ts closes the persistence fiber
+// first. Keeping each root's dispose+delete in one closure preserves that order.
+const cleanups: Array<() => Promise<void>> = []
+afterEach(async () => {
+  for (const cleanup of cleanups.splice(0)) await cleanup()
 })
 
 /** Boot the full continuable stack: loop, persistence, providers, and subagents. */
@@ -64,9 +69,15 @@ async function setupWith(adapter: LlmAdapter, options: { persistence?: boolean }
   let root: string | undefined
   if (options.persistence !== false) {
     root = mkdtempSync(join(tmpdir(), 'dsh-subagent-continuation-'))
-    roots.push(root)
+    const persistedRoot = root
     const persistenceFiber = await ctx.plugin(JsonlSessionPersistence, { root })
     disposePersistence = () => persistenceFiber.dispose()
+    // Close the handle first, then delete: on Windows rmSync over a dir holding
+    // a still-open handle EPERMs. Mirrors jsonl.spec.ts.
+    cleanups.push(async () => {
+      await persistenceFiber.dispose()
+      rmSync(persistedRoot, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 })
+    })
   }
   await ctx.plugin(AgentLoop, { agents: [] })
   await ctx.plugin(SubagentRuntime)
@@ -2432,8 +2443,12 @@ describe('continuable errors', () => {
     const ctx = new Context()
     await mountAgentLoopTestDependencies(ctx)
     const root = mkdtempSync(join(tmpdir(), 'dsh-subagent-continuation-'))
-    roots.push(root)
-    await ctx.plugin(JsonlSessionPersistence, { root })
+    const persistenceFiber = await ctx.plugin(JsonlSessionPersistence, { root })
+    // Close the handle before deleting (Windows EPERM on held handles).
+    cleanups.push(async () => {
+      await persistenceFiber.dispose()
+      rmSync(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 })
+    })
     await ctx.plugin(AgentLoop, { agents: [] })
     const serviceFiber = await ctx.plugin(SubagentRuntime)
     await ctx.plugin(SubagentSpawn, { providerName: 'spawn' })