Files
openclaw/src/telegram/exec-approvals-handler.test.ts
Harold Hunt de49a8b72c Telegram: exec approvals for OpenCode/Codex (#37233)
Merged via squash.

Prepared head SHA: f243379094
Co-authored-by: huntharo <5617868+huntharo@users.noreply.github.com>
Co-authored-by: huntharo <5617868+huntharo@users.noreply.github.com>
Reviewed-by: @huntharo
2026-03-09 23:04:35 -04:00

157 lines
4.2 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import { TelegramExecApprovalHandler } from "./exec-approvals-handler.js";
const baseRequest = {
id: "9f1c7d5d-b1fb-46ef-ac45-662723b65bb7",
request: {
command: "npm view diver name version description",
agentId: "main",
sessionKey: "agent:main:telegram:group:-1003841603622:topic:928",
turnSourceChannel: "telegram",
turnSourceTo: "-1003841603622",
turnSourceThreadId: "928",
turnSourceAccountId: "default",
},
createdAtMs: 1000,
expiresAtMs: 61_000,
};
function createHandler(cfg: OpenClawConfig) {
const sendTyping = vi.fn().mockResolvedValue({ ok: true });
const sendMessage = vi
.fn()
.mockResolvedValueOnce({ messageId: "m1", chatId: "-1003841603622" })
.mockResolvedValue({ messageId: "m2", chatId: "8460800771" });
const editReplyMarkup = vi.fn().mockResolvedValue({ ok: true });
const handler = new TelegramExecApprovalHandler(
{
token: "tg-token",
accountId: "default",
cfg,
},
{
nowMs: () => 1000,
sendTyping,
sendMessage,
editReplyMarkup,
},
);
return { handler, sendTyping, sendMessage, editReplyMarkup };
}
describe("TelegramExecApprovalHandler", () => {
it("sends approval prompts to the originating telegram topic when target=channel", async () => {
const cfg = {
channels: {
telegram: {
execApprovals: {
enabled: true,
approvers: ["8460800771"],
target: "channel",
},
},
},
} as OpenClawConfig;
const { handler, sendTyping, sendMessage } = createHandler(cfg);
await handler.handleRequested(baseRequest);
expect(sendTyping).toHaveBeenCalledWith(
"-1003841603622",
expect.objectContaining({
accountId: "default",
messageThreadId: 928,
}),
);
expect(sendMessage).toHaveBeenCalledWith(
"-1003841603622",
expect.stringContaining("/approve 9f1c7d5d-b1fb-46ef-ac45-662723b65bb7 allow-once"),
expect.objectContaining({
accountId: "default",
messageThreadId: 928,
buttons: [
[
{
text: "Allow Once",
callback_data: "/approve 9f1c7d5d-b1fb-46ef-ac45-662723b65bb7 allow-once",
},
{
text: "Allow Always",
callback_data: "/approve 9f1c7d5d-b1fb-46ef-ac45-662723b65bb7 allow-always",
},
],
[
{
text: "Deny",
callback_data: "/approve 9f1c7d5d-b1fb-46ef-ac45-662723b65bb7 deny",
},
],
],
}),
);
});
it("falls back to approver DMs when channel routing is unavailable", async () => {
const cfg = {
channels: {
telegram: {
execApprovals: {
enabled: true,
approvers: ["111", "222"],
target: "channel",
},
},
},
} as OpenClawConfig;
const { handler, sendMessage } = createHandler(cfg);
await handler.handleRequested({
...baseRequest,
request: {
...baseRequest.request,
turnSourceChannel: "slack",
turnSourceTo: "U1",
turnSourceAccountId: null,
turnSourceThreadId: null,
},
});
expect(sendMessage).toHaveBeenCalledTimes(2);
expect(sendMessage.mock.calls.map((call) => call[0])).toEqual(["111", "222"]);
});
it("clears buttons from tracked approval messages when resolved", async () => {
const cfg = {
channels: {
telegram: {
execApprovals: {
enabled: true,
approvers: ["8460800771"],
target: "both",
},
},
},
} as OpenClawConfig;
const { handler, editReplyMarkup } = createHandler(cfg);
await handler.handleRequested(baseRequest);
await handler.handleResolved({
id: baseRequest.id,
decision: "allow-once",
resolvedBy: "telegram:8460800771",
ts: 2000,
});
expect(editReplyMarkup).toHaveBeenCalled();
expect(editReplyMarkup).toHaveBeenCalledWith(
"-1003841603622",
"m1",
[],
expect.objectContaining({
accountId: "default",
}),
);
});
});