mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-10 11:23:31 +02:00
151 lines
4.6 KiB
TypeScript
151 lines
4.6 KiB
TypeScript
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
sendExecApprovalFollowup: vi.fn(),
|
|
logWarn: vi.fn(),
|
|
resolveExecApprovals: vi.fn(() => ({
|
|
defaults: {
|
|
security: "allowlist",
|
|
ask: "off",
|
|
askFallback: "deny",
|
|
autoAllowSkills: false,
|
|
},
|
|
agent: {
|
|
security: "allowlist",
|
|
ask: "off",
|
|
askFallback: "deny",
|
|
autoAllowSkills: false,
|
|
},
|
|
allowlist: [],
|
|
file: { version: 1, agents: {} },
|
|
})),
|
|
}));
|
|
|
|
vi.mock("./bash-tools.exec-approval-followup.js", () => ({
|
|
sendExecApprovalFollowup: mocks.sendExecApprovalFollowup,
|
|
}));
|
|
|
|
vi.mock("../logger.js", () => ({
|
|
logWarn: mocks.logWarn,
|
|
}));
|
|
|
|
vi.mock("../infra/exec-approvals.js", async (importOriginal) => {
|
|
const mod = await importOriginal<typeof import("../infra/exec-approvals.js")>();
|
|
return {
|
|
...mod,
|
|
resolveExecApprovals: mocks.resolveExecApprovals,
|
|
};
|
|
});
|
|
|
|
let sendExecApprovalFollowupResult: typeof import("./bash-tools.exec-host-shared.js").sendExecApprovalFollowupResult;
|
|
let maxExecApprovalFollowupFailureLogKeys: typeof import("./bash-tools.exec-host-shared.js").MAX_EXEC_APPROVAL_FOLLOWUP_FAILURE_LOG_KEYS;
|
|
let resolveExecHostApprovalContext: typeof import("./bash-tools.exec-host-shared.js").resolveExecHostApprovalContext;
|
|
let sendExecApprovalFollowup: typeof import("./bash-tools.exec-approval-followup.js").sendExecApprovalFollowup;
|
|
let logWarn: typeof import("../logger.js").logWarn;
|
|
|
|
beforeAll(async () => {
|
|
({
|
|
sendExecApprovalFollowupResult,
|
|
MAX_EXEC_APPROVAL_FOLLOWUP_FAILURE_LOG_KEYS: maxExecApprovalFollowupFailureLogKeys,
|
|
resolveExecHostApprovalContext,
|
|
} = await import("./bash-tools.exec-host-shared.js"));
|
|
({ sendExecApprovalFollowup } = await import("./bash-tools.exec-approval-followup.js"));
|
|
({ logWarn } = await import("../logger.js"));
|
|
});
|
|
|
|
describe("sendExecApprovalFollowupResult", () => {
|
|
beforeEach(() => {
|
|
vi.mocked(sendExecApprovalFollowup).mockReset();
|
|
vi.mocked(logWarn).mockReset();
|
|
mocks.resolveExecApprovals.mockReset();
|
|
mocks.resolveExecApprovals.mockReturnValue({
|
|
defaults: {
|
|
security: "allowlist",
|
|
ask: "off",
|
|
askFallback: "deny",
|
|
autoAllowSkills: false,
|
|
},
|
|
agent: {
|
|
security: "allowlist",
|
|
ask: "off",
|
|
askFallback: "deny",
|
|
autoAllowSkills: false,
|
|
},
|
|
allowlist: [],
|
|
file: { version: 1, agents: {} },
|
|
});
|
|
});
|
|
|
|
it("logs repeated followup dispatch failures once per approval id and error message", async () => {
|
|
vi.mocked(sendExecApprovalFollowup).mockRejectedValue(new Error("Channel is required"));
|
|
|
|
const target = {
|
|
approvalId: "approval-log-once",
|
|
sessionKey: "agent:main:main",
|
|
};
|
|
await sendExecApprovalFollowupResult(target, "Exec finished");
|
|
await sendExecApprovalFollowupResult(target, "Exec finished");
|
|
|
|
expect(logWarn).toHaveBeenCalledTimes(1);
|
|
expect(logWarn).toHaveBeenCalledWith(
|
|
"exec approval followup dispatch failed (id=approval-log-once): Channel is required",
|
|
);
|
|
});
|
|
|
|
it("evicts oldest followup failure dedupe keys after reaching the cap", async () => {
|
|
vi.mocked(sendExecApprovalFollowup).mockRejectedValue(new Error("Channel is required"));
|
|
|
|
for (let i = 0; i <= maxExecApprovalFollowupFailureLogKeys; i += 1) {
|
|
await sendExecApprovalFollowupResult(
|
|
{
|
|
approvalId: `approval-${i}`,
|
|
sessionKey: "agent:main:main",
|
|
},
|
|
"Exec finished",
|
|
);
|
|
}
|
|
await sendExecApprovalFollowupResult(
|
|
{
|
|
approvalId: "approval-0",
|
|
sessionKey: "agent:main:main",
|
|
},
|
|
"Exec finished",
|
|
);
|
|
|
|
expect(logWarn).toHaveBeenCalledTimes(maxExecApprovalFollowupFailureLogKeys + 2);
|
|
expect(logWarn).toHaveBeenLastCalledWith(
|
|
"exec approval followup dispatch failed (id=approval-0): Channel is required",
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("resolveExecHostApprovalContext", () => {
|
|
it("uses exec-approvals.json agent security even when it is broader than the tool default", () => {
|
|
mocks.resolveExecApprovals.mockReturnValue({
|
|
defaults: {
|
|
security: "allowlist",
|
|
ask: "off",
|
|
askFallback: "deny",
|
|
autoAllowSkills: false,
|
|
},
|
|
agent: {
|
|
security: "full",
|
|
ask: "off",
|
|
askFallback: "deny",
|
|
autoAllowSkills: false,
|
|
},
|
|
allowlist: [],
|
|
file: { version: 1, agents: {} },
|
|
});
|
|
|
|
const result = resolveExecHostApprovalContext({
|
|
agentId: "agent-main",
|
|
security: "allowlist",
|
|
ask: "off",
|
|
host: "gateway",
|
|
});
|
|
|
|
expect(result.hostSecurity).toBe("full");
|
|
});
|
|
});
|