mirror of
https://github.com/hanzoai/vm.git
synced 2026-10-01 00:47:56 +00:00
Crates keep their history: vm (lib, was shuru-vm), hanzo-vm (CLI package and binary, was shuru-cli), vm-guest, vm-proto, vm-proxy, vm-sdk, vm-store, vm-darwin, vm-linux; all at 0.1.0, Apache-2.0, hanzoai/vm. Config is vm.json, data lives in ~/.hanzo/vm (HANZO_VM_HOME), env vars are HANZO_VM_*, the guest runs as /usr/bin/vm-guest, and assets download from hanzoai/vm releases. The TypeScript SDK is @hanzo/vm. NOTICE credits shuru as the origin. prepare-rootfs.sh builds the image natively on arm64 Linux with mke2fs -d and re-execs in Docker only on macOS. Includes rustfmt over the tree and fixes for clippy 1.98 lints. Co-authored-by: Hanzo Dev <[email protected]>
186 lines
3.9 KiB
TypeScript
186 lines
3.9 KiB
TypeScript
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
import { resolve } from "node:path";
|
|
import { Sandbox } from "../../src/sandbox";
|
|
|
|
const MOCK_BIN = `bun ${resolve(import.meta.dir, "mock-vm.ts")}`;
|
|
|
|
describe("mkdir", () => {
|
|
let sb: Sandbox;
|
|
|
|
beforeAll(async () => {
|
|
sb = await Sandbox.start({ bin: MOCK_BIN });
|
|
}, 10_000);
|
|
|
|
afterAll(async () => {
|
|
await sb?.stop();
|
|
}, 5_000);
|
|
|
|
test("creates directory", async () => {
|
|
await sb.mkdir("/tmp/test");
|
|
});
|
|
|
|
test("recursive defaults to true", async () => {
|
|
await sb.mkdir("/tmp/a/b/c");
|
|
});
|
|
|
|
test("explicit recursive false", async () => {
|
|
await sb.mkdir("/tmp/single", { recursive: false });
|
|
});
|
|
|
|
test("throws on failure", async () => {
|
|
await expect(sb.mkdir("/fail")).rejects.toThrow("Permission denied");
|
|
});
|
|
});
|
|
|
|
describe("readDir", () => {
|
|
let sb: Sandbox;
|
|
|
|
beforeAll(async () => {
|
|
sb = await Sandbox.start({ bin: MOCK_BIN });
|
|
}, 10_000);
|
|
|
|
afterAll(async () => {
|
|
await sb?.stop();
|
|
}, 5_000);
|
|
|
|
test("returns entries with name, type, size", async () => {
|
|
const entries = await sb.readDir("/tmp");
|
|
expect(entries).toHaveLength(3);
|
|
expect(entries[0]).toEqual({ name: "file.txt", type: "file", size: 42 });
|
|
expect(entries[1]).toEqual({
|
|
name: "subdir",
|
|
type: "dir",
|
|
size: 4096,
|
|
});
|
|
expect(entries[2]).toEqual({ name: "link", type: "symlink", size: 10 });
|
|
});
|
|
|
|
test("throws on nonexistent path", async () => {
|
|
await expect(sb.readDir("/nonexistent")).rejects.toThrow(
|
|
"No such file or directory",
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("stat", () => {
|
|
let sb: Sandbox;
|
|
|
|
beforeAll(async () => {
|
|
sb = await Sandbox.start({ bin: MOCK_BIN });
|
|
}, 10_000);
|
|
|
|
afterAll(async () => {
|
|
await sb?.stop();
|
|
}, 5_000);
|
|
|
|
test("returns metadata with camelCase fields", async () => {
|
|
const s = await sb.stat("/tmp/file.txt");
|
|
expect(s.size).toBe(1024);
|
|
expect(s.mode).toBe(0o100644);
|
|
expect(s.mtime).toBe(1700000000);
|
|
expect(s.isDir).toBe(false);
|
|
expect(s.isFile).toBe(true);
|
|
expect(s.isSymlink).toBe(false);
|
|
});
|
|
|
|
test("throws on nonexistent path", async () => {
|
|
await expect(sb.stat("/nonexistent")).rejects.toThrow(
|
|
"No such file or directory",
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("remove", () => {
|
|
let sb: Sandbox;
|
|
|
|
beforeAll(async () => {
|
|
sb = await Sandbox.start({ bin: MOCK_BIN });
|
|
}, 10_000);
|
|
|
|
afterAll(async () => {
|
|
await sb?.stop();
|
|
}, 5_000);
|
|
|
|
test("removes file", async () => {
|
|
await sb.remove("/tmp/file.txt");
|
|
});
|
|
|
|
test("removes directory recursively", async () => {
|
|
await sb.remove("/tmp/dir", { recursive: true });
|
|
});
|
|
});
|
|
|
|
describe("rename", () => {
|
|
let sb: Sandbox;
|
|
|
|
beforeAll(async () => {
|
|
sb = await Sandbox.start({ bin: MOCK_BIN });
|
|
}, 10_000);
|
|
|
|
afterAll(async () => {
|
|
await sb?.stop();
|
|
}, 5_000);
|
|
|
|
test("renames file", async () => {
|
|
await sb.rename("/tmp/old.txt", "/tmp/new.txt");
|
|
});
|
|
});
|
|
|
|
describe("copy", () => {
|
|
let sb: Sandbox;
|
|
|
|
beforeAll(async () => {
|
|
sb = await Sandbox.start({ bin: MOCK_BIN });
|
|
}, 10_000);
|
|
|
|
afterAll(async () => {
|
|
await sb?.stop();
|
|
}, 5_000);
|
|
|
|
test("copies file", async () => {
|
|
await sb.copy("/tmp/src.txt", "/tmp/dst.txt");
|
|
});
|
|
|
|
test("copies directory recursively", async () => {
|
|
await sb.copy("/tmp/srcdir", "/tmp/dstdir", { recursive: true });
|
|
});
|
|
});
|
|
|
|
describe("chmod", () => {
|
|
let sb: Sandbox;
|
|
|
|
beforeAll(async () => {
|
|
sb = await Sandbox.start({ bin: MOCK_BIN });
|
|
}, 10_000);
|
|
|
|
afterAll(async () => {
|
|
await sb?.stop();
|
|
}, 5_000);
|
|
|
|
test("changes permissions", async () => {
|
|
await sb.chmod("/tmp/file.txt", 0o755);
|
|
});
|
|
});
|
|
|
|
describe("exists", () => {
|
|
let sb: Sandbox;
|
|
|
|
beforeAll(async () => {
|
|
sb = await Sandbox.start({ bin: MOCK_BIN });
|
|
}, 10_000);
|
|
|
|
afterAll(async () => {
|
|
await sb?.stop();
|
|
}, 5_000);
|
|
|
|
test("returns true for existing path", async () => {
|
|
const result = await sb.exists("/tmp/file.txt");
|
|
expect(result).toBe(true);
|
|
});
|
|
|
|
test("returns false for nonexistent path", async () => {
|
|
const result = await sb.exists("/nonexistent");
|
|
expect(result).toBe(false);
|
|
});
|
|
});
|