From 17996d366591326366c5c2609719e053bb3ee825 Mon Sep 17 00:00:00 2001 From: Tak Hoffman <781889+Takhoffman@users.noreply.github.com> Date: Sat, 28 Feb 2026 09:54:08 -0600 Subject: [PATCH] Feishu: align docx descendant insertion tests and changelog --- CHANGELOG.md | 1 + extensions/feishu/src/docx.test.ts | 31 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ccc97ca3a4..90e95967ba6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,7 @@ Docs: https://docs.openclaw.ai - FS/Sandbox workspace boundaries: add a dedicated `outside-workspace` safe-open error code for root-escape checks, and propagate specific outside-workspace messages across edit/browser/media consumers instead of generic not-found/invalid-path fallbacks. (#29715) Thanks @YuzuruS. - Config/Doctor group allowlist diagnostics: align `groupPolicy: "allowlist"` warnings with per-channel runtime semantics by excluding Google Chat sender-list checks and by warning when no-fallback channels (for example iMessage) omit `groupAllowFrom`, with regression coverage. (#28477) Thanks @tonydehnke. - Onboarding/Custom providers: use Azure OpenAI-specific verification auth/payload shape (`api-key`, deployment-path chat completions payload) when probing Azure endpoints so valid Azure custom-provider setup no longer fails preflight. (#29421) Thanks @kunalk16. +- Feishu/Docx editing tools: add `feishu_doc` positional insert, table row/column operations, table-cell merge, and color-text updates; switch markdown write/append/insert to Descendant API insertion with large-document batching; and harden image uploads for data URI/base64/local-path inputs with strict validation and routing-safe upload metadata. (#29411) Thanks @Elarwei001. ## 2026.2.26 diff --git a/extensions/feishu/src/docx.test.ts b/extensions/feishu/src/docx.test.ts index a7a5bbd8a12..5fdfec208de 100644 --- a/extensions/feishu/src/docx.test.ts +++ b/extensions/feishu/src/docx.test.ts @@ -130,11 +130,10 @@ describe("feishu_doc image fetch hardening", () => { blockListMock.mockResolvedValue({ code: 0, data: { items: [] } }); - // Each call returns the single block that was passed in - blockChildrenCreateMock - .mockResolvedValueOnce({ code: 0, data: { children: [{ block_type: 3, block_id: "h1" }] } }) - .mockResolvedValueOnce({ code: 0, data: { children: [{ block_type: 2, block_id: "t1" }] } }) - .mockResolvedValueOnce({ code: 0, data: { children: [{ block_type: 3, block_id: "h2" }] } }); + blockDescendantCreateMock.mockResolvedValueOnce({ + code: 0, + data: { children: [{ block_type: 3, block_id: "h1" }] }, + }); const registerTool = vi.fn(); registerFeishuDocTools({ @@ -159,15 +158,11 @@ describe("feishu_doc image fetch hardening", () => { content: "plain text body", }); - // Verify sequential insertion: one call per block - expect(blockChildrenCreateMock).toHaveBeenCalledTimes(3); - - // Verify each call received exactly one block in the correct order - const calls = blockChildrenCreateMock.mock.calls; - expect(calls[0][0].data.children).toHaveLength(1); - expect(calls[0][0].data.children[0].block_id).toBe("h1"); - expect(calls[1][0].data.children[0].block_id).toBe("t1"); - expect(calls[2][0].data.children[0].block_id).toBe("h2"); + expect(blockDescendantCreateMock).toHaveBeenCalledTimes(1); + const call = blockDescendantCreateMock.mock.calls[0]?.[0]; + expect(call?.data.children_id).toEqual(["h1", "t1", "h2"]); + expect(call?.data.descendants).toBeDefined(); + expect(call?.data.descendants.length).toBeGreaterThanOrEqual(3); expect(result.details.blocks_added).toBe(3); }); @@ -190,9 +185,13 @@ describe("feishu_doc image fetch hardening", () => { }; }); - blockChildrenCreateMock.mockImplementation(async ({ data }) => ({ + blockDescendantCreateMock.mockImplementation(async ({ data }) => ({ code: 0, - data: { children: data.children }, + data: { + children: (data.children_id as string[]).map((id) => ({ + block_id: id, + })), + }, })); const registerTool = vi.fn();