openclaw/extensions/feishu/src/send.test.ts

271 lines
6.5 KiB
TypeScript

import type { ClawdbotConfig } from "openclaw/plugin-sdk/feishu";
import { beforeEach, describe, expect, it, vi } from "vitest";
import {
buildStructuredCard,
getMessageFeishu,
listFeishuThreadMessages,
resolveFeishuCardTemplate,
} from "./send.js";
const { mockClientGet, mockClientList, mockCreateFeishuClient, mockResolveFeishuAccount } =
vi.hoisted(() => ({
mockClientGet: vi.fn(),
mockClientList: vi.fn(),
mockCreateFeishuClient: vi.fn(),
mockResolveFeishuAccount: vi.fn(),
}));
vi.mock("./client.js", () => ({
createFeishuClient: mockCreateFeishuClient,
}));
vi.mock("./accounts.js", () => ({
resolveFeishuAccount: mockResolveFeishuAccount,
}));
describe("getMessageFeishu", () => {
beforeEach(() => {
vi.clearAllMocks();
mockResolveFeishuAccount.mockReturnValue({
accountId: "default",
configured: true,
});
mockCreateFeishuClient.mockReturnValue({
im: {
message: {
get: mockClientGet,
list: mockClientList,
},
},
});
});
it("extracts text content from interactive card elements", async () => {
mockClientGet.mockResolvedValueOnce({
code: 0,
data: {
items: [
{
message_id: "om_1",
chat_id: "oc_1",
msg_type: "interactive",
body: {
content: JSON.stringify({
elements: [
{ tag: "markdown", content: "hello markdown" },
{ tag: "div", text: { content: "hello div" } },
],
}),
},
},
],
},
});
const result = await getMessageFeishu({
cfg: {} as ClawdbotConfig,
messageId: "om_1",
});
expect(result).toEqual(
expect.objectContaining({
messageId: "om_1",
chatId: "oc_1",
contentType: "interactive",
content: "hello markdown\nhello div",
}),
);
});
it("extracts text content from post messages", async () => {
mockClientGet.mockResolvedValueOnce({
code: 0,
data: {
items: [
{
message_id: "om_post",
chat_id: "oc_post",
msg_type: "post",
body: {
content: JSON.stringify({
zh_cn: {
title: "Summary",
content: [[{ tag: "text", text: "post body" }]],
},
}),
},
},
],
},
});
const result = await getMessageFeishu({
cfg: {} as ClawdbotConfig,
messageId: "om_post",
});
expect(result).toEqual(
expect.objectContaining({
messageId: "om_post",
chatId: "oc_post",
contentType: "post",
content: "Summary\n\npost body",
}),
);
});
it("returns text placeholder instead of raw JSON for unsupported message types", async () => {
mockClientGet.mockResolvedValueOnce({
code: 0,
data: {
items: [
{
message_id: "om_file",
chat_id: "oc_file",
msg_type: "file",
body: {
content: JSON.stringify({ file_key: "file_v3_123" }),
},
},
],
},
});
const result = await getMessageFeishu({
cfg: {} as ClawdbotConfig,
messageId: "om_file",
});
expect(result).toEqual(
expect.objectContaining({
messageId: "om_file",
chatId: "oc_file",
contentType: "file",
content: "[file message]",
}),
);
});
it("supports single-object response shape from Feishu API", async () => {
mockClientGet.mockResolvedValueOnce({
code: 0,
data: {
message_id: "om_single",
chat_id: "oc_single",
msg_type: "text",
body: {
content: JSON.stringify({ text: "single payload" }),
},
},
});
const result = await getMessageFeishu({
cfg: {} as ClawdbotConfig,
messageId: "om_single",
});
expect(result).toEqual(
expect.objectContaining({
messageId: "om_single",
chatId: "oc_single",
contentType: "text",
content: "single payload",
}),
);
});
it("reuses the same content parsing for thread history messages", async () => {
mockClientList.mockResolvedValueOnce({
code: 0,
data: {
items: [
{
message_id: "om_root",
msg_type: "text",
body: {
content: JSON.stringify({ text: "root starter" }),
},
},
{
message_id: "om_card",
msg_type: "interactive",
body: {
content: JSON.stringify({
body: {
elements: [{ tag: "markdown", content: "hello from card 2.0" }],
},
}),
},
sender: {
id: "app_1",
sender_type: "app",
},
create_time: "1710000000000",
},
{
message_id: "om_file",
msg_type: "file",
body: {
content: JSON.stringify({ file_key: "file_v3_123" }),
},
sender: {
id: "ou_1",
sender_type: "user",
},
create_time: "1710000001000",
},
],
},
});
const result = await listFeishuThreadMessages({
cfg: {} as ClawdbotConfig,
threadId: "omt_1",
rootMessageId: "om_root",
});
expect(result).toEqual([
expect.objectContaining({
messageId: "om_file",
contentType: "file",
content: "[file message]",
}),
expect.objectContaining({
messageId: "om_card",
contentType: "interactive",
content: "hello from card 2.0",
}),
]);
});
});
describe("resolveFeishuCardTemplate", () => {
it("accepts supported Feishu templates", () => {
expect(resolveFeishuCardTemplate(" purple ")).toBe("purple");
});
it("drops unsupported free-form identity themes", () => {
expect(resolveFeishuCardTemplate("space lobster")).toBeUndefined();
});
});
describe("buildStructuredCard", () => {
it("falls back to blue when the header template is unsupported", () => {
const card = buildStructuredCard("hello", {
header: {
title: "Agent",
template: "space lobster",
},
});
expect(card).toEqual(
expect.objectContaining({
header: {
title: { tag: "plain_text", content: "Agent" },
template: "blue",
},
}),
);
});
});