test(FN-4639): complete Step 5 — add sandbox exports and tests
Fusion-Task-Id: FN-4639 Fusion-Task-Lineage: 40745d49-dd20-4c51-87e3-42062417f788
This commit is contained in:
55
packages/core/src/__tests__/sandbox-prompt-override.test.ts
Normal file
55
packages/core/src/__tests__/sandbox-prompt-override.test.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { SANDBOX_BACKEND_NAMES } from "../settings-validation.js";
|
||||
import { parseSandboxPromptOverride, resolveSandboxBackend } from "../sandbox-prompt-override.js";
|
||||
|
||||
describe("sandbox prompt override", () => {
|
||||
describe("parseSandboxPromptOverride", () => {
|
||||
it.each(SANDBOX_BACKEND_NAMES)("parses %s", (backend) => {
|
||||
expect(parseSandboxPromptOverride(`**Sandbox:** ${backend}`)).toBe(backend);
|
||||
});
|
||||
|
||||
it("parses when prefix casing varies", () => {
|
||||
expect(parseSandboxPromptOverride("**sAnDbOx:** native")).toBe("native");
|
||||
});
|
||||
|
||||
it("does not accept mixed-case backend values", () => {
|
||||
expect(parseSandboxPromptOverride("**Sandbox:** Docker")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("parses in multi-line prompt content", () => {
|
||||
expect(parseSandboxPromptOverride("# Task\nSome content\n**Sandbox:** podman\nMore text")).toBe("podman");
|
||||
});
|
||||
|
||||
it("returns undefined for missing or malformed inputs", () => {
|
||||
expect(parseSandboxPromptOverride(undefined)).toBeUndefined();
|
||||
expect(parseSandboxPromptOverride("")).toBeUndefined();
|
||||
expect(parseSandboxPromptOverride("Sandbox: docker")).toBeUndefined();
|
||||
expect(parseSandboxPromptOverride("**Sandbox:** firejail")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveSandboxBackend", () => {
|
||||
it("prefers prompt override", () => {
|
||||
expect(
|
||||
resolveSandboxBackend(
|
||||
{ sandbox: { backend: "docker" } },
|
||||
"**Sandbox:** bubblewrap",
|
||||
),
|
||||
).toEqual({ backend: "bubblewrap", source: "prompt" });
|
||||
});
|
||||
|
||||
it("falls back to project setting", () => {
|
||||
expect(resolveSandboxBackend({ sandbox: { backend: "podman" } }, undefined)).toEqual({
|
||||
backend: "podman",
|
||||
source: "project",
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to default", () => {
|
||||
expect(resolveSandboxBackend(undefined, undefined)).toEqual({
|
||||
backend: "native",
|
||||
source: "default",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
105
packages/core/src/__tests__/sandbox-settings.test.ts
Normal file
105
packages/core/src/__tests__/sandbox-settings.test.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
DEFAULT_PROJECT_SETTINGS,
|
||||
GLOBAL_SETTINGS_KEYS,
|
||||
PROJECT_SETTINGS_KEYS,
|
||||
SANDBOX_BACKEND_NAMES,
|
||||
SANDBOX_FAILURE_MODES,
|
||||
validateSandboxBackendName,
|
||||
validateSandboxFailureMode,
|
||||
validateSandboxPolicy,
|
||||
validateSandboxProjectSettings,
|
||||
} from "../index.js";
|
||||
|
||||
describe("sandbox settings", () => {
|
||||
it("defines sandbox defaults and scope keys", () => {
|
||||
expect(DEFAULT_PROJECT_SETTINGS.sandbox).toEqual({
|
||||
backend: "native",
|
||||
policy: { allowNetwork: true, allowedPaths: [] },
|
||||
failureMode: "fail-hard",
|
||||
});
|
||||
expect(PROJECT_SETTINGS_KEYS).toContain("sandbox");
|
||||
expect(GLOBAL_SETTINGS_KEYS).not.toContain("sandbox");
|
||||
});
|
||||
|
||||
describe("validateSandboxBackendName", () => {
|
||||
it.each(SANDBOX_BACKEND_NAMES)("accepts %s", (backend) => {
|
||||
expect(validateSandboxBackendName(backend)).toBe(backend);
|
||||
});
|
||||
|
||||
it("rejects invalid values", () => {
|
||||
expect(validateSandboxBackendName("firejail")).toBeUndefined();
|
||||
expect(validateSandboxBackendName(123)).toBeUndefined();
|
||||
expect(validateSandboxBackendName(null)).toBeUndefined();
|
||||
expect(validateSandboxBackendName(undefined)).toBeUndefined();
|
||||
expect(validateSandboxBackendName("")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("validateSandboxFailureMode", () => {
|
||||
it.each(SANDBOX_FAILURE_MODES)("accepts %s", (mode) => {
|
||||
expect(validateSandboxFailureMode(mode)).toBe(mode);
|
||||
});
|
||||
|
||||
it("rejects invalid values", () => {
|
||||
expect(validateSandboxFailureMode("fallback")).toBeUndefined();
|
||||
expect(validateSandboxFailureMode({})).toBeUndefined();
|
||||
expect(validateSandboxFailureMode(null)).toBeUndefined();
|
||||
expect(validateSandboxFailureMode(undefined)).toBeUndefined();
|
||||
expect(validateSandboxFailureMode("")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("validateSandboxPolicy", () => {
|
||||
it("accepts individual valid keys", () => {
|
||||
expect(validateSandboxPolicy({ allowNetwork: true })).toEqual({ allowNetwork: true });
|
||||
expect(validateSandboxPolicy({ allowedPaths: ["foo", "bar/baz"] })).toEqual({
|
||||
allowedPaths: ["foo", "bar/baz"],
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects invalid policy payloads", () => {
|
||||
expect(validateSandboxPolicy({ allowedPaths: [""] })).toBeUndefined();
|
||||
expect(validateSandboxPolicy({ allowedPaths: "not-an-array" })).toBeUndefined();
|
||||
expect(validateSandboxPolicy(null)).toBeUndefined();
|
||||
expect(validateSandboxPolicy([])).toBeUndefined();
|
||||
expect(validateSandboxPolicy(42)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("drops invalid sub-fields when a valid field remains", () => {
|
||||
expect(validateSandboxPolicy({ allowNetwork: true, allowedPaths: ["", "ok"] })).toEqual({
|
||||
allowNetwork: true,
|
||||
});
|
||||
expect(validateSandboxPolicy({ allowNetwork: "yes", allowedPaths: ["ok"] })).toEqual({
|
||||
allowedPaths: ["ok"],
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects traversal and tilde paths", () => {
|
||||
expect(validateSandboxPolicy({ allowedPaths: ["../etc"] })).toBeUndefined();
|
||||
expect(validateSandboxPolicy({ allowedPaths: ["~/secret"] })).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("validateSandboxProjectSettings", () => {
|
||||
it("composes valid nested settings", () => {
|
||||
expect(
|
||||
validateSandboxProjectSettings({
|
||||
backend: "docker",
|
||||
failureMode: "fallback-native",
|
||||
policy: { allowNetwork: false, allowedPaths: ["src/**"] },
|
||||
}),
|
||||
).toEqual({
|
||||
backend: "docker",
|
||||
failureMode: "fallback-native",
|
||||
policy: { allowNetwork: false, allowedPaths: ["src/**"] },
|
||||
});
|
||||
});
|
||||
|
||||
it("returns undefined for empty and invalid inputs", () => {
|
||||
expect(validateSandboxProjectSettings({})).toBeUndefined();
|
||||
expect(validateSandboxProjectSettings({ backend: "firejail", policy: { allowedPaths: [""] } })).toBeUndefined();
|
||||
expect(validateSandboxProjectSettings("nope")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user