mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 15:35:03 +00:00
test(actions): table-drive slack and telegram action cases
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { describe, expect, it, vi } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import type { OpenClawConfig } from "../../config/config.js";
|
import type { OpenClawConfig } from "../../config/config.js";
|
||||||
import { handleSlackAction } from "./slack-actions.js";
|
import { handleSlackAction } from "./slack-actions.js";
|
||||||
|
|
||||||
@@ -17,52 +17,59 @@ const sendSlackMessage = vi.fn(async (..._args: unknown[]) => ({}));
|
|||||||
const unpinSlackMessage = vi.fn(async (..._args: unknown[]) => ({}));
|
const unpinSlackMessage = vi.fn(async (..._args: unknown[]) => ({}));
|
||||||
|
|
||||||
vi.mock("../../slack/actions.js", () => ({
|
vi.mock("../../slack/actions.js", () => ({
|
||||||
deleteSlackMessage,
|
deleteSlackMessage: (...args: Parameters<typeof deleteSlackMessage>) =>
|
||||||
editSlackMessage,
|
deleteSlackMessage(...args),
|
||||||
getSlackMemberInfo,
|
editSlackMessage: (...args: Parameters<typeof editSlackMessage>) => editSlackMessage(...args),
|
||||||
listSlackEmojis,
|
getSlackMemberInfo: (...args: Parameters<typeof getSlackMemberInfo>) =>
|
||||||
listSlackPins,
|
getSlackMemberInfo(...args),
|
||||||
listSlackReactions,
|
listSlackEmojis: (...args: Parameters<typeof listSlackEmojis>) => listSlackEmojis(...args),
|
||||||
pinSlackMessage,
|
listSlackPins: (...args: Parameters<typeof listSlackPins>) => listSlackPins(...args),
|
||||||
reactSlackMessage,
|
listSlackReactions: (...args: Parameters<typeof listSlackReactions>) =>
|
||||||
readSlackMessages,
|
listSlackReactions(...args),
|
||||||
removeOwnSlackReactions,
|
pinSlackMessage: (...args: Parameters<typeof pinSlackMessage>) => pinSlackMessage(...args),
|
||||||
removeSlackReaction,
|
reactSlackMessage: (...args: Parameters<typeof reactSlackMessage>) => reactSlackMessage(...args),
|
||||||
sendSlackMessage,
|
readSlackMessages: (...args: Parameters<typeof readSlackMessages>) => readSlackMessages(...args),
|
||||||
unpinSlackMessage,
|
removeOwnSlackReactions: (...args: Parameters<typeof removeOwnSlackReactions>) =>
|
||||||
|
removeOwnSlackReactions(...args),
|
||||||
|
removeSlackReaction: (...args: Parameters<typeof removeSlackReaction>) =>
|
||||||
|
removeSlackReaction(...args),
|
||||||
|
sendSlackMessage: (...args: Parameters<typeof sendSlackMessage>) => sendSlackMessage(...args),
|
||||||
|
unpinSlackMessage: (...args: Parameters<typeof unpinSlackMessage>) => unpinSlackMessage(...args),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe("handleSlackAction", () => {
|
describe("handleSlackAction", () => {
|
||||||
it("adds reactions", async () => {
|
function slackConfig(overrides?: Record<string, unknown>): OpenClawConfig {
|
||||||
const cfg = { channels: { slack: { botToken: "tok" } } } as OpenClawConfig;
|
return {
|
||||||
await handleSlackAction(
|
channels: {
|
||||||
{
|
slack: {
|
||||||
action: "react",
|
botToken: "tok",
|
||||||
channelId: "C1",
|
...overrides,
|
||||||
messageId: "123.456",
|
},
|
||||||
emoji: "✅",
|
|
||||||
},
|
},
|
||||||
cfg,
|
} as OpenClawConfig;
|
||||||
);
|
}
|
||||||
expect(reactSlackMessage).toHaveBeenCalledWith("C1", "123.456", "✅");
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("strips channel: prefix for channelId params", async () => {
|
it.each([
|
||||||
const cfg = { channels: { slack: { botToken: "tok" } } } as OpenClawConfig;
|
{ name: "raw channel id", channelId: "C1" },
|
||||||
|
{ name: "channel: prefixed id", channelId: "channel:C1" },
|
||||||
|
])("adds reactions for $name", async ({ channelId }) => {
|
||||||
await handleSlackAction(
|
await handleSlackAction(
|
||||||
{
|
{
|
||||||
action: "react",
|
action: "react",
|
||||||
channelId: "channel:C1",
|
channelId,
|
||||||
messageId: "123.456",
|
messageId: "123.456",
|
||||||
emoji: "✅",
|
emoji: "✅",
|
||||||
},
|
},
|
||||||
cfg,
|
slackConfig(),
|
||||||
);
|
);
|
||||||
expect(reactSlackMessage).toHaveBeenCalledWith("C1", "123.456", "✅");
|
expect(reactSlackMessage).toHaveBeenCalledWith("C1", "123.456", "✅");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("removes reactions on empty emoji", async () => {
|
it("removes reactions on empty emoji", async () => {
|
||||||
const cfg = { channels: { slack: { botToken: "tok" } } } as OpenClawConfig;
|
|
||||||
await handleSlackAction(
|
await handleSlackAction(
|
||||||
{
|
{
|
||||||
action: "react",
|
action: "react",
|
||||||
@@ -70,13 +77,12 @@ describe("handleSlackAction", () => {
|
|||||||
messageId: "123.456",
|
messageId: "123.456",
|
||||||
emoji: "",
|
emoji: "",
|
||||||
},
|
},
|
||||||
cfg,
|
slackConfig(),
|
||||||
);
|
);
|
||||||
expect(removeOwnSlackReactions).toHaveBeenCalledWith("C1", "123.456");
|
expect(removeOwnSlackReactions).toHaveBeenCalledWith("C1", "123.456");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("removes reactions when remove flag set", async () => {
|
it("removes reactions when remove flag set", async () => {
|
||||||
const cfg = { channels: { slack: { botToken: "tok" } } } as OpenClawConfig;
|
|
||||||
await handleSlackAction(
|
await handleSlackAction(
|
||||||
{
|
{
|
||||||
action: "react",
|
action: "react",
|
||||||
@@ -85,13 +91,12 @@ describe("handleSlackAction", () => {
|
|||||||
emoji: "✅",
|
emoji: "✅",
|
||||||
remove: true,
|
remove: true,
|
||||||
},
|
},
|
||||||
cfg,
|
slackConfig(),
|
||||||
);
|
);
|
||||||
expect(removeSlackReaction).toHaveBeenCalledWith("C1", "123.456", "✅");
|
expect(removeSlackReaction).toHaveBeenCalledWith("C1", "123.456", "✅");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("rejects removes without emoji", async () => {
|
it("rejects removes without emoji", async () => {
|
||||||
const cfg = { channels: { slack: { botToken: "tok" } } } as OpenClawConfig;
|
|
||||||
await expect(
|
await expect(
|
||||||
handleSlackAction(
|
handleSlackAction(
|
||||||
{
|
{
|
||||||
@@ -101,15 +106,12 @@ describe("handleSlackAction", () => {
|
|||||||
emoji: "",
|
emoji: "",
|
||||||
remove: true,
|
remove: true,
|
||||||
},
|
},
|
||||||
cfg,
|
slackConfig(),
|
||||||
),
|
),
|
||||||
).rejects.toThrow(/Emoji is required/);
|
).rejects.toThrow(/Emoji is required/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("respects reaction gating", async () => {
|
it("respects reaction gating", async () => {
|
||||||
const cfg = {
|
|
||||||
channels: { slack: { botToken: "tok", actions: { reactions: false } } },
|
|
||||||
} as OpenClawConfig;
|
|
||||||
await expect(
|
await expect(
|
||||||
handleSlackAction(
|
handleSlackAction(
|
||||||
{
|
{
|
||||||
@@ -118,13 +120,12 @@ describe("handleSlackAction", () => {
|
|||||||
messageId: "123.456",
|
messageId: "123.456",
|
||||||
emoji: "✅",
|
emoji: "✅",
|
||||||
},
|
},
|
||||||
cfg,
|
slackConfig({ actions: { reactions: false } }),
|
||||||
),
|
),
|
||||||
).rejects.toThrow(/Slack reactions are disabled/);
|
).rejects.toThrow(/Slack reactions are disabled/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("passes threadTs to sendSlackMessage for thread replies", async () => {
|
it("passes threadTs to sendSlackMessage for thread replies", async () => {
|
||||||
const cfg = { channels: { slack: { botToken: "tok" } } } as OpenClawConfig;
|
|
||||||
await handleSlackAction(
|
await handleSlackAction(
|
||||||
{
|
{
|
||||||
action: "sendMessage",
|
action: "sendMessage",
|
||||||
@@ -132,7 +133,7 @@ describe("handleSlackAction", () => {
|
|||||||
content: "Hello thread",
|
content: "Hello thread",
|
||||||
threadTs: "1234567890.123456",
|
threadTs: "1234567890.123456",
|
||||||
},
|
},
|
||||||
cfg,
|
slackConfig(),
|
||||||
);
|
);
|
||||||
expect(sendSlackMessage).toHaveBeenCalledWith("channel:C123", "Hello thread", {
|
expect(sendSlackMessage).toHaveBeenCalledWith("channel:C123", "Hello thread", {
|
||||||
mediaUrl: undefined,
|
mediaUrl: undefined,
|
||||||
@@ -141,74 +142,56 @@ describe("handleSlackAction", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("accepts blocks JSON and allows empty content", async () => {
|
it.each([
|
||||||
const cfg = { channels: { slack: { botToken: "tok" } } } as OpenClawConfig;
|
{
|
||||||
sendSlackMessage.mockClear();
|
name: "JSON blocks",
|
||||||
await handleSlackAction(
|
blocks: JSON.stringify([
|
||||||
{
|
{ type: "section", text: { type: "mrkdwn", text: "*Deploy* status" } },
|
||||||
action: "sendMessage",
|
]),
|
||||||
to: "channel:C123",
|
expectedBlocks: [{ type: "section", text: { type: "mrkdwn", text: "*Deploy* status" } }],
|
||||||
blocks: JSON.stringify([
|
},
|
||||||
{ type: "section", text: { type: "mrkdwn", text: "*Deploy* status" } },
|
{
|
||||||
]),
|
name: "array blocks",
|
||||||
},
|
|
||||||
cfg,
|
|
||||||
);
|
|
||||||
expect(sendSlackMessage).toHaveBeenCalledWith("channel:C123", "", {
|
|
||||||
mediaUrl: undefined,
|
|
||||||
threadTs: undefined,
|
|
||||||
blocks: [{ type: "section", text: { type: "mrkdwn", text: "*Deploy* status" } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("accepts blocks arrays directly", async () => {
|
|
||||||
const cfg = { channels: { slack: { botToken: "tok" } } } as OpenClawConfig;
|
|
||||||
sendSlackMessage.mockClear();
|
|
||||||
await handleSlackAction(
|
|
||||||
{
|
|
||||||
action: "sendMessage",
|
|
||||||
to: "channel:C123",
|
|
||||||
blocks: [{ type: "divider" }],
|
|
||||||
},
|
|
||||||
cfg,
|
|
||||||
);
|
|
||||||
expect(sendSlackMessage).toHaveBeenCalledWith("channel:C123", "", {
|
|
||||||
mediaUrl: undefined,
|
|
||||||
threadTs: undefined,
|
|
||||||
blocks: [{ type: "divider" }],
|
blocks: [{ type: "divider" }],
|
||||||
|
expectedBlocks: [{ type: "divider" }],
|
||||||
|
},
|
||||||
|
])("accepts $name and allows empty content", async ({ blocks, expectedBlocks }) => {
|
||||||
|
await handleSlackAction(
|
||||||
|
{
|
||||||
|
action: "sendMessage",
|
||||||
|
to: "channel:C123",
|
||||||
|
blocks,
|
||||||
|
},
|
||||||
|
slackConfig(),
|
||||||
|
);
|
||||||
|
expect(sendSlackMessage).toHaveBeenCalledWith("channel:C123", "", {
|
||||||
|
mediaUrl: undefined,
|
||||||
|
threadTs: undefined,
|
||||||
|
blocks: expectedBlocks,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("rejects invalid blocks JSON", async () => {
|
it.each([
|
||||||
const cfg = { channels: { slack: { botToken: "tok" } } } as OpenClawConfig;
|
{
|
||||||
|
name: "invalid blocks JSON",
|
||||||
|
blocks: "{bad-json",
|
||||||
|
expectedError: /blocks must be valid JSON/i,
|
||||||
|
},
|
||||||
|
{ name: "empty blocks arrays", blocks: "[]", expectedError: /at least one block/i },
|
||||||
|
])("rejects $name", async ({ blocks, expectedError }) => {
|
||||||
await expect(
|
await expect(
|
||||||
handleSlackAction(
|
handleSlackAction(
|
||||||
{
|
{
|
||||||
action: "sendMessage",
|
action: "sendMessage",
|
||||||
to: "channel:C123",
|
to: "channel:C123",
|
||||||
blocks: "{bad-json",
|
blocks,
|
||||||
},
|
},
|
||||||
cfg,
|
slackConfig(),
|
||||||
),
|
),
|
||||||
).rejects.toThrow(/blocks must be valid JSON/i);
|
).rejects.toThrow(expectedError);
|
||||||
});
|
|
||||||
|
|
||||||
it("rejects empty blocks arrays", async () => {
|
|
||||||
const cfg = { channels: { slack: { botToken: "tok" } } } as OpenClawConfig;
|
|
||||||
await expect(
|
|
||||||
handleSlackAction(
|
|
||||||
{
|
|
||||||
action: "sendMessage",
|
|
||||||
to: "channel:C123",
|
|
||||||
blocks: "[]",
|
|
||||||
},
|
|
||||||
cfg,
|
|
||||||
),
|
|
||||||
).rejects.toThrow(/at least one block/i);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("requires at least one of content, blocks, or mediaUrl", async () => {
|
it("requires at least one of content, blocks, or mediaUrl", async () => {
|
||||||
const cfg = { channels: { slack: { botToken: "tok" } } } as OpenClawConfig;
|
|
||||||
await expect(
|
await expect(
|
||||||
handleSlackAction(
|
handleSlackAction(
|
||||||
{
|
{
|
||||||
@@ -216,13 +199,12 @@ describe("handleSlackAction", () => {
|
|||||||
to: "channel:C123",
|
to: "channel:C123",
|
||||||
content: "",
|
content: "",
|
||||||
},
|
},
|
||||||
cfg,
|
slackConfig(),
|
||||||
),
|
),
|
||||||
).rejects.toThrow(/requires content, blocks, or mediaUrl/i);
|
).rejects.toThrow(/requires content, blocks, or mediaUrl/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("rejects blocks combined with mediaUrl", async () => {
|
it("rejects blocks combined with mediaUrl", async () => {
|
||||||
const cfg = { channels: { slack: { botToken: "tok" } } } as OpenClawConfig;
|
|
||||||
await expect(
|
await expect(
|
||||||
handleSlackAction(
|
handleSlackAction(
|
||||||
{
|
{
|
||||||
@@ -231,47 +213,38 @@ describe("handleSlackAction", () => {
|
|||||||
blocks: [{ type: "divider" }],
|
blocks: [{ type: "divider" }],
|
||||||
mediaUrl: "https://example.com/image.png",
|
mediaUrl: "https://example.com/image.png",
|
||||||
},
|
},
|
||||||
cfg,
|
slackConfig(),
|
||||||
),
|
),
|
||||||
).rejects.toThrow(/does not support blocks with mediaUrl/i);
|
).rejects.toThrow(/does not support blocks with mediaUrl/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("passes blocks JSON to editSlackMessage with empty content", async () => {
|
it.each([
|
||||||
const cfg = { channels: { slack: { botToken: "tok" } } } as OpenClawConfig;
|
{
|
||||||
editSlackMessage.mockClear();
|
name: "JSON blocks",
|
||||||
await handleSlackAction(
|
blocks: JSON.stringify([{ type: "section", text: { type: "mrkdwn", text: "Updated" } }]),
|
||||||
{
|
expectedBlocks: [{ type: "section", text: { type: "mrkdwn", text: "Updated" } }],
|
||||||
action: "editMessage",
|
},
|
||||||
channelId: "C123",
|
{
|
||||||
messageId: "123.456",
|
name: "array blocks",
|
||||||
blocks: JSON.stringify([{ type: "section", text: { type: "mrkdwn", text: "Updated" } }]),
|
|
||||||
},
|
|
||||||
cfg,
|
|
||||||
);
|
|
||||||
expect(editSlackMessage).toHaveBeenCalledWith("C123", "123.456", "", {
|
|
||||||
blocks: [{ type: "section", text: { type: "mrkdwn", text: "Updated" } }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("passes blocks arrays to editSlackMessage", async () => {
|
|
||||||
const cfg = { channels: { slack: { botToken: "tok" } } } as OpenClawConfig;
|
|
||||||
editSlackMessage.mockClear();
|
|
||||||
await handleSlackAction(
|
|
||||||
{
|
|
||||||
action: "editMessage",
|
|
||||||
channelId: "C123",
|
|
||||||
messageId: "123.456",
|
|
||||||
blocks: [{ type: "divider" }],
|
|
||||||
},
|
|
||||||
cfg,
|
|
||||||
);
|
|
||||||
expect(editSlackMessage).toHaveBeenCalledWith("C123", "123.456", "", {
|
|
||||||
blocks: [{ type: "divider" }],
|
blocks: [{ type: "divider" }],
|
||||||
|
expectedBlocks: [{ type: "divider" }],
|
||||||
|
},
|
||||||
|
])("passes $name to editSlackMessage", async ({ blocks, expectedBlocks }) => {
|
||||||
|
await handleSlackAction(
|
||||||
|
{
|
||||||
|
action: "editMessage",
|
||||||
|
channelId: "C123",
|
||||||
|
messageId: "123.456",
|
||||||
|
blocks,
|
||||||
|
},
|
||||||
|
slackConfig(),
|
||||||
|
);
|
||||||
|
expect(editSlackMessage).toHaveBeenCalledWith("C123", "123.456", "", {
|
||||||
|
blocks: expectedBlocks,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("requires content or blocks for editMessage", async () => {
|
it("requires content or blocks for editMessage", async () => {
|
||||||
const cfg = { channels: { slack: { botToken: "tok" } } } as OpenClawConfig;
|
|
||||||
await expect(
|
await expect(
|
||||||
handleSlackAction(
|
handleSlackAction(
|
||||||
{
|
{
|
||||||
@@ -280,7 +253,7 @@ describe("handleSlackAction", () => {
|
|||||||
messageId: "123.456",
|
messageId: "123.456",
|
||||||
content: "",
|
content: "",
|
||||||
},
|
},
|
||||||
cfg,
|
slackConfig(),
|
||||||
),
|
),
|
||||||
).rejects.toThrow(/requires content or blocks/i);
|
).rejects.toThrow(/requires content or blocks/i);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -40,6 +40,17 @@ describe("handleTelegramAction", () => {
|
|||||||
} as OpenClawConfig;
|
} as OpenClawConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function telegramConfig(overrides?: Record<string, unknown>): OpenClawConfig {
|
||||||
|
return {
|
||||||
|
channels: {
|
||||||
|
telegram: {
|
||||||
|
botToken: "tok",
|
||||||
|
...overrides,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as OpenClawConfig;
|
||||||
|
}
|
||||||
|
|
||||||
async function expectReactionAdded(reactionLevel: "minimal" | "extensive") {
|
async function expectReactionAdded(reactionLevel: "minimal" | "extensive") {
|
||||||
await handleTelegramAction(defaultReactionAction, reactionConfig(reactionLevel));
|
await handleTelegramAction(defaultReactionAction, reactionConfig(reactionLevel));
|
||||||
expect(reactMessageTelegram).toHaveBeenCalledWith(
|
expect(reactMessageTelegram).toHaveBeenCalledWith(
|
||||||
@@ -166,8 +177,16 @@ describe("handleTelegramAction", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("blocks reactions when reactionLevel is off", async () => {
|
it.each([
|
||||||
const cfg = reactionConfig("off");
|
{
|
||||||
|
level: "off" as const,
|
||||||
|
expectedMessage: /Telegram agent reactions disabled.*reactionLevel="off"/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: "ack" as const,
|
||||||
|
expectedMessage: /Telegram agent reactions disabled.*reactionLevel="ack"/,
|
||||||
|
},
|
||||||
|
])("blocks reactions when reactionLevel is $level", async ({ level, expectedMessage }) => {
|
||||||
await expect(
|
await expect(
|
||||||
handleTelegramAction(
|
handleTelegramAction(
|
||||||
{
|
{
|
||||||
@@ -176,24 +195,9 @@ describe("handleTelegramAction", () => {
|
|||||||
messageId: "456",
|
messageId: "456",
|
||||||
emoji: "✅",
|
emoji: "✅",
|
||||||
},
|
},
|
||||||
cfg,
|
reactionConfig(level),
|
||||||
),
|
),
|
||||||
).rejects.toThrow(/Telegram agent reactions disabled.*reactionLevel="off"/);
|
).rejects.toThrow(expectedMessage);
|
||||||
});
|
|
||||||
|
|
||||||
it("blocks reactions when reactionLevel is ack", async () => {
|
|
||||||
const cfg = reactionConfig("ack");
|
|
||||||
await expect(
|
|
||||||
handleTelegramAction(
|
|
||||||
{
|
|
||||||
action: "react",
|
|
||||||
chatId: "123",
|
|
||||||
messageId: "456",
|
|
||||||
emoji: "✅",
|
|
||||||
},
|
|
||||||
cfg,
|
|
||||||
),
|
|
||||||
).rejects.toThrow(/Telegram agent reactions disabled.*reactionLevel="ack"/);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("also respects legacy actions.reactions gating", async () => {
|
it("also respects legacy actions.reactions gating", async () => {
|
||||||
@@ -220,16 +224,13 @@ describe("handleTelegramAction", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("sends a text message", async () => {
|
it("sends a text message", async () => {
|
||||||
const cfg = {
|
|
||||||
channels: { telegram: { botToken: "tok" } },
|
|
||||||
} as OpenClawConfig;
|
|
||||||
const result = await handleTelegramAction(
|
const result = await handleTelegramAction(
|
||||||
{
|
{
|
||||||
action: "sendMessage",
|
action: "sendMessage",
|
||||||
to: "@testchannel",
|
to: "@testchannel",
|
||||||
content: "Hello, Telegram!",
|
content: "Hello, Telegram!",
|
||||||
},
|
},
|
||||||
cfg,
|
telegramConfig(),
|
||||||
);
|
);
|
||||||
expect(sendMessageTelegram).toHaveBeenCalledWith(
|
expect(sendMessageTelegram).toHaveBeenCalledWith(
|
||||||
"@testchannel",
|
"@testchannel",
|
||||||
@@ -242,87 +243,66 @@ describe("handleTelegramAction", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sends a message with media", async () => {
|
it.each([
|
||||||
const cfg = {
|
{
|
||||||
channels: { telegram: { botToken: "tok" } },
|
name: "media",
|
||||||
} as OpenClawConfig;
|
params: {
|
||||||
await handleTelegramAction(
|
|
||||||
{
|
|
||||||
action: "sendMessage",
|
action: "sendMessage",
|
||||||
to: "123456",
|
to: "123456",
|
||||||
content: "Check this image!",
|
content: "Check this image!",
|
||||||
mediaUrl: "https://example.com/image.jpg",
|
mediaUrl: "https://example.com/image.jpg",
|
||||||
},
|
},
|
||||||
cfg,
|
expectedTo: "123456",
|
||||||
);
|
expectedContent: "Check this image!",
|
||||||
expect(sendMessageTelegram).toHaveBeenCalledWith(
|
expectedOptions: { mediaUrl: "https://example.com/image.jpg" },
|
||||||
"123456",
|
},
|
||||||
"Check this image!",
|
{
|
||||||
expect.objectContaining({
|
name: "quoteText",
|
||||||
token: "tok",
|
params: {
|
||||||
mediaUrl: "https://example.com/image.jpg",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("passes quoteText when provided", async () => {
|
|
||||||
const cfg = {
|
|
||||||
channels: { telegram: { botToken: "tok" } },
|
|
||||||
} as OpenClawConfig;
|
|
||||||
await handleTelegramAction(
|
|
||||||
{
|
|
||||||
action: "sendMessage",
|
action: "sendMessage",
|
||||||
to: "123456",
|
to: "123456",
|
||||||
content: "Replying now",
|
content: "Replying now",
|
||||||
replyToMessageId: 144,
|
replyToMessageId: 144,
|
||||||
quoteText: "The text you want to quote",
|
quoteText: "The text you want to quote",
|
||||||
},
|
},
|
||||||
cfg,
|
expectedTo: "123456",
|
||||||
);
|
expectedContent: "Replying now",
|
||||||
expect(sendMessageTelegram).toHaveBeenCalledWith(
|
expectedOptions: {
|
||||||
"123456",
|
|
||||||
"Replying now",
|
|
||||||
expect.objectContaining({
|
|
||||||
token: "tok",
|
|
||||||
replyToMessageId: 144,
|
replyToMessageId: 144,
|
||||||
quoteText: "The text you want to quote",
|
quoteText: "The text you want to quote",
|
||||||
}),
|
},
|
||||||
);
|
},
|
||||||
});
|
{
|
||||||
|
name: "media-only",
|
||||||
it("allows media-only messages without content", async () => {
|
params: {
|
||||||
const cfg = {
|
|
||||||
channels: { telegram: { botToken: "tok" } },
|
|
||||||
} as OpenClawConfig;
|
|
||||||
await handleTelegramAction(
|
|
||||||
{
|
|
||||||
action: "sendMessage",
|
action: "sendMessage",
|
||||||
to: "123456",
|
to: "123456",
|
||||||
mediaUrl: "https://example.com/note.ogg",
|
mediaUrl: "https://example.com/note.ogg",
|
||||||
},
|
},
|
||||||
cfg,
|
expectedTo: "123456",
|
||||||
);
|
expectedContent: "",
|
||||||
|
expectedOptions: { mediaUrl: "https://example.com/note.ogg" },
|
||||||
|
},
|
||||||
|
] as const)("maps sendMessage params for $name", async (testCase) => {
|
||||||
|
await handleTelegramAction(testCase.params, telegramConfig());
|
||||||
expect(sendMessageTelegram).toHaveBeenCalledWith(
|
expect(sendMessageTelegram).toHaveBeenCalledWith(
|
||||||
"123456",
|
testCase.expectedTo,
|
||||||
"",
|
testCase.expectedContent,
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
token: "tok",
|
token: "tok",
|
||||||
mediaUrl: "https://example.com/note.ogg",
|
...testCase.expectedOptions,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("requires content when no mediaUrl is provided", async () => {
|
it("requires content when no mediaUrl is provided", async () => {
|
||||||
const cfg = {
|
|
||||||
channels: { telegram: { botToken: "tok" } },
|
|
||||||
} as OpenClawConfig;
|
|
||||||
await expect(
|
await expect(
|
||||||
handleTelegramAction(
|
handleTelegramAction(
|
||||||
{
|
{
|
||||||
action: "sendMessage",
|
action: "sendMessage",
|
||||||
to: "123456",
|
to: "123456",
|
||||||
},
|
},
|
||||||
cfg,
|
telegramConfig(),
|
||||||
),
|
),
|
||||||
).rejects.toThrow(/content required/i);
|
).rejects.toThrow(/content required/i);
|
||||||
});
|
});
|
||||||
@@ -413,42 +393,31 @@ describe("handleTelegramAction", () => {
|
|||||||
expect(sendMessageTelegram).toHaveBeenCalled();
|
expect(sendMessageTelegram).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("blocks inline buttons when scope is off", async () => {
|
it.each([
|
||||||
const cfg = {
|
{
|
||||||
channels: {
|
name: "scope is off",
|
||||||
telegram: { botToken: "tok", capabilities: { inlineButtons: "off" } },
|
to: "@testchannel",
|
||||||
},
|
inlineButtons: "off" as const,
|
||||||
} as OpenClawConfig;
|
expectedMessage: /inline buttons are disabled/i,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "scope is dm and target is group",
|
||||||
|
to: "-100123456",
|
||||||
|
inlineButtons: "dm" as const,
|
||||||
|
expectedMessage: /inline buttons are limited to DMs/i,
|
||||||
|
},
|
||||||
|
])("blocks inline buttons when $name", async ({ to, inlineButtons, expectedMessage }) => {
|
||||||
await expect(
|
await expect(
|
||||||
handleTelegramAction(
|
handleTelegramAction(
|
||||||
{
|
{
|
||||||
action: "sendMessage",
|
action: "sendMessage",
|
||||||
to: "@testchannel",
|
to,
|
||||||
content: "Choose",
|
content: "Choose",
|
||||||
buttons: [[{ text: "Ok", callback_data: "cmd:ok" }]],
|
buttons: [[{ text: "Ok", callback_data: "cmd:ok" }]],
|
||||||
},
|
},
|
||||||
cfg,
|
telegramConfig({ capabilities: { inlineButtons } }),
|
||||||
),
|
),
|
||||||
).rejects.toThrow(/inline buttons are disabled/i);
|
).rejects.toThrow(expectedMessage);
|
||||||
});
|
|
||||||
|
|
||||||
it("blocks inline buttons in groups when scope is dm", async () => {
|
|
||||||
const cfg = {
|
|
||||||
channels: {
|
|
||||||
telegram: { botToken: "tok", capabilities: { inlineButtons: "dm" } },
|
|
||||||
},
|
|
||||||
} as OpenClawConfig;
|
|
||||||
await expect(
|
|
||||||
handleTelegramAction(
|
|
||||||
{
|
|
||||||
action: "sendMessage",
|
|
||||||
to: "-100123456",
|
|
||||||
content: "Choose",
|
|
||||||
buttons: [[{ text: "Ok", callback_data: "cmd:ok" }]],
|
|
||||||
},
|
|
||||||
cfg,
|
|
||||||
),
|
|
||||||
).rejects.toThrow(/inline buttons are limited to DMs/i);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows inline buttons in DMs with tg: prefixed targets", async () => {
|
it("allows inline buttons in DMs with tg: prefixed targets", async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user