diff --git a/extensions/feishu/src/bot.broadcast.test.ts b/extensions/feishu/src/bot.broadcast.test.ts index 1d6aa878cd7..10c388a342c 100644 --- a/extensions/feishu/src/bot.broadcast.test.ts +++ b/extensions/feishu/src/bot.broadcast.test.ts @@ -148,9 +148,11 @@ describe("broadcast dispatch", () => { reply: { resolveEnvelopeFormatOptions: resolveEnvelopeFormatOptionsMock, formatAgentEnvelope: vi.fn((params: { body: string }) => params.body), - finalizeInboundContext: mockFinalizeInboundContext, + finalizeInboundContext: + mockFinalizeInboundContext as unknown as PluginRuntime["channel"]["reply"]["finalizeInboundContext"], dispatchReplyFromConfig: mockDispatchReplyFromConfig, - withReplyDispatcher: mockWithReplyDispatcher, + withReplyDispatcher: + mockWithReplyDispatcher as unknown as PluginRuntime["channel"]["reply"]["withReplyDispatcher"], }, commands: { shouldComputeCommandAuthorized: mockShouldComputeCommandAuthorized, diff --git a/extensions/feishu/src/bot.test.ts b/extensions/feishu/src/bot.test.ts index 259da551f99..bb9c2a8e43f 100644 --- a/extensions/feishu/src/bot.test.ts +++ b/extensions/feishu/src/bot.test.ts @@ -122,12 +122,12 @@ function createConfiguredFeishuRoute(): NonNullable { mainSessionKey: "agent:codex:main", lastRoutePolicy: "session", matchedBy: "binding.channel", - }, + } as ResolvedAgentRoute, }; } function createConfiguredBindingReadiness(ok: boolean, error?: string): BindingReadiness { - return ok ? { ok: true } : { ok: false, error: error ?? "unknown error" }; + return (ok ? { ok: true } : { ok: false, error: error ?? "unknown error" }) as BindingReadiness; } function createBoundConversation(): NonNullable { @@ -158,6 +158,12 @@ function buildDefaultResolveRoute(): ResolvedAgentRoute { }; } +function createUnboundConfiguredRoute( + route: NonNullable["route"], +): ConfiguredBindingRoute { + return { bindingResolution: null, route }; +} + const resolveAgentRouteMock: PluginRuntime["channel"]["routing"]["resolveAgentRoute"] = (params) => mockResolveAgentRoute(params); const readSessionUpdatedAtMock: PluginRuntime["channel"]["session"]["readSessionUpdatedAt"] = ( @@ -310,7 +316,8 @@ describe("handleFeishuMessage ACP routing", () => { resolveStorePath: resolveStorePathMock, }, reply: { - resolveEnvelopeFormatOptions: resolveEnvelopeFormatOptionsMock, + resolveEnvelopeFormatOptions: + resolveEnvelopeFormatOptionsMock as unknown as PluginRuntime["channel"]["reply"]["resolveEnvelopeFormatOptions"], formatAgentEnvelope: vi.fn((params: { body: string }) => params.body), finalizeInboundContext: finalizeInboundContextMock as never, dispatchReplyFromConfig: vi.fn().mockResolvedValue({ @@ -513,7 +520,8 @@ describe("handleFeishuMessage command authorization", () => { resolveStorePath: resolveStorePathMock, }, reply: { - resolveEnvelopeFormatOptions: resolveEnvelopeFormatOptionsMock, + resolveEnvelopeFormatOptions: + resolveEnvelopeFormatOptionsMock as unknown as PluginRuntime["channel"]["reply"]["resolveEnvelopeFormatOptions"], formatAgentEnvelope: vi.fn((params: { body: string }) => params.body), finalizeInboundContext: mockFinalizeInboundContext as never, dispatchReplyFromConfig: mockDispatchReplyFromConfig, diff --git a/extensions/feishu/src/docx-batch-insert.test.ts b/extensions/feishu/src/docx-batch-insert.test.ts index 2b3f9f39db0..f033383f132 100644 --- a/extensions/feishu/src/docx-batch-insert.test.ts +++ b/extensions/feishu/src/docx-batch-insert.test.ts @@ -16,6 +16,7 @@ function createDocxDescendantClient(create: DocxDescendantCreate): InsertBlocksC }, }, } as InsertBlocksClient; + } as InsertBlocksClient; } function createCountingIterable(values: T[]) { diff --git a/extensions/feishu/src/docx.test.ts b/extensions/feishu/src/docx.test.ts index aec9f56260c..5505db10b6e 100644 --- a/extensions/feishu/src/docx.test.ts +++ b/extensions/feishu/src/docx.test.ts @@ -35,6 +35,10 @@ vi.mock("./runtime.js", () => ({ import { registerFeishuDocTools } from "./docx.js"; +type ToolResultWithDetails = { + details: Record; +}; + describe("feishu_doc image fetch hardening", () => { beforeEach(() => { vi.clearAllMocks(); @@ -131,6 +135,13 @@ describe("feishu_doc image fetch hardening", () => { return tool as ToolLike; } + async function executeFeishuDocTool( + tool: ToolLike, + params: Record, + ): Promise { + return (await tool.execute("tool-call", params)) as ToolResultWithDetails; + } + it("inserts blocks sequentially to preserve document order", async () => { const blocks = [ { block_type: 3, block_id: "h1" }, @@ -154,7 +165,7 @@ describe("feishu_doc image fetch hardening", () => { const feishuDocTool = resolveFeishuDocTool(); - const result = await feishuDocTool.execute("tool-call", { + const result = await executeFeishuDocTool(feishuDocTool, { action: "append", doc_token: "doc_1", content: "plain text body", @@ -247,7 +258,7 @@ describe("feishu_doc image fetch hardening", () => { (_, i) => `line ${i} with enough content to trigger fallback chunking`, ).join("\n"); - const result = await feishuDocTool.execute("tool-call", { + const result = await executeFeishuDocTool(feishuDocTool, { action: "append", doc_token: "doc_1", content: longMarkdown, @@ -301,7 +312,7 @@ describe("feishu_doc image fetch hardening", () => { "Tail paragraph three with enough text to exceed API limits when combined. ".repeat(8), ].join("\n"); - const result = await feishuDocTool.execute("tool-call", { + const result = await executeFeishuDocTool(feishuDocTool, { action: "append", doc_token: "doc_1", content: fencedMarkdown, @@ -324,7 +335,7 @@ describe("feishu_doc image fetch hardening", () => { const feishuDocTool = resolveFeishuDocTool(); - const result = await feishuDocTool.execute("tool-call", { + const result = await executeFeishuDocTool(feishuDocTool, { action: "write", doc_token: "doc_1", content: "![x](https://x.test/image.png)", @@ -344,7 +355,7 @@ describe("feishu_doc image fetch hardening", () => { requesterSenderId: "ou_123", }); - const result = await feishuDocTool.execute("tool-call", { + const result = await executeFeishuDocTool(feishuDocTool, { action: "create", title: "Demo", }); @@ -369,7 +380,7 @@ describe("feishu_doc image fetch hardening", () => { messageChannel: "feishu", }); - const result = await feishuDocTool.execute("tool-call", { + const result = await executeFeishuDocTool(feishuDocTool, { action: "create", title: "Demo", }); @@ -385,7 +396,7 @@ describe("feishu_doc image fetch hardening", () => { requesterSenderId: "ou_123", }); - const result = await feishuDocTool.execute("tool-call", { + const result = await executeFeishuDocTool(feishuDocTool, { action: "create", title: "Demo", grant_to_requester: false, @@ -403,7 +414,7 @@ describe("feishu_doc image fetch hardening", () => { const feishuDocTool = resolveFeishuDocTool(); - const result = await feishuDocTool.execute("tool-call", { + const result = await executeFeishuDocTool(feishuDocTool, { action: "create", title: "Demo", }); @@ -426,7 +437,7 @@ describe("feishu_doc image fetch hardening", () => { const feishuDocTool = resolveFeishuDocTool(); - const result = await feishuDocTool.execute("tool-call", { + const result = await executeFeishuDocTool(feishuDocTool, { action: "upload_file", doc_token: "doc_1", file_path: "/tmp/allowed/test-local.txt", @@ -475,7 +486,7 @@ describe("feishu_doc image fetch hardening", () => { const feishuDocTool = resolveFeishuDocTool(); - const result = await feishuDocTool.execute("tool-call", { + const result = await executeFeishuDocTool(feishuDocTool, { action: "upload_file", doc_token: "doc_1", file_path: "/tmp/allowed/test-local.txt", @@ -493,7 +504,7 @@ describe("feishu_doc image fetch hardening", () => { const feishuDocTool = resolveFeishuDocTool(); - const result = await feishuDocTool.execute("tool-call", { + const result = await executeFeishuDocTool(feishuDocTool, { action: "upload_file", doc_token: "doc_1", file_path: "/etc/passwd", @@ -519,7 +530,7 @@ describe("feishu_doc image fetch hardening", () => { const feishuDocTool = resolveFeishuDocTool(); - const result = await feishuDocTool.execute("tool-call", { + const result = await executeFeishuDocTool(feishuDocTool, { action: "upload_image", doc_token: "doc_1", file_path: "/home/admin/.openclaw/openclaw.json", diff --git a/extensions/feishu/src/policy.test.ts b/extensions/feishu/src/policy.test.ts index 1fd8f0e4e94..47d4b83b36d 100644 --- a/extensions/feishu/src/policy.test.ts +++ b/extensions/feishu/src/policy.test.ts @@ -17,6 +17,10 @@ function createCfg(feishu: Record): OpenClawConfig { } as OpenClawConfig; } +function createFeishuConfig(overrides: Partial): FeishuConfig { + return FeishuConfigSchema.parse(overrides); +} + describe("resolveFeishuReplyPolicy", () => { it("defaults open groups to no mention when unset", () => { expect( @@ -89,7 +93,7 @@ describe("resolveFeishuReplyPolicy", () => { describe("resolveFeishuGroupConfig", () => { it("falls back to wildcard group config when direct match is missing", () => { - const cfg: FeishuConfig = FeishuConfigSchema.parse({ + const cfg = createFeishuConfig({ groups: { "*": { requireMention: false }, "oc-explicit": { requireMention: true }, @@ -105,7 +109,7 @@ describe("resolveFeishuGroupConfig", () => { }); it("prefers exact group config over wildcard", () => { - const cfg: FeishuConfig = FeishuConfigSchema.parse({ + const cfg = createFeishuConfig({ groups: { "*": { requireMention: false }, "oc-explicit": { requireMention: true }, @@ -121,7 +125,7 @@ describe("resolveFeishuGroupConfig", () => { }); it("keeps case-insensitive matching for explicit group ids", () => { - const cfg: FeishuConfig = FeishuConfigSchema.parse({ + const cfg = createFeishuConfig({ groups: { "*": { requireMention: false }, OC_UPPER: { requireMention: true },