test: speed up safe-bins exec harness

This commit is contained in:
Peter Steinberger
2026-04-17 02:34:41 +01:00
parent acd86a06cd
commit ee856ab31f

View File

@@ -1,46 +1,17 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { beforeAll, describe, expect, it, vi } from "vitest";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import type { ExecApprovalsResolved } from "../infra/exec-approvals.js";
import type { SafeBinProfileFixture } from "../infra/exec-safe-bin-policy.js";
import { withEnvAsync } from "../test-utils/env.js";
import { resetProcessRegistryForTests } from "./bash-process-registry.js";
let createOpenClawCodingTools: typeof import("./pi-tools.js").createOpenClawCodingTools;
beforeAll(async () => {
await withEnvAsync(
{
OPENCLAW_BUNDLED_PLUGINS_DIR: path.join(os.tmpdir(), "openclaw-test-no-bundled-extensions"),
},
async () => {
({ createOpenClawCodingTools } = await import("./pi-tools.js"));
},
);
});
vi.mock("../infra/shell-env.js", async () => {
const mod =
await vi.importActual<typeof import("../infra/shell-env.js")>("../infra/shell-env.js");
return {
...mod,
getShellPathFromLoginShell: vi.fn(() => null),
resolveShellEnvFallbackTimeoutMs: vi.fn(() => 50),
};
});
vi.mock("../plugins/tools.js", () => ({
copyPluginToolMeta: vi.fn((_from, to) => to),
resolvePluginTools: () => [],
getPluginToolMeta: () => undefined,
}));
vi.mock("../infra/exec-approvals.js", async () => {
const mod = await vi.importActual<typeof import("../infra/exec-approvals.js")>(
"../infra/exec-approvals.js",
);
const approvals: ExecApprovalsResolved = {
const { mockExecApprovals, supervisorSpawnMock } = vi.hoisted(() => {
const execApprovals = {
path: "/tmp/exec-approvals.json",
socketPath: "/tmp/exec-approvals.sock",
token: "token",
@@ -74,7 +45,123 @@ vi.mock("../infra/exec-approvals.js", async () => {
agents: {},
},
};
return { ...mod, resolveExecApprovals: () => approvals };
return {
mockExecApprovals: execApprovals,
supervisorSpawnMock: vi.fn(
async (input: { argv?: string[]; onStdout?: (chunk: string) => void }) => {
input.onStdout?.(`${input.argv?.join(" ") ?? ""}\n`);
return {
runId: "safe-bins-test-run",
pid: 1234,
startedAtMs: Date.now(),
stdin: undefined,
wait: async () => ({
reason: "exit" as const,
exitCode: 0,
exitSignal: null,
durationMs: 1,
stdout: "",
stderr: "",
timedOut: false,
noOutputTimedOut: false,
}),
cancel: vi.fn(),
};
},
),
};
});
beforeAll(async () => {
await withEnvAsync(
{
OPENCLAW_BUNDLED_PLUGINS_DIR: path.join(os.tmpdir(), "openclaw-test-no-bundled-extensions"),
},
async () => {
({ createOpenClawCodingTools } = await import("./pi-tools.js"));
},
);
});
beforeEach(() => {
supervisorSpawnMock.mockClear();
});
vi.mock("../infra/shell-env.js", async () => {
const mod =
await vi.importActual<typeof import("../infra/shell-env.js")>("../infra/shell-env.js");
return {
...mod,
getShellPathFromLoginShell: vi.fn(() => null),
resolveShellEnvFallbackTimeoutMs: vi.fn(() => 50),
};
});
vi.mock("../process/supervisor/index.js", () => ({
getProcessSupervisor: () => ({
spawn: supervisorSpawnMock,
cancel: vi.fn(),
cancelScope: vi.fn(),
reconcileOrphans: vi.fn(),
getRecord: vi.fn(),
}),
}));
vi.mock("./channel-tools.js", () => ({
copyChannelAgentToolMeta: vi.fn((_from, to) => to),
listChannelAgentTools: () => [],
}));
vi.mock("./openclaw-tools.js", () => ({
createOpenClawTools: () => [],
}));
vi.mock("./bash-tools.exec-host-shared.js", async () => {
const mod = await vi.importActual<typeof import("./bash-tools.exec-host-shared.js")>(
"./bash-tools.exec-host-shared.js",
);
return {
...mod,
resolveExecHostApprovalContext: () => ({
approvals: mockExecApprovals,
hostSecurity: "allowlist",
hostAsk: "off",
askFallback: "deny",
}),
};
});
vi.mock("../plugins/tools.js", () => ({
copyPluginToolMeta: vi.fn((_from, to) => to),
resolvePluginTools: () => [],
getPluginToolMeta: () => undefined,
}));
vi.mock("@mariozechner/pi-coding-agent", () => ({
AuthStorage: vi.fn(),
CURRENT_SESSION_VERSION: 1,
ModelRegistry: vi.fn(),
SessionManager: vi.fn(),
SettingsManager: vi.fn(),
codingTools: [],
createEditTool: vi.fn(),
createReadTool: vi.fn(),
createWriteTool: vi.fn(),
estimateTokens: vi.fn(() => 0),
formatSkillsForPrompt: vi.fn(() => ""),
readTool: undefined,
}));
vi.mock("../infra/exec-approvals.js", async () => {
const mod = await vi.importActual<typeof import("../infra/exec-approvals.js")>(
"../infra/exec-approvals.js",
);
const approvals = mockExecApprovals as ExecApprovalsResolved;
return {
...mod,
loadExecApprovals: () => approvals.file,
resolveExecApprovals: () => approvals,
};
});
type ExecToolResult = {
@@ -118,6 +205,9 @@ async function createSafeBinsExecTool(params: {
const tools = createOpenClawCodingTools({
config: cfg,
exec: {
notifyOnExit: false,
},
sessionKey: "agent:main:main",
workspaceDir: tmpDir,
agentDir: path.join(tmpDir, "agent"),
@@ -138,9 +228,18 @@ async function withSafeBinsExecTool(
}
const ctx = await createSafeBinsExecTool(params);
try {
await run(ctx);
await withEnvAsync(
{
OPENCLAW_SHELL_ENV_TIMEOUT_MS: "1",
SHELL: "/bin/sh",
},
async () => {
await run(ctx);
},
);
} finally {
fs.rmSync(ctx.tmpDir, { recursive: true, force: true });
resetProcessRegistryForTests();
}
}
@@ -165,6 +264,7 @@ describe("createOpenClawCodingTools safeBins", () => {
const resultDetails = result.details as { status?: string };
expect(resultDetails.status).toBe("completed");
expect(text).toContain(marker);
expect(supervisorSpawnMock).toHaveBeenCalledOnce();
},
);
});