feishu: harden media support and align capability docs (#47968)

* feishu: harden media support and action surface

* feishu: format media action changes

* feishu: fix review follow-ups

* fix: scope Feishu target aliases to Feishu (#47968) (thanks @Takhoffman)
This commit is contained in:
Tak Hoffman
2026-03-16 02:02:48 -05:00
committed by GitHub
parent 476d948732
commit 3c6a49b27e
24 changed files with 1900 additions and 245 deletions

View File

@@ -171,6 +171,75 @@ describe("Feishu reply fallback for withdrawn/deleted targets", () => {
expect(createMock).not.toHaveBeenCalled();
});
it("fails thread replies instead of falling back to a top-level send", async () => {
replyMock.mockResolvedValue({
code: 230011,
msg: "The message was withdrawn.",
});
await expect(
sendMessageFeishu({
cfg: {} as never,
to: "chat:oc_group_1",
text: "hello",
replyToMessageId: "om_parent",
replyInThread: true,
}),
).rejects.toThrow(
"Feishu thread reply failed: reply target is unavailable and cannot safely fall back to a top-level send.",
);
expect(createMock).not.toHaveBeenCalled();
expect(replyMock).toHaveBeenCalledWith({
path: { message_id: "om_parent" },
data: expect.objectContaining({
reply_in_thread: true,
}),
});
});
it("fails thrown withdrawn thread replies instead of falling back to create", async () => {
const sdkError = Object.assign(new Error("request failed"), { code: 230011 });
replyMock.mockRejectedValue(sdkError);
await expect(
sendMessageFeishu({
cfg: {} as never,
to: "chat:oc_group_1",
text: "hello",
replyToMessageId: "om_parent",
replyInThread: true,
}),
).rejects.toThrow(
"Feishu thread reply failed: reply target is unavailable and cannot safely fall back to a top-level send.",
);
expect(createMock).not.toHaveBeenCalled();
});
it("still falls back for non-thread replies to withdrawn targets", async () => {
replyMock.mockResolvedValue({
code: 230011,
msg: "The message was withdrawn.",
});
createMock.mockResolvedValue({
code: 0,
data: { message_id: "om_non_thread_fallback" },
});
await expectFallbackResult(
() =>
sendMessageFeishu({
cfg: {} as never,
to: "user:ou_target",
text: "hello",
replyToMessageId: "om_parent",
replyInThread: false,
}),
"om_non_thread_fallback",
);
});
it("re-throws non-withdrawn thrown errors for card messages", async () => {
const sdkError = Object.assign(new Error("permission denied"), { code: 99991401 });
replyMock.mockRejectedValue(sdkError);