mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-11 03:24:32 +00:00
test(agents): dedupe agent and cron test scaffolds
This commit is contained in:
@@ -15,6 +15,14 @@ import { createOpenClawTools } from "./openclaw-tools.js";
|
||||
|
||||
const NODE_ID = "mac-1";
|
||||
const BASE_RUN_INPUT = { action: "run", node: NODE_ID, command: ["echo", "hi"] } as const;
|
||||
const JPG_PAYLOAD = {
|
||||
format: "jpg",
|
||||
base64: "aGVsbG8=",
|
||||
width: 1,
|
||||
height: 1,
|
||||
} as const;
|
||||
|
||||
type GatewayCall = { method: string; params?: unknown };
|
||||
|
||||
function unexpectedGatewayMethod(method: unknown): never {
|
||||
throw new Error(`unexpected method: ${String(method)}`);
|
||||
@@ -32,24 +40,99 @@ async function executeNodes(input: Record<string, unknown>) {
|
||||
return getNodesTool().execute("call1", input as never);
|
||||
}
|
||||
|
||||
type NodesToolResult = Awaited<ReturnType<typeof executeNodes>>;
|
||||
type GatewayMockResult = Record<string, unknown> | null | undefined;
|
||||
|
||||
function mockNodeList(commands?: string[]) {
|
||||
return {
|
||||
nodes: [{ nodeId: NODE_ID, ...(commands ? { commands } : {}) }],
|
||||
};
|
||||
}
|
||||
|
||||
function expectSingleImage(result: NodesToolResult, params?: { mimeType?: string }) {
|
||||
const images = (result.content ?? []).filter((block) => block.type === "image");
|
||||
expect(images).toHaveLength(1);
|
||||
if (params?.mimeType) {
|
||||
expect(images[0]?.mimeType).toBe(params.mimeType);
|
||||
}
|
||||
}
|
||||
|
||||
function expectFirstTextContains(result: NodesToolResult, expectedText: string) {
|
||||
expect(result.content?.[0]).toMatchObject({
|
||||
type: "text",
|
||||
text: expect.stringContaining(expectedText),
|
||||
});
|
||||
}
|
||||
|
||||
function setupNodeInvokeMock(params: {
|
||||
commands?: string[];
|
||||
onInvoke?: (invokeParams: unknown) => GatewayMockResult | Promise<GatewayMockResult>;
|
||||
invokePayload?: unknown;
|
||||
}) {
|
||||
callGateway.mockImplementation(async ({ method, params: invokeParams }: GatewayCall) => {
|
||||
if (method === "node.list") {
|
||||
return mockNodeList(params.commands);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
if (params.onInvoke) {
|
||||
return await params.onInvoke(invokeParams);
|
||||
}
|
||||
if (params.invokePayload !== undefined) {
|
||||
return { payload: params.invokePayload };
|
||||
}
|
||||
return { payload: {} };
|
||||
}
|
||||
return unexpectedGatewayMethod(method);
|
||||
});
|
||||
}
|
||||
|
||||
function createSystemRunPreparePayload(cwd: string | null) {
|
||||
return {
|
||||
payload: {
|
||||
cmdText: "echo hi",
|
||||
plan: {
|
||||
argv: ["echo", "hi"],
|
||||
cwd,
|
||||
rawCommand: "echo hi",
|
||||
agentId: null,
|
||||
sessionKey: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function setupSystemRunGateway(params: {
|
||||
onRunInvoke: (invokeParams: unknown) => GatewayMockResult | Promise<GatewayMockResult>;
|
||||
onApprovalRequest?: (approvalParams: unknown) => GatewayMockResult | Promise<GatewayMockResult>;
|
||||
prepareCwd?: string | null;
|
||||
}) {
|
||||
callGateway.mockImplementation(async ({ method, params: gatewayParams }: GatewayCall) => {
|
||||
if (method === "node.list") {
|
||||
return mockNodeList(["system.run"]);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
const command = (gatewayParams as { command?: string } | undefined)?.command;
|
||||
if (command === "system.run.prepare") {
|
||||
return createSystemRunPreparePayload(params.prepareCwd ?? null);
|
||||
}
|
||||
return await params.onRunInvoke(gatewayParams);
|
||||
}
|
||||
if (method === "exec.approval.request" && params.onApprovalRequest) {
|
||||
return await params.onApprovalRequest(gatewayParams);
|
||||
}
|
||||
return unexpectedGatewayMethod(method);
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
callGateway.mockClear();
|
||||
});
|
||||
|
||||
describe("nodes camera_snap", () => {
|
||||
it("uses front/high-quality defaults when params are omitted", async () => {
|
||||
callGateway.mockImplementation(async ({ method, params }) => {
|
||||
if (method === "node.list") {
|
||||
return mockNodeList();
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
expect(params).toMatchObject({
|
||||
setupNodeInvokeMock({
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
command: "camera.snap",
|
||||
params: {
|
||||
facing: "front",
|
||||
@@ -57,16 +140,8 @@ describe("nodes camera_snap", () => {
|
||||
quality: 0.95,
|
||||
},
|
||||
});
|
||||
return {
|
||||
payload: {
|
||||
format: "jpg",
|
||||
base64: "aGVsbG8=",
|
||||
width: 1,
|
||||
height: 1,
|
||||
},
|
||||
};
|
||||
}
|
||||
return unexpectedGatewayMethod(method);
|
||||
return { payload: JPG_PAYLOAD };
|
||||
},
|
||||
});
|
||||
|
||||
const result = await executeNodes({
|
||||
@@ -74,26 +149,12 @@ describe("nodes camera_snap", () => {
|
||||
node: NODE_ID,
|
||||
});
|
||||
|
||||
const images = (result.content ?? []).filter((block) => block.type === "image");
|
||||
expect(images).toHaveLength(1);
|
||||
expectSingleImage(result);
|
||||
});
|
||||
|
||||
it("maps jpg payloads to image/jpeg", async () => {
|
||||
callGateway.mockImplementation(async ({ method }) => {
|
||||
if (method === "node.list") {
|
||||
return mockNodeList();
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
return {
|
||||
payload: {
|
||||
format: "jpg",
|
||||
base64: "aGVsbG8=",
|
||||
width: 1,
|
||||
height: 1,
|
||||
},
|
||||
};
|
||||
}
|
||||
return unexpectedGatewayMethod(method);
|
||||
setupNodeInvokeMock({
|
||||
invokePayload: JPG_PAYLOAD,
|
||||
});
|
||||
|
||||
const result = await executeNodes({
|
||||
@@ -102,31 +163,18 @@ describe("nodes camera_snap", () => {
|
||||
facing: "front",
|
||||
});
|
||||
|
||||
const images = (result.content ?? []).filter((block) => block.type === "image");
|
||||
expect(images).toHaveLength(1);
|
||||
expect(images[0]?.mimeType).toBe("image/jpeg");
|
||||
expectSingleImage(result, { mimeType: "image/jpeg" });
|
||||
});
|
||||
|
||||
it("passes deviceId when provided", async () => {
|
||||
callGateway.mockImplementation(async ({ method, params }) => {
|
||||
if (method === "node.list") {
|
||||
return mockNodeList();
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
expect(params).toMatchObject({
|
||||
setupNodeInvokeMock({
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
command: "camera.snap",
|
||||
params: { deviceId: "cam-123" },
|
||||
});
|
||||
return {
|
||||
payload: {
|
||||
format: "jpg",
|
||||
base64: "aGVsbG8=",
|
||||
width: 1,
|
||||
height: 1,
|
||||
},
|
||||
};
|
||||
}
|
||||
return unexpectedGatewayMethod(method);
|
||||
return { payload: JPG_PAYLOAD };
|
||||
},
|
||||
});
|
||||
|
||||
await executeNodes({
|
||||
@@ -151,12 +199,10 @@ describe("nodes camera_snap", () => {
|
||||
|
||||
describe("nodes notifications_list", () => {
|
||||
it("invokes notifications.list and returns payload", async () => {
|
||||
callGateway.mockImplementation(async ({ method, params }) => {
|
||||
if (method === "node.list") {
|
||||
return mockNodeList(["notifications.list"]);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
expect(params).toMatchObject({
|
||||
setupNodeInvokeMock({
|
||||
commands: ["notifications.list"],
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
nodeId: NODE_ID,
|
||||
command: "notifications.list",
|
||||
params: {},
|
||||
@@ -169,8 +215,7 @@ describe("nodes notifications_list", () => {
|
||||
notifications: [{ key: "n1", packageName: "com.example.app" }],
|
||||
},
|
||||
};
|
||||
}
|
||||
return unexpectedGatewayMethod(method);
|
||||
},
|
||||
});
|
||||
|
||||
const result = await executeNodes({
|
||||
@@ -178,21 +223,16 @@ describe("nodes notifications_list", () => {
|
||||
node: NODE_ID,
|
||||
});
|
||||
|
||||
expect(result.content?.[0]).toMatchObject({
|
||||
type: "text",
|
||||
text: expect.stringContaining('"notifications"'),
|
||||
});
|
||||
expectFirstTextContains(result, '"notifications"');
|
||||
});
|
||||
});
|
||||
|
||||
describe("nodes notifications_action", () => {
|
||||
it("invokes notifications.actions dismiss", async () => {
|
||||
callGateway.mockImplementation(async ({ method, params }) => {
|
||||
if (method === "node.list") {
|
||||
return mockNodeList(["notifications.actions"]);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
expect(params).toMatchObject({
|
||||
setupNodeInvokeMock({
|
||||
commands: ["notifications.actions"],
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
nodeId: NODE_ID,
|
||||
command: "notifications.actions",
|
||||
params: {
|
||||
@@ -201,8 +241,7 @@ describe("nodes notifications_action", () => {
|
||||
},
|
||||
});
|
||||
return { payload: { ok: true, key: "n1", action: "dismiss" } };
|
||||
}
|
||||
return unexpectedGatewayMethod(method);
|
||||
},
|
||||
});
|
||||
|
||||
const result = await executeNodes({
|
||||
@@ -212,21 +251,16 @@ describe("nodes notifications_action", () => {
|
||||
notificationAction: "dismiss",
|
||||
});
|
||||
|
||||
expect(result.content?.[0]).toMatchObject({
|
||||
type: "text",
|
||||
text: expect.stringContaining('"dismiss"'),
|
||||
});
|
||||
expectFirstTextContains(result, '"dismiss"');
|
||||
});
|
||||
});
|
||||
|
||||
describe("nodes device_status and device_info", () => {
|
||||
it("invokes device.status and returns payload", async () => {
|
||||
callGateway.mockImplementation(async ({ method, params }) => {
|
||||
if (method === "node.list") {
|
||||
return mockNodeList(["device.status", "device.info"]);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
expect(params).toMatchObject({
|
||||
setupNodeInvokeMock({
|
||||
commands: ["device.status", "device.info"],
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
nodeId: NODE_ID,
|
||||
command: "device.status",
|
||||
params: {},
|
||||
@@ -236,8 +270,7 @@ describe("nodes device_status and device_info", () => {
|
||||
battery: { state: "charging", lowPowerModeEnabled: false },
|
||||
},
|
||||
};
|
||||
}
|
||||
return unexpectedGatewayMethod(method);
|
||||
},
|
||||
});
|
||||
|
||||
const result = await executeNodes({
|
||||
@@ -245,19 +278,14 @@ describe("nodes device_status and device_info", () => {
|
||||
node: NODE_ID,
|
||||
});
|
||||
|
||||
expect(result.content?.[0]).toMatchObject({
|
||||
type: "text",
|
||||
text: expect.stringContaining('"battery"'),
|
||||
});
|
||||
expectFirstTextContains(result, '"battery"');
|
||||
});
|
||||
|
||||
it("invokes device.info and returns payload", async () => {
|
||||
callGateway.mockImplementation(async ({ method, params }) => {
|
||||
if (method === "node.list") {
|
||||
return mockNodeList(["device.status", "device.info"]);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
expect(params).toMatchObject({
|
||||
setupNodeInvokeMock({
|
||||
commands: ["device.status", "device.info"],
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
nodeId: NODE_ID,
|
||||
command: "device.info",
|
||||
params: {},
|
||||
@@ -268,8 +296,7 @@ describe("nodes device_status and device_info", () => {
|
||||
appVersion: "1.0.0",
|
||||
},
|
||||
};
|
||||
}
|
||||
return unexpectedGatewayMethod(method);
|
||||
},
|
||||
});
|
||||
|
||||
const result = await executeNodes({
|
||||
@@ -277,19 +304,14 @@ describe("nodes device_status and device_info", () => {
|
||||
node: NODE_ID,
|
||||
});
|
||||
|
||||
expect(result.content?.[0]).toMatchObject({
|
||||
type: "text",
|
||||
text: expect.stringContaining('"systemName"'),
|
||||
});
|
||||
expectFirstTextContains(result, '"systemName"');
|
||||
});
|
||||
|
||||
it("invokes device.permissions and returns payload", async () => {
|
||||
callGateway.mockImplementation(async ({ method, params }) => {
|
||||
if (method === "node.list") {
|
||||
return mockNodeList(["device.permissions"]);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
expect(params).toMatchObject({
|
||||
setupNodeInvokeMock({
|
||||
commands: ["device.permissions"],
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
nodeId: NODE_ID,
|
||||
command: "device.permissions",
|
||||
params: {},
|
||||
@@ -301,8 +323,7 @@ describe("nodes device_status and device_info", () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
return unexpectedGatewayMethod(method);
|
||||
},
|
||||
});
|
||||
|
||||
const result = await executeNodes({
|
||||
@@ -310,19 +331,14 @@ describe("nodes device_status and device_info", () => {
|
||||
node: NODE_ID,
|
||||
});
|
||||
|
||||
expect(result.content?.[0]).toMatchObject({
|
||||
type: "text",
|
||||
text: expect.stringContaining('"permissions"'),
|
||||
});
|
||||
expectFirstTextContains(result, '"permissions"');
|
||||
});
|
||||
|
||||
it("invokes device.health and returns payload", async () => {
|
||||
callGateway.mockImplementation(async ({ method, params }) => {
|
||||
if (method === "node.list") {
|
||||
return mockNodeList(["device.health"]);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
expect(params).toMatchObject({
|
||||
setupNodeInvokeMock({
|
||||
commands: ["device.health"],
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
nodeId: NODE_ID,
|
||||
command: "device.health",
|
||||
params: {},
|
||||
@@ -333,8 +349,7 @@ describe("nodes device_status and device_info", () => {
|
||||
battery: { chargingType: "usb" },
|
||||
},
|
||||
};
|
||||
}
|
||||
return unexpectedGatewayMethod(method);
|
||||
},
|
||||
});
|
||||
|
||||
const result = await executeNodes({
|
||||
@@ -342,36 +357,16 @@ describe("nodes device_status and device_info", () => {
|
||||
node: NODE_ID,
|
||||
});
|
||||
|
||||
expect(result.content?.[0]).toMatchObject({
|
||||
type: "text",
|
||||
text: expect.stringContaining('"memory"'),
|
||||
});
|
||||
expectFirstTextContains(result, '"memory"');
|
||||
});
|
||||
});
|
||||
|
||||
describe("nodes run", () => {
|
||||
it("passes invoke and command timeouts", async () => {
|
||||
callGateway.mockImplementation(async ({ method, params }) => {
|
||||
if (method === "node.list") {
|
||||
return mockNodeList(["system.run"]);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
const command = (params as { command?: string } | undefined)?.command;
|
||||
if (command === "system.run.prepare") {
|
||||
return {
|
||||
payload: {
|
||||
cmdText: "echo hi",
|
||||
plan: {
|
||||
argv: ["echo", "hi"],
|
||||
cwd: "/tmp",
|
||||
rawCommand: "echo hi",
|
||||
agentId: null,
|
||||
sessionKey: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
expect(params).toMatchObject({
|
||||
setupSystemRunGateway({
|
||||
prepareCwd: "/tmp",
|
||||
onRunInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
nodeId: NODE_ID,
|
||||
command: "system.run",
|
||||
timeoutMs: 45_000,
|
||||
@@ -385,8 +380,7 @@ describe("nodes run", () => {
|
||||
return {
|
||||
payload: { stdout: "", stderr: "", exitCode: 0, success: true },
|
||||
};
|
||||
}
|
||||
return unexpectedGatewayMethod(method);
|
||||
},
|
||||
});
|
||||
|
||||
await executeNodes({
|
||||
@@ -401,31 +395,13 @@ describe("nodes run", () => {
|
||||
it("requests approval and retries with allow-once decision", async () => {
|
||||
let invokeCalls = 0;
|
||||
let approvalId: string | null = null;
|
||||
callGateway.mockImplementation(async ({ method, params }) => {
|
||||
if (method === "node.list") {
|
||||
return mockNodeList(["system.run"]);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
const command = (params as { command?: string } | undefined)?.command;
|
||||
if (command === "system.run.prepare") {
|
||||
return {
|
||||
payload: {
|
||||
cmdText: "echo hi",
|
||||
plan: {
|
||||
argv: ["echo", "hi"],
|
||||
cwd: null,
|
||||
rawCommand: "echo hi",
|
||||
agentId: null,
|
||||
sessionKey: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
setupSystemRunGateway({
|
||||
onRunInvoke: (invokeParams) => {
|
||||
invokeCalls += 1;
|
||||
if (invokeCalls === 1) {
|
||||
throw new Error("SYSTEM_RUN_DENIED: approval required");
|
||||
}
|
||||
expect(params).toMatchObject({
|
||||
expect(invokeParams).toMatchObject({
|
||||
nodeId: NODE_ID,
|
||||
command: "system.run",
|
||||
params: {
|
||||
@@ -436,9 +412,9 @@ describe("nodes run", () => {
|
||||
},
|
||||
});
|
||||
return { payload: { stdout: "", stderr: "", exitCode: 0, success: true } };
|
||||
}
|
||||
if (method === "exec.approval.request") {
|
||||
expect(params).toMatchObject({
|
||||
},
|
||||
onApprovalRequest: (approvalParams) => {
|
||||
expect(approvalParams).toMatchObject({
|
||||
id: expect.any(String),
|
||||
command: "echo hi",
|
||||
commandArgv: ["echo", "hi"],
|
||||
@@ -450,12 +426,11 @@ describe("nodes run", () => {
|
||||
timeoutMs: 120_000,
|
||||
});
|
||||
approvalId =
|
||||
typeof (params as { id?: unknown } | undefined)?.id === "string"
|
||||
? ((params as { id: string }).id ?? null)
|
||||
typeof (approvalParams as { id?: unknown } | undefined)?.id === "string"
|
||||
? ((approvalParams as { id: string }).id ?? null)
|
||||
: null;
|
||||
return { decision: "allow-once" };
|
||||
}
|
||||
return unexpectedGatewayMethod(method);
|
||||
},
|
||||
});
|
||||
|
||||
await executeNodes(BASE_RUN_INPUT);
|
||||
@@ -463,93 +438,36 @@ describe("nodes run", () => {
|
||||
});
|
||||
|
||||
it("fails with user denied when approval decision is deny", async () => {
|
||||
callGateway.mockImplementation(async ({ method, params }) => {
|
||||
if (method === "node.list") {
|
||||
return mockNodeList(["system.run"]);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
const command = (params as { command?: string } | undefined)?.command;
|
||||
if (command === "system.run.prepare") {
|
||||
return {
|
||||
payload: {
|
||||
cmdText: "echo hi",
|
||||
plan: {
|
||||
argv: ["echo", "hi"],
|
||||
cwd: null,
|
||||
rawCommand: "echo hi",
|
||||
agentId: null,
|
||||
sessionKey: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
setupSystemRunGateway({
|
||||
onRunInvoke: () => {
|
||||
throw new Error("SYSTEM_RUN_DENIED: approval required");
|
||||
}
|
||||
if (method === "exec.approval.request") {
|
||||
},
|
||||
onApprovalRequest: () => {
|
||||
return { decision: "deny" };
|
||||
}
|
||||
return unexpectedGatewayMethod(method);
|
||||
},
|
||||
});
|
||||
|
||||
await expect(executeNodes(BASE_RUN_INPUT)).rejects.toThrow("exec denied: user denied");
|
||||
});
|
||||
|
||||
it("fails closed for timeout and invalid approval decisions", async () => {
|
||||
callGateway.mockImplementation(async ({ method, params }) => {
|
||||
if (method === "node.list") {
|
||||
return mockNodeList(["system.run"]);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
const command = (params as { command?: string } | undefined)?.command;
|
||||
if (command === "system.run.prepare") {
|
||||
return {
|
||||
payload: {
|
||||
cmdText: "echo hi",
|
||||
plan: {
|
||||
argv: ["echo", "hi"],
|
||||
cwd: null,
|
||||
rawCommand: "echo hi",
|
||||
agentId: null,
|
||||
sessionKey: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
setupSystemRunGateway({
|
||||
onRunInvoke: () => {
|
||||
throw new Error("SYSTEM_RUN_DENIED: approval required");
|
||||
}
|
||||
if (method === "exec.approval.request") {
|
||||
},
|
||||
onApprovalRequest: () => {
|
||||
return {};
|
||||
}
|
||||
return unexpectedGatewayMethod(method);
|
||||
},
|
||||
});
|
||||
await expect(executeNodes(BASE_RUN_INPUT)).rejects.toThrow("exec denied: approval timed out");
|
||||
|
||||
callGateway.mockImplementation(async ({ method, params }) => {
|
||||
if (method === "node.list") {
|
||||
return mockNodeList(["system.run"]);
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
const command = (params as { command?: string } | undefined)?.command;
|
||||
if (command === "system.run.prepare") {
|
||||
return {
|
||||
payload: {
|
||||
cmdText: "echo hi",
|
||||
plan: {
|
||||
argv: ["echo", "hi"],
|
||||
cwd: null,
|
||||
rawCommand: "echo hi",
|
||||
agentId: null,
|
||||
sessionKey: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
setupSystemRunGateway({
|
||||
onRunInvoke: () => {
|
||||
throw new Error("SYSTEM_RUN_DENIED: approval required");
|
||||
}
|
||||
if (method === "exec.approval.request") {
|
||||
},
|
||||
onApprovalRequest: () => {
|
||||
return { decision: "allow-never" };
|
||||
}
|
||||
return unexpectedGatewayMethod(method);
|
||||
},
|
||||
});
|
||||
await expect(executeNodes(BASE_RUN_INPUT)).rejects.toThrow(
|
||||
"exec denied: invalid approval decision",
|
||||
|
||||
Reference in New Issue
Block a user