From e5aa7f336daa2a7c4388025e6a58e4939a76bf2c Mon Sep 17 00:00:00 2001 From: Ritikag33 Date: Thu, 1 Oct 2026 12:32:25 +0530 Subject: [PATCH] test(core): cover VM disposal after guest fetch --- packages/core/package.json | 2 +- .../vm-dispose-network-regression.test.ts | 58 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 packages/core/tests/vm-dispose-network-regression.test.ts diff --git a/packages/core/package.json b/packages/core/package.json index 06d86ccf81..53af310904 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -55,7 +55,7 @@ "build": "tsc && node scripts/stage-default-software.mjs", "test": "vitest run --exclude '**/*.nightly.test.ts' --reporter=verbose", "test:unit": "vitest run tests/agentos-package.test.ts tests/allowed-node-builtins.test.ts tests/host-functions-zod.test.ts tests/host-functions.test.ts tests/host-function-command-exec.test.ts tests/host-function-permissions.test.ts tests/cron-manager.test.ts tests/cron-timer-driver.test.ts tests/exec-timeout.test.ts tests/execution-abort.test.ts tests/generated-protocol.test.ts tests/leak-agent-os-processes.test.ts tests/leak-rpc-client.test.ts tests/mount-descriptors.test.ts tests/mount-reconfigure.test.ts tests/options-schema.test.ts tests/output-replay.test.ts tests/public-api-exports.test.ts tests/root-filesystem-descriptors.test.ts tests/runtime-compat-mount.test.ts tests/sidecar-client.test.ts tests/sidecar-permission-descriptors.test.ts tests/software-install.test.ts tests/wasm-permission-tiers.test.ts --fileParallelism=false", - "test:pr": "pnpm test:unit", + "test:pr": "pnpm test:unit && vitest run tests/vm-dispose-network-regression.test.ts --fileParallelism=false --reporter=verbose", "test:nightly": "vitest run tests/*.nightly.test.ts --reporter=verbose --passWithNoTests" }, "dependencies": { diff --git a/packages/core/tests/vm-dispose-network-regression.test.ts b/packages/core/tests/vm-dispose-network-regression.test.ts new file mode 100644 index 0000000000..a69c09a192 --- /dev/null +++ b/packages/core/tests/vm-dispose-network-regression.test.ts @@ -0,0 +1,58 @@ +import { createServer, type Server } from "node:http"; +import { afterEach, expect, test } from "vitest"; +import { AgentOs } from "../src/index.js"; + +let server: Server | null = null; +let vm: AgentOs | null = null; + +// Keep this regression isolated in its own Vitest worker so the sidecar's +// process-global JavaScript timer wheel is first initialized by this fetch. +afterEach(async () => { + await vm?.dispose(); + vm = null; + if (server?.listening) { + await new Promise((resolve, reject) => { + server?.close((error) => (error ? reject(error) : resolve())); + }); + } + server = null; +}); + +test("disposes promptly after guest fetch consumes the response body", async () => { + server = createServer((_request, response) => { + response.writeHead(200, { "content-type": "text/plain" }); + response.end("ok"); + }); + await new Promise((resolve) => { + server?.listen(0, "127.0.0.1", resolve); + }); + const address = server.address(); + if (!address || typeof address === "string") { + throw new Error("local HTTP fixture did not expose a TCP port"); + } + + vm = await AgentOs.create({ + defaultSoftware: false, + loopbackExemptPorts: [address.port], + permissions: { + fs: "allow", + network: "allow", + childProcess: "allow", + }, + }); + + const result = await vm.javascript.evaluate( + `(async () => { + const response = await fetch("http://127.0.0.1:${address.port}/dispose"); + return await response.text(); + })()`, + ); + expect(result).toMatchObject({ outcome: "succeeded", value: "ok" }); + + const disposeStartedAt = performance.now(); + await vm.dispose(); + vm = null; + const disposeDurationMs = performance.now() - disposeStartedAt; + + expect(disposeDurationMs).toBeLessThan(4_000); +}, 30_000);