138 lines · 4.3 KB · TypeScript
RawDownload
// Nightly: projects the complete registry command bundle.import { afterAll, beforeAll, describe, expect, test } from "vitest";import { AgentOs } from "../src/index.js";import { REGISTRY_SOFTWARE } from "./helpers/registry-commands.js"; // End-to-end coverage for the Pyodide-powered `python` / `python3` CLI exposed// by secure-exec, driven through the public AgentOs API. `python` resolves as a// runtime command (like `node`): `vm.exec()` / `vm.execArgv()` / `spawn()` route// it directly to the embedded Pyodide runtime, and a `/bin/python` stub also// makes it resolvable on the guest shell `PATH`. The Python runtime bridges the// whole VM filesystem into Pyodide, so scripts and file I/O work anywhere on the// VM (these tests use `/tmp`).describe("python CLI (Pyodide runtime)", () => {	let vm: AgentOs; 	beforeAll(async () => {		vm = await AgentOs.create();	}, 120_000); 	afterAll(async () => {		await vm?.dispose();	}); 	test(		"python -c runs inline code",		async () => {			const result = await vm.execArgv("python", ["-c", "print(1 + 1)"]);			expect(result.exitCode, result.stderr).toBe(0);			expect(result.stdout.trim()).toBe("2");		},		120_000,	); 	test(		"python via vm.exec (agent command path) runs inline code",		async () => {			const result = await vm.exec('python -c "print(2 + 3)"');			expect(result.exitCode, result.stderr).toBe(0);			expect(result.stdout.trim()).toBe("5");		},		120_000,	); 	test(		"python runs a script file anywhere on the VM filesystem with sys.argv",		async () => {			// /tmp is bridged into Python via the whole-root mount (not just /workspace).			await vm.writeFile(				"/tmp/argv.py",				"import sys\nprint(','.join(sys.argv))\n",			);			const result = await vm.execArgv("python", [				"/tmp/argv.py",				"alpha",				"beta",			]);			expect(result.exitCode, result.stderr).toBe(0);			expect(result.stdout.trim()).toBe("/tmp/argv.py,alpha,beta");		},		120_000,	); 	test(		"python writes to the VM filesystem, visible to the host (cross-runtime)",		async () => {			const write = await vm.execArgv("python", [				"-c",				"open('/tmp/from-python.txt','w').write('written-by-python')",			]);			expect(write.exitCode, write.stderr).toBe(0);			// The write landed in the kernel VFS, so the host sees it too.			const contents = await vm.readFile("/tmp/from-python.txt");			expect(Buffer.from(contents).toString("utf8")).toBe("written-by-python");		},		120_000,	); 	test(		"python -m runs a module",		async () => {			const result = await vm.execArgv("python", ["-m", "this"]);			expect(result.exitCode, result.stderr).toBe(0);			expect(result.stdout).toContain("Beautiful is better than ugly");		},		120_000,	); 	test(		"python3 alias runs inline code",		async () => {			const result = await vm.execArgv("python3", ["-c", "print(6 * 7)"]);			expect(result.exitCode, result.stderr).toBe(0);			expect(result.stdout.trim()).toBe("42");		},		120_000,	); 	test(		"python - reads the program from stdin",		async () => {			const chunks: string[] = [];			const errors: string[] = [];			const { pid } = vm.spawn("python", ["-"], {				onStdout: (data) => chunks.push(Buffer.from(data).toString("utf8")),				onStderr: (data) => errors.push(Buffer.from(data).toString("utf8")),			});			await vm.writeProcessStdin(pid, "print('from stdin program')\n");			await vm.closeProcessStdin(pid);			const exitCode = await vm.waitProcess(pid);			// Native-sidecar process_output can lag the exit notification by a turn.			await new Promise((resolve) => setTimeout(resolve, 0));			expect(exitCode, errors.join("")).toBe(0);			expect(chunks.join("")).toContain("from stdin program");		},		120_000,	); 	// The guest-shell path needs the WASM `sh` from the registry. `python` resolves	// on the shell PATH via its `/bin/python` stub, so `sh -c`/pipelines work.	test(		"python runs through the guest shell and pipelines",		async () => {			const shellVm = await AgentOs.create({ software: REGISTRY_SOFTWARE });			try {				const direct = await shellVm.exec('sh -c "python -c \'print(2 + 3)\'"');				expect(direct.exitCode, direct.stderr).toBe(0);				expect(direct.stdout.trim()).toBe("5"); 				const piped = await shellVm.exec('echo "print(6 * 7)" | python -');				expect(piped.exitCode, piped.stderr).toBe(0);				expect(piped.stdout.trim()).toBe("42");			} finally {				await shellVm.dispose();			}		},		120_000,	);});