From 684a1565a9c130d8d872d670a79d43926b8d8ade Mon Sep 17 00:00:00 2001 From: Tak Hoffman <781889+Takhoffman@users.noreply.github.com> Date: Fri, 27 Mar 2026 21:51:37 -0500 Subject: [PATCH] fix(regression): align feishu send helper runtime usage --- extensions/feishu/src/send.test.ts | 67 +++++++++++++++++++++++++++++- extensions/feishu/src/send.ts | 10 +++-- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/extensions/feishu/src/send.test.ts b/extensions/feishu/src/send.test.ts index edd906d984f..69ecba127fe 100644 --- a/extensions/feishu/src/send.test.ts +++ b/extensions/feishu/src/send.test.ts @@ -2,17 +2,33 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import type { ClawdbotConfig } from "../runtime-api.js"; const { + mockConvertMarkdownTables, mockClientGet, mockClientList, mockClientPatch, mockCreateFeishuClient, + mockResolveMarkdownTableMode, mockResolveFeishuAccount, + mockRuntimeConvertMarkdownTables, + mockRuntimeResolveMarkdownTableMode, } = vi.hoisted(() => ({ + mockConvertMarkdownTables: vi.fn((text: string) => text), mockClientGet: vi.fn(), mockClientList: vi.fn(), mockClientPatch: vi.fn(), mockCreateFeishuClient: vi.fn(), + mockResolveMarkdownTableMode: vi.fn(() => "preserve"), mockResolveFeishuAccount: vi.fn(), + mockRuntimeConvertMarkdownTables: vi.fn((text: string) => text), + mockRuntimeResolveMarkdownTableMode: vi.fn(() => "preserve"), +})); + +vi.mock("openclaw/plugin-sdk/config-runtime", () => ({ + resolveMarkdownTableMode: mockResolveMarkdownTableMode, +})); + +vi.mock("openclaw/plugin-sdk/text-runtime", () => ({ + convertMarkdownTables: mockConvertMarkdownTables, })); vi.mock("./client.js", () => ({ @@ -28,8 +44,8 @@ vi.mock("./runtime.js", () => ({ getFeishuRuntime: () => ({ channel: { text: { - resolveMarkdownTableMode: () => "preserve", - convertMarkdownTables: (text: string) => text, + resolveMarkdownTableMode: mockRuntimeResolveMarkdownTableMode, + convertMarkdownTables: mockRuntimeConvertMarkdownTables, }, }, }), @@ -40,6 +56,7 @@ let editMessageFeishu: typeof import("./send.js").editMessageFeishu; let getMessageFeishu: typeof import("./send.js").getMessageFeishu; let listFeishuThreadMessages: typeof import("./send.js").listFeishuThreadMessages; let resolveFeishuCardTemplate: typeof import("./send.js").resolveFeishuCardTemplate; +let sendMessageFeishu: typeof import("./send.js").sendMessageFeishu; describe("getMessageFeishu", () => { beforeEach(async () => { @@ -50,8 +67,13 @@ describe("getMessageFeishu", () => { getMessageFeishu, listFeishuThreadMessages, resolveFeishuCardTemplate, + sendMessageFeishu, } = await import("./send.js")); vi.clearAllMocks(); + mockResolveMarkdownTableMode.mockReturnValue("preserve"); + mockConvertMarkdownTables.mockImplementation((text: string) => text); + mockRuntimeResolveMarkdownTableMode.mockReturnValue("preserve"); + mockRuntimeConvertMarkdownTables.mockImplementation((text: string) => text); mockResolveFeishuAccount.mockReturnValue({ accountId: "default", configured: true, @@ -59,6 +81,7 @@ describe("getMessageFeishu", () => { mockCreateFeishuClient.mockReturnValue({ im: { message: { + create: vi.fn(), get: mockClientGet, list: mockClientList, patch: mockClientPatch, @@ -67,6 +90,40 @@ describe("getMessageFeishu", () => { }); }); + it("sends text without requiring Feishu runtime text helpers", async () => { + mockRuntimeResolveMarkdownTableMode.mockImplementation(() => { + throw new Error("Feishu runtime not initialized"); + }); + mockRuntimeConvertMarkdownTables.mockImplementation(() => { + throw new Error("Feishu runtime not initialized"); + }); + mockClientPatch.mockResolvedValueOnce({ code: 0 }); + mockCreateFeishuClient.mockReturnValue({ + im: { + message: { + create: vi.fn().mockResolvedValue({ code: 0, data: { message_id: "om_send" } }), + reply: vi.fn(), + get: mockClientGet, + list: mockClientList, + patch: mockClientPatch, + }, + }, + }); + + const result = await sendMessageFeishu({ + cfg: {} as ClawdbotConfig, + to: "oc_send", + text: "hello", + }); + + expect(mockResolveMarkdownTableMode).toHaveBeenCalledWith({ + cfg: {}, + channel: "feishu", + }); + expect(mockConvertMarkdownTables).toHaveBeenCalledWith("hello", "preserve"); + expect(result).toEqual({ messageId: "om_send", chatId: "oc_send" }); + }); + it("extracts text content from interactive card elements", async () => { mockClientGet.mockResolvedValueOnce({ code: 0, @@ -283,6 +340,12 @@ describe("editMessageFeishu", () => { }); it("patches post content for text edits", async () => { + mockRuntimeResolveMarkdownTableMode.mockImplementation(() => { + throw new Error("Feishu runtime not initialized"); + }); + mockRuntimeConvertMarkdownTables.mockImplementation(() => { + throw new Error("Feishu runtime not initialized"); + }); mockClientPatch.mockResolvedValueOnce({ code: 0 }); const result = await editMessageFeishu({ diff --git a/extensions/feishu/src/send.ts b/extensions/feishu/src/send.ts index 0ac34cadeff..3791fca739c 100644 --- a/extensions/feishu/src/send.ts +++ b/extensions/feishu/src/send.ts @@ -1,3 +1,5 @@ +import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/config-runtime"; +import { convertMarkdownTables } from "openclaw/plugin-sdk/text-runtime"; import type { ClawdbotConfig } from "../runtime-api.js"; import { resolveFeishuRuntimeAccount } from "./accounts.js"; import { createFeishuClient } from "./client.js"; @@ -445,7 +447,7 @@ export async function sendMessageFeishu( ): Promise { const { cfg, to, text, replyToMessageId, replyInThread, mentions, accountId } = params; const { client, receiveId, receiveIdType } = resolveFeishuSendTarget({ cfg, to, accountId }); - const tableMode = getFeishuRuntime().channel.text.resolveMarkdownTableMode({ + const tableMode = resolveMarkdownTableMode({ cfg, channel: "feishu", }); @@ -455,7 +457,7 @@ export async function sendMessageFeishu( if (mentions && mentions.length > 0) { rawText = buildMentionedMessage(mentions, rawText); } - const messageText = getFeishuRuntime().channel.text.convertMarkdownTables(rawText, tableMode); + const messageText = convertMarkdownTables(rawText, tableMode); const { content, msgType } = buildFeishuPostMessagePayload({ messageText }); @@ -533,11 +535,11 @@ export async function editMessageFeishu(params: { return { messageId, contentType: "interactive" }; } - const tableMode = getFeishuRuntime().channel.text.resolveMarkdownTableMode({ + const tableMode = resolveMarkdownTableMode({ cfg, channel: "feishu", }); - const messageText = getFeishuRuntime().channel.text.convertMarkdownTables(text!, tableMode); + const messageText = convertMarkdownTables(text!, tableMode); const payload = buildFeishuPostMessagePayload({ messageText }); const response = await client.im.message.patch({ path: { message_id: messageId },