From 09c1265bd04b9c64ad91a256f33a442b9e19b85c Mon Sep 17 00:00:00 2001 From: elkaix Date: Wed, 26 Aug 2026 21:49:11 -0400 Subject: [PATCH] test(agent-core-v2): dispose task fixtures before deleting their session dir Five task-manager cases created a temp session dir and an agent context, then deleted the dir in their finally block without disposing the context. The context kept writing session state into that dir while the delete walked it, which failed the run with ENOTEMPTY on a loaded CI machine. Dispose every context first, and let fs.rm retry the delete, which is what its maxRetries option exists for. --- .../test/agent/task/taskManager.test.ts | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/packages/agent-core-v2/test/agent/task/taskManager.test.ts b/packages/agent-core-v2/test/agent/task/taskManager.test.ts index ca6be88e1..c56448b35 100644 --- a/packages/agent-core-v2/test/agent/task/taskManager.test.ts +++ b/packages/agent-core-v2/test/agent/task/taskManager.test.ts @@ -66,6 +66,14 @@ function createAgentTaskService(options: { }; } +async function cleanupSessionDir( + sessionDir: string, + ...contexts: readonly (TestAgentContext | undefined)[] +): Promise { + for (const ctx of contexts) await ctx?.dispose(); + await rm(sessionDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 50 }); +} + function registerProcess( manager: IAgentTaskService, proc: IHostProcess, @@ -681,8 +689,8 @@ describe('AgentTaskService', () => { it('stops appending persisted foreground output once the output limit trips', async () => { const sessionDir = await mkdtemp(join(tmpdir(), 'pythinker-bg-limit-fg-')); + const { ctx, manager } = createAgentTaskService({ sessionDir }); try { - const { manager } = createAgentTaskService({ sessionDir }); const chunks = Array.from({ length: 20 }, () => 'x'.repeat(MiB)); const { proc } = sigtermIgnoringProcess(chunks); @@ -701,14 +709,14 @@ describe('AgentTaskService', () => { expect(info).toMatchObject({ status: 'killed' }); expect(output.outputSizeBytes).toBeLessThanOrEqual(LIMIT_BYTES); } finally { - await rm(sessionDir, { recursive: true, force: true }); + await cleanupSessionDir(sessionDir, ctx); } }); it('stops appending persisted output once the output limit trips for a detached process task', async () => { const sessionDir = await mkdtemp(join(tmpdir(), 'pythinker-bg-limit-bg-')); + const { ctx, manager } = createAgentTaskService({ sessionDir }); try { - const { manager } = createAgentTaskService({ sessionDir }); const chunks = Array.from({ length: 20 }, () => 'x'.repeat(MiB)); const { proc } = sigtermIgnoringProcess(chunks); @@ -727,14 +735,14 @@ describe('AgentTaskService', () => { expect(info?.stopReason ?? '').toMatch(/output limit/i); expect(output.outputSizeBytes).toBeLessThanOrEqual(LIMIT_BYTES); } finally { - await rm(sessionDir, { recursive: true, force: true }); + await cleanupSessionDir(sessionDir, ctx); } }); it('does not cap a detached subagent result larger than the process output limit', async () => { const sessionDir = await mkdtemp(join(tmpdir(), 'pythinker-bg-limit-agent-')); + const { ctx, manager } = createAgentTaskService({ sessionDir }); try { - const { manager } = createAgentTaskService({ sessionDir }); const result = 'y'.repeat(20 * MiB); const taskId = manager.registerTask( agentTask(Promise.resolve({ result }), 'big subagent result'), @@ -747,7 +755,7 @@ describe('AgentTaskService', () => { expect(info).toMatchObject({ status: 'completed' }); expect(output.outputSizeBytes).toBe(Buffer.byteLength(result)); } finally { - await rm(sessionDir, { recursive: true, force: true }); + await cleanupSessionDir(sessionDir, ctx); } }); @@ -1079,8 +1087,10 @@ describe('AgentTaskService', () => { it('persists graceful process shutdown as killed when stop was requested', async () => { const sessionDir = await mkdtemp(join(tmpdir(), 'pythinker-bg-stop-race-')); + const writerFixture = createAgentTaskService({ sessionDir }); + const writer = writerFixture.manager; + let readerFixture: TaskServiceFixture | undefined; try { - const writer = createAgentTaskService({ sessionDir }).manager; const { proc, resolve } = manuallyResolvedProcess(); const taskId = registerProcess(writer, proc, 'sleep 60', 'persisted race'); @@ -1088,7 +1098,8 @@ describe('AgentTaskService', () => { resolve(0); await stopPromise; - const reader = createAgentTaskService({ sessionDir }).manager; + readerFixture = createAgentTaskService({ sessionDir }); + const reader = readerFixture.manager; await reader.loadFromDisk(); expect(reader.getTask(taskId)).toMatchObject({ @@ -1098,7 +1109,7 @@ describe('AgentTaskService', () => { stopReason: 'user requested', }); } finally { - await rm(sessionDir, { recursive: true, force: true }); + await cleanupSessionDir(sessionDir, writerFixture.ctx, readerFixture?.ctx); } }); @@ -1285,15 +1296,15 @@ describe('AgentTaskService', () => { it('getTask on an unknown id does not create persisted state', async () => { const sessionDir = await mkdtemp(join(tmpdir(), 'pythinker-bg-mgr-missing-')); + const { ctx, manager, persistence } = createAgentTaskService({ sessionDir }); try { - const { ctx, manager, persistence } = createAgentTaskService({ sessionDir }); expect(manager.getTask('bash-bogusss0')).toBeUndefined(); expect(await persistence!.listTasks()).toEqual([]); await ctx.get(ISessionMetadata).ready; } finally { - await rm(sessionDir, { recursive: true, force: true }); + await cleanupSessionDir(sessionDir, ctx); } });