Files
openclaw/src/agents/sandbox-merge.test.ts
2026-02-24 01:46:12 +00:00

132 lines
4.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
resolveSandboxBrowserConfig,
resolveSandboxDockerConfig,
resolveSandboxPruneConfig,
resolveSandboxScope,
} from "./sandbox/config.js";
describe("sandbox config merges", () => {
it("resolves sandbox scope deterministically", () => {
expect(resolveSandboxScope({})).toBe("agent");
expect(resolveSandboxScope({ perSession: true })).toBe("session");
expect(resolveSandboxScope({ perSession: false })).toBe("shared");
expect(resolveSandboxScope({ perSession: true, scope: "agent" })).toBe("agent");
});
it("merges sandbox docker env and ulimits (agent wins)", () => {
const resolved = resolveSandboxDockerConfig({
scope: "agent",
globalDocker: {
env: { LANG: "C.UTF-8", FOO: "1" },
ulimits: { nofile: { soft: 10, hard: 20 } },
},
agentDocker: {
env: { FOO: "2", BAR: "3" },
ulimits: { nproc: 256 },
},
});
expect(resolved.env).toEqual({ LANG: "C.UTF-8", FOO: "2", BAR: "3" });
expect(resolved.ulimits).toEqual({
nofile: { soft: 10, hard: 20 },
nproc: 256,
});
});
it("resolves docker binds and shared-scope override behavior", () => {
for (const scenario of [
{
name: "merges sandbox docker binds (global + agent combined)",
input: {
scope: "agent" as const,
globalDocker: {
binds: ["/var/run/docker.sock:/var/run/docker.sock"],
},
agentDocker: {
binds: ["/home/user/source:/source:rw"],
},
},
assert: (resolved: ReturnType<typeof resolveSandboxDockerConfig>) => {
expect(resolved.binds).toEqual([
"/var/run/docker.sock:/var/run/docker.sock",
"/home/user/source:/source:rw",
]);
},
},
{
name: "returns undefined binds when neither global nor agent has binds",
input: {
scope: "agent" as const,
globalDocker: {},
agentDocker: {},
},
assert: (resolved: ReturnType<typeof resolveSandboxDockerConfig>) => {
expect(resolved.binds).toBeUndefined();
},
},
{
name: "ignores agent binds under shared scope",
input: {
scope: "shared" as const,
globalDocker: {
binds: ["/var/run/docker.sock:/var/run/docker.sock"],
},
agentDocker: {
binds: ["/home/user/source:/source:rw"],
},
},
assert: (resolved: ReturnType<typeof resolveSandboxDockerConfig>) => {
expect(resolved.binds).toEqual(["/var/run/docker.sock:/var/run/docker.sock"]);
},
},
{
name: "ignores agent docker overrides under shared scope",
input: {
scope: "shared" as const,
globalDocker: { image: "global" },
agentDocker: { image: "agent" },
},
assert: (resolved: ReturnType<typeof resolveSandboxDockerConfig>) => {
expect(resolved.image).toBe("global");
},
},
]) {
const resolved = resolveSandboxDockerConfig(scenario.input);
scenario.assert(resolved);
}
});
it("applies per-agent browser and prune overrides (ignored under shared scope)", () => {
const browser = resolveSandboxBrowserConfig({
scope: "agent",
globalBrowser: { enabled: false, headless: false, enableNoVnc: true },
agentBrowser: { enabled: true, headless: true, enableNoVnc: false },
});
expect(browser.enabled).toBe(true);
expect(browser.headless).toBe(true);
expect(browser.enableNoVnc).toBe(false);
const prune = resolveSandboxPruneConfig({
scope: "agent",
globalPrune: { idleHours: 24, maxAgeDays: 7 },
agentPrune: { idleHours: 0, maxAgeDays: 1 },
});
expect(prune).toEqual({ idleHours: 0, maxAgeDays: 1 });
const browserShared = resolveSandboxBrowserConfig({
scope: "shared",
globalBrowser: { enabled: false },
agentBrowser: { enabled: true },
});
expect(browserShared.enabled).toBe(false);
const pruneShared = resolveSandboxPruneConfig({
scope: "shared",
globalPrune: { idleHours: 24, maxAgeDays: 7 },
agentPrune: { idleHours: 0, maxAgeDays: 1 },
});
expect(pruneShared).toEqual({ idleHours: 24, maxAgeDays: 7 });
});
});