test(daytona): replace tick-counting barriers with explicit entry signals

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 <noreply@anthropic.com>
This commit is contained in:
Evgeny Marchenkov 2026-09-10 02:15:21 +02:00
parent bd1fdc2887
commit 15f90a44b3
1 changed files with 40 additions and 8 deletions

View File

@ -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<void>((resolve) => {
uploadEnteredResolve = resolve;
});
sandbox.fs.uploadFiles.mockImplementation(async () => {
uploadEnteredResolve();
await new Promise<void>((resolve) => {
releaseUpload = resolve;
});
});
let releaseDownload!: () => void;
let downloadEnteredResolve!: () => void;
const downloadEntered = new Promise<void>((resolve) => {
downloadEnteredResolve = resolve;
});
sandbox.fs.downloadFiles.mockImplementation(async (requests: Array<{ source: string; destination: string }>) => {
downloadEnteredResolve();
await new Promise<void>((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",