From 15f90a44b3a4ca9f1f9b410c0b405ff5e0ed7a9d Mon Sep 17 00:00:00 2001 From: Evgeny Marchenkov Date: Thu, 10 Sep 2026 02:15:21 +0200 Subject: [PATCH 1/2] test(daytona): replace tick-counting barriers with explicit entry signals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The racy teardown test relied on a bare setTimeout(0) to assume both the inbound upload and outbound download mocks had been entered. The outbound path takes materially longer to reach its mock (a sandbox round trip plus a per-target mkdir before downloadFiles is ever called), so the fixed tick budget could elapse before releaseDownload was assigned, throwing 'releaseDownload is not a function'. Replace the blind tick with explicit deferred signals that resolve as the first statement inside each mock, and await both before relying on the refCount-of-two precondition. Also convert the same setTimeout(0)-as-barrier pattern in four other tests where a mock-assigned release handle is used before confirming the mock actually ran, using vi.waitFor to poll for the real call instead of guessing a tick count (matching the existing precedent already used elsewhere in this file). No change to plugin.ts or file-sync.ts — this is a test-only fix. Fixes HEA-60. Co-Authored-By: Claude Sonnet 5 --- .../daytona/src/plugin.test.ts | 48 +++++++++++++++---- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/packages/plugins/sandbox-providers/daytona/src/plugin.test.ts b/packages/plugins/sandbox-providers/daytona/src/plugin.test.ts index 84787dff01..1efd765a66 100644 --- a/packages/plugins/sandbox-providers/daytona/src/plugin.test.ts +++ b/packages/plugins/sandbox-providers/daytona/src/plugin.test.ts @@ -2618,7 +2618,11 @@ describe("Daytona sandbox provider plugin", () => { mockGet.mockResolvedValue(sandbox); const executePromise = plugin.definition.onEnvironmentExecute?.(execParams("lease-a")); - await new Promise((resolve) => setTimeout(resolve, 0)); + // Poll for the actual executeCommand call instead of guessing a tick + // count, so resolveExecute is guaranteed to be assigned before it's used. + await vi.waitFor(() => { + expect(sandbox.process.executeCommand).toHaveBeenCalled(); + }); const releasePromise = plugin.definition.onEnvironmentReleaseLease?.({ driverKey: "daytona", @@ -2757,7 +2761,11 @@ describe("Daytona sandbox provider plugin", () => { mockGet.mockResolvedValue(sandbox); const executePromise = plugin.definition.onEnvironmentExecute?.(execParams("lease-a")); - await new Promise((resolve) => setTimeout(resolve, 0)); + // Poll for the actual executeCommand call instead of guessing a tick + // count, so resolveExecute is guaranteed to be assigned before it's used. + await vi.waitFor(() => { + expect(sandbox.process.executeCommand).toHaveBeenCalled(); + }); const cancelPromise = plugin.definition.onEnvironmentCancelInteractiveSetup?.({ driverKey: "daytona", @@ -2857,7 +2865,11 @@ describe("Daytona sandbox provider plugin", () => { mockGet.mockResolvedValue(sandbox); const firstExecutePromise = plugin.definition.onEnvironmentExecute?.(execParams("lease-a")); - await new Promise((resolve) => setTimeout(resolve, 0)); + // Poll for the actual executeCommand call instead of guessing a tick + // count, so resolveFirstExecute is guaranteed to be assigned before it's used. + await vi.waitFor(() => { + expect(sandbox.process.executeCommand).toHaveBeenCalled(); + }); const cancelPromise = plugin.definition.onEnvironmentCancelInteractiveSetup?.({ driverKey: "daytona", @@ -2910,7 +2922,11 @@ describe("Daytona sandbox provider plugin", () => { config: { timeoutMs: 300000, reuseLease: false }, templateLabel: "snapshot-check", }); - await new Promise((resolve) => setTimeout(resolve, 0)); + // Poll for the actual snapshot call instead of guessing a tick count, so + // resolveSnapshot is guaranteed to be assigned before it's used. + await vi.waitFor(() => { + expect(sandbox._experimental_createSnapshot).toHaveBeenCalled(); + }); const destroyPromise = plugin.definition.onEnvironmentDestroyLease?.({ driverKey: "daytona", @@ -4828,15 +4844,31 @@ describe("daytona native file-sync hooks", () => { const sandbox = createMockSandbox({ id: "sandbox-123" }); // Hold the inbound upload and the outbound download open at the same time, so - // the shared lease has two active sync calls when teardown starts. + // the shared lease has two active sync calls when teardown starts. Each mock + // resolves a deferred as its first statement, so the test can wait for both + // transfers to actually be parked instead of guessing a tick count — the + // outbound path takes materially longer to reach its mock (a sandbox round + // trip plus a per-target mkdir before the download call ever fires), so a + // fixed setTimeout(0) barrier can assert before the outbound transfer parks + // and leave releaseDownload unassigned. let releaseUpload!: () => void; + let uploadEnteredResolve!: () => void; + const uploadEntered = new Promise((resolve) => { + uploadEnteredResolve = resolve; + }); sandbox.fs.uploadFiles.mockImplementation(async () => { + uploadEnteredResolve(); await new Promise((resolve) => { releaseUpload = resolve; }); }); let releaseDownload!: () => void; + let downloadEnteredResolve!: () => void; + const downloadEntered = new Promise((resolve) => { + downloadEnteredResolve = resolve; + }); sandbox.fs.downloadFiles.mockImplementation(async (requests: Array<{ source: string; destination: string }>) => { + downloadEnteredResolve(); await new Promise((resolve) => { releaseDownload = resolve; }); @@ -4855,9 +4887,9 @@ describe("daytona native file-sync hooks", () => { const outboundCall = plugin.definition.onEnvironmentSyncOut?.( syncOutParams({ operationId: "out-active", sourcePath: `${REMOTE_DIR}/out.txt`, targetPath: outboundTarget }), ); - // Let both sync calls register on the activity gate and reach their hung - // transfer, so teardown sees a refCount of two. - await new Promise((resolve) => setTimeout(resolve, 0)); + // Wait for both transfers to actually reach their hung mock before relying + // on refCount being two, instead of guessing a tick count. + await Promise.all([uploadEntered, downloadEntered]); const destroyCall = plugin.definition.onEnvironmentDestroyLease?.({ driverKey: "daytona",