test: fix exec approval and pty fallback e2e flows

This commit is contained in:
Peter Steinberger
2026-02-13 19:19:15 +00:00
parent 34eb14d24f
commit 7d1be585de
2 changed files with 16 additions and 24 deletions

View File

@@ -44,23 +44,14 @@ describe("exec approvals", () => {
it("reuses approval id as the node runId", async () => { it("reuses approval id as the node runId", async () => {
const { callGatewayTool } = await import("./tools/gateway.js"); const { callGatewayTool } = await import("./tools/gateway.js");
let invokeParams: unknown; let invokeParams: unknown;
let resolveInvoke: (() => void) | undefined;
const invokeSeen = new Promise<void>((resolve) => {
resolveInvoke = resolve;
});
vi.mocked(callGatewayTool).mockImplementation(async (method, _opts, params) => { vi.mocked(callGatewayTool).mockImplementation(async (method, _opts, params) => {
if (method === "exec.approval.request") { if (method === "exec.approval.request") {
// Return registration confirmation (status: "accepted") // Approval request now carries the decision directly.
return { status: "accepted", id: (params as { id?: string })?.id };
}
if (method === "exec.approval.waitDecision") {
// Return the decision when waitDecision is called
return { decision: "allow-once" }; return { decision: "allow-once" };
} }
if (method === "node.invoke") { if (method === "node.invoke") {
invokeParams = params; invokeParams = params;
resolveInvoke?.();
return { ok: true }; return { ok: true };
} }
return { ok: true }; return { ok: true };
@@ -77,10 +68,12 @@ describe("exec approvals", () => {
expect(result.details.status).toBe("approval-pending"); expect(result.details.status).toBe("approval-pending");
const approvalId = (result.details as { approvalId: string }).approvalId; const approvalId = (result.details as { approvalId: string }).approvalId;
await invokeSeen; await expect
.poll(() => (invokeParams as { params?: { runId?: string } } | undefined)?.params?.runId, {
const runId = (invokeParams as { params?: { runId?: string } } | undefined)?.params?.runId; timeout: 2000,
expect(runId).toBe(approvalId); interval: 20,
})
.toBe(approvalId);
}); });
it("skips approval when node allowlist is satisfied", async () => { it("skips approval when node allowlist is satisfied", async () => {

View File

@@ -1,22 +1,21 @@
import { afterEach, expect, test, vi } from "vitest"; import { afterEach, expect, test, vi } from "vitest";
import { resetProcessRegistryForTests } from "./bash-process-registry"; import { resetProcessRegistryForTests } from "./bash-process-registry";
import { createExecTool, setPtyModuleLoaderForTests } from "./bash-tools.exec"; import { createExecTool } from "./bash-tools.exec";
vi.mock("@lydell/node-pty", () => ({
spawn: () => {
const err = new Error("spawn EBADF");
(err as NodeJS.ErrnoException).code = "EBADF";
throw err;
},
}));
afterEach(() => { afterEach(() => {
resetProcessRegistryForTests(); resetProcessRegistryForTests();
setPtyModuleLoaderForTests();
vi.clearAllMocks(); vi.clearAllMocks();
}); });
test("exec falls back when PTY spawn fails", async () => { test("exec falls back when PTY spawn fails", async () => {
setPtyModuleLoaderForTests(async () => ({
spawn: () => {
const err = new Error("spawn EBADF");
(err as NodeJS.ErrnoException).code = "EBADF";
throw err;
},
}));
const tool = createExecTool({ allowBackground: false }); const tool = createExecTool({ allowBackground: false });
const result = await tool.execute("toolcall", { const result = await tool.execute("toolcall", {
command: "printf ok", command: "printf ok",