140 lines
4.1 KiB
TypeScript
140 lines
4.1 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const gatewayMocks = vi.hoisted(() => ({
|
|
callGatewayTool: vi.fn(),
|
|
readGatewayCallOptions: vi.fn(() => ({})),
|
|
}));
|
|
|
|
const nodeUtilsMocks = vi.hoisted(() => ({
|
|
resolveNodeId: vi.fn(async () => "node-1"),
|
|
listNodes: vi.fn(async () => [] as Array<{ nodeId: string; commands?: string[] }>),
|
|
resolveNodeIdFromList: vi.fn(() => "node-1"),
|
|
}));
|
|
|
|
const screenMocks = vi.hoisted(() => ({
|
|
parseScreenRecordPayload: vi.fn(() => ({
|
|
base64: "ZmFrZQ==",
|
|
format: "mp4",
|
|
durationMs: 300_000,
|
|
fps: 10,
|
|
screenIndex: 0,
|
|
hasAudio: true,
|
|
})),
|
|
screenRecordTempPath: vi.fn(() => "/tmp/screen-record.mp4"),
|
|
writeScreenRecordToFile: vi.fn(async () => ({ path: "/tmp/screen-record.mp4" })),
|
|
}));
|
|
|
|
vi.mock("./gateway.js", () => ({
|
|
callGatewayTool: gatewayMocks.callGatewayTool,
|
|
readGatewayCallOptions: gatewayMocks.readGatewayCallOptions,
|
|
}));
|
|
|
|
vi.mock("./nodes-utils.js", () => ({
|
|
resolveNodeId: nodeUtilsMocks.resolveNodeId,
|
|
listNodes: nodeUtilsMocks.listNodes,
|
|
resolveNodeIdFromList: nodeUtilsMocks.resolveNodeIdFromList,
|
|
}));
|
|
|
|
vi.mock("../../cli/nodes-screen.js", () => ({
|
|
parseScreenRecordPayload: screenMocks.parseScreenRecordPayload,
|
|
screenRecordTempPath: screenMocks.screenRecordTempPath,
|
|
writeScreenRecordToFile: screenMocks.writeScreenRecordToFile,
|
|
}));
|
|
|
|
import { createNodesTool } from "./nodes-tool.js";
|
|
|
|
describe("createNodesTool screen_record duration guardrails", () => {
|
|
beforeEach(() => {
|
|
gatewayMocks.callGatewayTool.mockReset();
|
|
gatewayMocks.readGatewayCallOptions.mockReset();
|
|
gatewayMocks.readGatewayCallOptions.mockReturnValue({});
|
|
nodeUtilsMocks.resolveNodeId.mockClear();
|
|
screenMocks.parseScreenRecordPayload.mockClear();
|
|
screenMocks.writeScreenRecordToFile.mockClear();
|
|
});
|
|
|
|
it("marks nodes as owner-only", () => {
|
|
const tool = createNodesTool();
|
|
expect(tool.ownerOnly).toBe(true);
|
|
});
|
|
|
|
it("caps durationMs schema at 300000", () => {
|
|
const tool = createNodesTool();
|
|
const schema = tool.parameters as {
|
|
properties?: {
|
|
durationMs?: {
|
|
maximum?: number;
|
|
};
|
|
};
|
|
};
|
|
expect(schema.properties?.durationMs?.maximum).toBe(300_000);
|
|
});
|
|
|
|
it("clamps screen_record durationMs argument to 300000 before gateway invoke", async () => {
|
|
gatewayMocks.callGatewayTool.mockResolvedValue({ payload: { ok: true } });
|
|
const tool = createNodesTool();
|
|
|
|
await tool.execute("call-1", {
|
|
action: "screen_record",
|
|
node: "macbook",
|
|
durationMs: 900_000,
|
|
});
|
|
|
|
expect(gatewayMocks.callGatewayTool).toHaveBeenCalledWith(
|
|
"node.invoke",
|
|
{},
|
|
expect.objectContaining({
|
|
params: expect.objectContaining({
|
|
durationMs: 300_000,
|
|
}),
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("omits rawCommand when preparing wrapped argv execution", async () => {
|
|
nodeUtilsMocks.listNodes.mockResolvedValue([
|
|
{
|
|
nodeId: "node-1",
|
|
commands: ["system.run"],
|
|
},
|
|
]);
|
|
gatewayMocks.callGatewayTool.mockImplementation(async (_method, _opts, payload) => {
|
|
if (payload?.command === "system.run.prepare") {
|
|
return {
|
|
payload: {
|
|
plan: {
|
|
argv: ["bash", "-lc", "echo hi"],
|
|
cwd: null,
|
|
commandText: 'bash -lc "echo hi"',
|
|
commandPreview: "echo hi",
|
|
agentId: null,
|
|
sessionKey: null,
|
|
},
|
|
},
|
|
};
|
|
}
|
|
if (payload?.command === "system.run") {
|
|
return { payload: { ok: true } };
|
|
}
|
|
throw new Error(`unexpected command: ${String(payload?.command)}`);
|
|
});
|
|
const tool = createNodesTool();
|
|
|
|
await tool.execute("call-1", {
|
|
action: "run",
|
|
node: "macbook",
|
|
command: ["bash", "-lc", "echo hi"],
|
|
});
|
|
|
|
const prepareCall = gatewayMocks.callGatewayTool.mock.calls.find(
|
|
(call) => call[2]?.command === "system.run.prepare",
|
|
)?.[2];
|
|
expect(prepareCall).toBeTruthy();
|
|
expect(prepareCall?.params).toMatchObject({
|
|
command: ["bash", "-lc", "echo hi"],
|
|
agentId: "main",
|
|
});
|
|
expect(prepareCall?.params).not.toHaveProperty("rawCommand");
|
|
});
|
|
});
|