mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 04:11:22 +00:00
refactor(test): dedupe agent harnesses and routing fixtures
This commit is contained in:
@@ -13,15 +13,40 @@ vi.mock("../media/image-ops.js", () => ({
|
||||
import "./test-helpers/fast-core-tools.js";
|
||||
import { createOpenClawTools } from "./openclaw-tools.js";
|
||||
|
||||
describe("nodes camera_snap", () => {
|
||||
beforeEach(() => {
|
||||
callGateway.mockReset();
|
||||
});
|
||||
const NODE_ID = "mac-1";
|
||||
const BASE_RUN_INPUT = { action: "run", node: NODE_ID, command: ["echo", "hi"] } as const;
|
||||
|
||||
function unexpectedGatewayMethod(method: unknown): never {
|
||||
throw new Error(`unexpected method: ${String(method)}`);
|
||||
}
|
||||
|
||||
function getNodesTool() {
|
||||
const tool = createOpenClawTools().find((candidate) => candidate.name === "nodes");
|
||||
if (!tool) {
|
||||
throw new Error("missing nodes tool");
|
||||
}
|
||||
return tool;
|
||||
}
|
||||
|
||||
async function executeNodes(input: Record<string, unknown>) {
|
||||
return getNodesTool().execute("call1", input as never);
|
||||
}
|
||||
|
||||
function mockNodeList(commands?: string[]) {
|
||||
return {
|
||||
nodes: [{ nodeId: NODE_ID, ...(commands ? { commands } : {}) }],
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
callGateway.mockReset();
|
||||
});
|
||||
|
||||
describe("nodes camera_snap", () => {
|
||||
it("maps jpg payloads to image/jpeg", async () => {
|
||||
callGateway.mockImplementation(async ({ method }) => {
|
||||
if (method === "node.list") {
|
||||
return { nodes: [{ nodeId: "mac-1" }] };
|
||||
return mockNodeList();
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
return {
|
||||
@@ -33,17 +58,12 @@ describe("nodes camera_snap", () => {
|
||||
},
|
||||
};
|
||||
}
|
||||
throw new Error(`unexpected method: ${String(method)}`);
|
||||
return unexpectedGatewayMethod(method);
|
||||
});
|
||||
|
||||
const tool = createOpenClawTools().find((candidate) => candidate.name === "nodes");
|
||||
if (!tool) {
|
||||
throw new Error("missing nodes tool");
|
||||
}
|
||||
|
||||
const result = await tool.execute("call1", {
|
||||
const result = await executeNodes({
|
||||
action: "camera_snap",
|
||||
node: "mac-1",
|
||||
node: NODE_ID,
|
||||
facing: "front",
|
||||
});
|
||||
|
||||
@@ -55,7 +75,7 @@ describe("nodes camera_snap", () => {
|
||||
it("passes deviceId when provided", async () => {
|
||||
callGateway.mockImplementation(async ({ method, params }) => {
|
||||
if (method === "node.list") {
|
||||
return { nodes: [{ nodeId: "mac-1" }] };
|
||||
return mockNodeList();
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
expect(params).toMatchObject({
|
||||
@@ -71,17 +91,12 @@ describe("nodes camera_snap", () => {
|
||||
},
|
||||
};
|
||||
}
|
||||
throw new Error(`unexpected method: ${String(method)}`);
|
||||
return unexpectedGatewayMethod(method);
|
||||
});
|
||||
|
||||
const tool = createOpenClawTools().find((candidate) => candidate.name === "nodes");
|
||||
if (!tool) {
|
||||
throw new Error("missing nodes tool");
|
||||
}
|
||||
|
||||
await tool.execute("call1", {
|
||||
await executeNodes({
|
||||
action: "camera_snap",
|
||||
node: "mac-1",
|
||||
node: NODE_ID,
|
||||
facing: "front",
|
||||
deviceId: "cam-123",
|
||||
});
|
||||
@@ -89,18 +104,14 @@ describe("nodes camera_snap", () => {
|
||||
});
|
||||
|
||||
describe("nodes run", () => {
|
||||
beforeEach(() => {
|
||||
callGateway.mockReset();
|
||||
});
|
||||
|
||||
it("passes invoke and command timeouts", async () => {
|
||||
callGateway.mockImplementation(async ({ method, params }) => {
|
||||
if (method === "node.list") {
|
||||
return { nodes: [{ nodeId: "mac-1", commands: ["system.run"] }] };
|
||||
return mockNodeList(["system.run"]);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
expect(params).toMatchObject({
|
||||
nodeId: "mac-1",
|
||||
nodeId: NODE_ID,
|
||||
command: "system.run",
|
||||
timeoutMs: 45_000,
|
||||
params: {
|
||||
@@ -114,18 +125,11 @@ describe("nodes run", () => {
|
||||
payload: { stdout: "", stderr: "", exitCode: 0, success: true },
|
||||
};
|
||||
}
|
||||
throw new Error(`unexpected method: ${String(method)}`);
|
||||
return unexpectedGatewayMethod(method);
|
||||
});
|
||||
|
||||
const tool = createOpenClawTools().find((candidate) => candidate.name === "nodes");
|
||||
if (!tool) {
|
||||
throw new Error("missing nodes tool");
|
||||
}
|
||||
|
||||
await tool.execute("call1", {
|
||||
action: "run",
|
||||
node: "mac-1",
|
||||
command: ["echo", "hi"],
|
||||
await executeNodes({
|
||||
...BASE_RUN_INPUT,
|
||||
cwd: "/tmp",
|
||||
env: ["FOO=bar"],
|
||||
commandTimeoutMs: 12_000,
|
||||
@@ -138,7 +142,7 @@ describe("nodes run", () => {
|
||||
let approvalId: string | null = null;
|
||||
callGateway.mockImplementation(async ({ method, params }) => {
|
||||
if (method === "node.list") {
|
||||
return { nodes: [{ nodeId: "mac-1", commands: ["system.run"] }] };
|
||||
return mockNodeList(["system.run"]);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
invokeCalls += 1;
|
||||
@@ -146,7 +150,7 @@ describe("nodes run", () => {
|
||||
throw new Error("SYSTEM_RUN_DENIED: approval required");
|
||||
}
|
||||
expect(params).toMatchObject({
|
||||
nodeId: "mac-1",
|
||||
nodeId: NODE_ID,
|
||||
command: "system.run",
|
||||
params: {
|
||||
command: ["echo", "hi"],
|
||||
@@ -170,26 +174,17 @@ describe("nodes run", () => {
|
||||
: null;
|
||||
return { decision: "allow-once" };
|
||||
}
|
||||
throw new Error(`unexpected method: ${String(method)}`);
|
||||
return unexpectedGatewayMethod(method);
|
||||
});
|
||||
|
||||
const tool = createOpenClawTools().find((candidate) => candidate.name === "nodes");
|
||||
if (!tool) {
|
||||
throw new Error("missing nodes tool");
|
||||
}
|
||||
|
||||
await tool.execute("call1", {
|
||||
action: "run",
|
||||
node: "mac-1",
|
||||
command: ["echo", "hi"],
|
||||
});
|
||||
await executeNodes(BASE_RUN_INPUT);
|
||||
expect(invokeCalls).toBe(2);
|
||||
});
|
||||
|
||||
it("fails with user denied when approval decision is deny", async () => {
|
||||
callGateway.mockImplementation(async ({ method }) => {
|
||||
if (method === "node.list") {
|
||||
return { nodes: [{ nodeId: "mac-1", commands: ["system.run"] }] };
|
||||
return mockNodeList(["system.run"]);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
throw new Error("SYSTEM_RUN_DENIED: approval required");
|
||||
@@ -197,32 +192,16 @@ describe("nodes run", () => {
|
||||
if (method === "exec.approval.request") {
|
||||
return { decision: "deny" };
|
||||
}
|
||||
throw new Error(`unexpected method: ${String(method)}`);
|
||||
return unexpectedGatewayMethod(method);
|
||||
});
|
||||
|
||||
const tool = createOpenClawTools().find((candidate) => candidate.name === "nodes");
|
||||
if (!tool) {
|
||||
throw new Error("missing nodes tool");
|
||||
}
|
||||
|
||||
await expect(
|
||||
tool.execute("call1", {
|
||||
action: "run",
|
||||
node: "mac-1",
|
||||
command: ["echo", "hi"],
|
||||
}),
|
||||
).rejects.toThrow("exec denied: user denied");
|
||||
await expect(executeNodes(BASE_RUN_INPUT)).rejects.toThrow("exec denied: user denied");
|
||||
});
|
||||
|
||||
it("fails closed for timeout and invalid approval decisions", async () => {
|
||||
const tool = createOpenClawTools().find((candidate) => candidate.name === "nodes");
|
||||
if (!tool) {
|
||||
throw new Error("missing nodes tool");
|
||||
}
|
||||
|
||||
callGateway.mockImplementation(async ({ method }) => {
|
||||
if (method === "node.list") {
|
||||
return { nodes: [{ nodeId: "mac-1", commands: ["system.run"] }] };
|
||||
return mockNodeList(["system.run"]);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
throw new Error("SYSTEM_RUN_DENIED: approval required");
|
||||
@@ -230,19 +209,13 @@ describe("nodes run", () => {
|
||||
if (method === "exec.approval.request") {
|
||||
return {};
|
||||
}
|
||||
throw new Error(`unexpected method: ${String(method)}`);
|
||||
return unexpectedGatewayMethod(method);
|
||||
});
|
||||
await expect(
|
||||
tool.execute("call1", {
|
||||
action: "run",
|
||||
node: "mac-1",
|
||||
command: ["echo", "hi"],
|
||||
}),
|
||||
).rejects.toThrow("exec denied: approval timed out");
|
||||
await expect(executeNodes(BASE_RUN_INPUT)).rejects.toThrow("exec denied: approval timed out");
|
||||
|
||||
callGateway.mockImplementation(async ({ method }) => {
|
||||
if (method === "node.list") {
|
||||
return { nodes: [{ nodeId: "mac-1", commands: ["system.run"] }] };
|
||||
return mockNodeList(["system.run"]);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
throw new Error("SYSTEM_RUN_DENIED: approval required");
|
||||
@@ -250,14 +223,10 @@ describe("nodes run", () => {
|
||||
if (method === "exec.approval.request") {
|
||||
return { decision: "allow-never" };
|
||||
}
|
||||
throw new Error(`unexpected method: ${String(method)}`);
|
||||
return unexpectedGatewayMethod(method);
|
||||
});
|
||||
await expect(
|
||||
tool.execute("call1", {
|
||||
action: "run",
|
||||
node: "mac-1",
|
||||
command: ["echo", "hi"],
|
||||
}),
|
||||
).rejects.toThrow("exec denied: invalid approval decision");
|
||||
await expect(executeNodes(BASE_RUN_INPUT)).rejects.toThrow(
|
||||
"exec denied: invalid approval decision",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user