Files
openclaw/extensions/mxc/test/binary-resolver.test.ts
Paul Campbell 008f04a656 feat(mxc): add Windows MXC sandbox backend (#97086)
* feat(mxc): add Windows MXC sandbox backend

Add the official MXC sandbox plugin package with Windows ProcessContainer execution, plugin-owned MXC SDK dependency packaging, host-backed filesystem bridge support, and configured MXC policy file loading via mxcPolicyPaths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(mxc): preserve Windows binary override paths

* fix: remove stray sandbox barrel export

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9ea19539-b8ca-44fb-93bd-b8496e3deb2c

* fix(mxc): address sandbox review feedback

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix(mxc): satisfy test type checks

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix(mxc): clarify protected skill enforcement

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* test(mxc): align fail-closed expectations

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix(mxc): satisfy extension lint

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix(plugin-sdk): narrow fs-safe remove surface

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix(mxc): repair rebased CI failures

* fix(scripts): declare shrinkwrap override normalizer

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Gio Della-Libera <235387111+giodl73-repo@users.noreply.github.com>
Co-authored-by: Dallin Romney <dallinromney@gmail.com>
2026-07-12 23:07:25 -07:00

125 lines
4.3 KiB
TypeScript

import path from "node:path";
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
// Mock node:fs and node:os for controlled testing
const { existsSyncMock, homedirMock } = vi.hoisted(() => ({
existsSyncMock: vi.fn(),
homedirMock: vi.fn(),
}));
vi.mock("node:fs", () => ({
existsSync: existsSyncMock,
}));
vi.mock("node:os", () => ({
homedir: homedirMock,
}));
import { resolveMxcBinaryPath } from "../src/binary-resolver.js";
describe("resolveMxcBinaryPath", () => {
const originalPath = process.env.PATH;
beforeEach(() => {
existsSyncMock.mockReset();
homedirMock.mockReturnValue("/home/openclaw");
process.env.PATH = "";
});
afterEach(() => {
process.env.PATH = originalPath;
});
test("config override returns the override path when file exists", () => {
existsSyncMock.mockReturnValue(true);
const result = resolveMxcBinaryPath("C:\\custom\\wxc-exec.exe");
expect(result).toBe("C:\\custom\\wxc-exec.exe");
});
test("config override returns an absolute path for relative inputs", () => {
const relativeOverride = path.join("tools", "wxc-exec.exe");
const absoluteOverride = path.resolve(relativeOverride);
existsSyncMock.mockImplementation((candidate) => candidate === absoluteOverride);
expect(resolveMxcBinaryPath(relativeOverride)).toBe(absoluteOverride);
});
test("config override throws when file does not exist", () => {
existsSyncMock.mockReturnValue(false);
expect(() => resolveMxcBinaryPath("C:\\missing\\wxc-exec.exe")).toThrow(
/not found at configured path/,
);
});
test("missing binary with no override throws descriptive error", () => {
existsSyncMock.mockReturnValue(false);
// Without override, it tries to discover; all paths will fail.
expect(() => resolveMxcBinaryPath()).toThrow(/wxc-exec\.exe.*not found/);
});
test("ignores project, PATH, and home candidates during discovery", () => {
const projectCandidate = path.join(process.cwd(), "bin", "wxc-exec.exe");
const homeCandidate = path.join("/home/openclaw", ".mxc", "wxc-exec.exe");
const trustedDir = "/trusted-path";
const pathCandidate = path.join(trustedDir, "wxc-exec.exe");
process.env.PATH = trustedDir;
existsSyncMock.mockImplementation((candidate) => {
const candidatePath = String(candidate);
return [projectCandidate, homeCandidate, pathCandidate].includes(candidatePath);
});
expect(() => resolveMxcBinaryPath()).toThrow(/wxc-exec\.exe.*not found/u);
});
test("resolves the SDK arch binary", () => {
const arch = process.arch === "arm64" ? "arm64" : "x64";
let sdkCandidate: string | undefined;
existsSyncMock.mockImplementation((candidate) => {
const candidatePath = String(candidate);
if (candidatePath.endsWith(`${path.sep}bin${path.sep}${arch}`)) {
return true;
}
if (candidatePath.endsWith(`${path.sep}bin${path.sep}${arch}${path.sep}wxc-exec.exe`)) {
sdkCandidate = candidatePath;
return true;
}
return false;
});
expect(resolveMxcBinaryPath()).toBe(sdkCandidate);
});
test("falls back to SDK flat bin when arch bin is absent", () => {
let sdkCandidate: string | undefined;
existsSyncMock.mockImplementation((candidate) => {
const candidatePath = String(candidate);
if (candidatePath.endsWith(`${path.sep}bin`)) {
return true;
}
if (candidatePath.endsWith(`${path.sep}bin${path.sep}wxc-exec.exe`)) {
sdkCandidate = candidatePath;
return true;
}
return false;
});
expect(resolveMxcBinaryPath()).toBe(sdkCandidate);
});
test("ignores empty and relative PATH entries during discovery", () => {
const relativeCandidate = path.join("relative-path", "wxc-exec.exe");
const currentDirectoryCandidate = path.join("", "wxc-exec.exe");
const trustedDir = "/trusted-path";
const trustedCandidate = path.join(trustedDir, "wxc-exec.exe");
process.env.PATH = `;relative-path;${trustedDir}`;
existsSyncMock.mockImplementation((candidate) => {
const candidatePath = String(candidate);
return [relativeCandidate, currentDirectoryCandidate, trustedCandidate].includes(
candidatePath,
);
});
expect(() => resolveMxcBinaryPath()).toThrow(/wxc-exec\.exe.*not found/u);
});
});