mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 00:27:26 +00:00
fix(extensions): synthesize mediaLocalRoots propagation across sendMedia adapters
Restore deterministic mediaLocalRoots propagation through extension sendMedia adapters and add coverage for local/remote media handling in Google Chat. Synthesis of #33581, #33545, #33540, #33536, #33528. Co-authored-by: bmendonca3 <bmendonca3@users.noreply.github.com>
This commit is contained in:
168
extensions/googlechat/src/channel.outbound.test.ts
Normal file
168
extensions/googlechat/src/channel.outbound.test.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const uploadGoogleChatAttachmentMock = vi.hoisted(() => vi.fn());
|
||||
const sendGoogleChatMessageMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("./api.js", () => ({
|
||||
sendGoogleChatMessage: sendGoogleChatMessageMock,
|
||||
uploadGoogleChatAttachment: uploadGoogleChatAttachmentMock,
|
||||
}));
|
||||
|
||||
import { googlechatPlugin } from "./channel.js";
|
||||
import { setGoogleChatRuntime } from "./runtime.js";
|
||||
|
||||
describe("googlechatPlugin outbound sendMedia", () => {
|
||||
it("loads local media with mediaLocalRoots via runtime media loader", async () => {
|
||||
const loadWebMedia = vi.fn(async () => ({
|
||||
buffer: Buffer.from("image-bytes"),
|
||||
fileName: "image.png",
|
||||
contentType: "image/png",
|
||||
}));
|
||||
const fetchRemoteMedia = vi.fn(async () => ({
|
||||
buffer: Buffer.from("remote-bytes"),
|
||||
fileName: "remote.png",
|
||||
contentType: "image/png",
|
||||
}));
|
||||
|
||||
setGoogleChatRuntime({
|
||||
media: { loadWebMedia },
|
||||
channel: {
|
||||
media: { fetchRemoteMedia },
|
||||
text: { chunkMarkdownText: (text: string) => [text] },
|
||||
},
|
||||
} as unknown as PluginRuntime);
|
||||
|
||||
uploadGoogleChatAttachmentMock.mockResolvedValue({
|
||||
attachmentUploadToken: "token-1",
|
||||
});
|
||||
sendGoogleChatMessageMock.mockResolvedValue({
|
||||
messageName: "spaces/AAA/messages/msg-1",
|
||||
});
|
||||
|
||||
const cfg: OpenClawConfig = {
|
||||
channels: {
|
||||
googlechat: {
|
||||
enabled: true,
|
||||
serviceAccount: {
|
||||
type: "service_account",
|
||||
client_email: "bot@example.com",
|
||||
private_key: "test-key",
|
||||
token_uri: "https://oauth2.googleapis.com/token",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = await googlechatPlugin.outbound?.sendMedia?.({
|
||||
cfg,
|
||||
to: "spaces/AAA",
|
||||
text: "caption",
|
||||
mediaUrl: "/tmp/workspace/image.png",
|
||||
mediaLocalRoots: ["/tmp/workspace"],
|
||||
accountId: "default",
|
||||
});
|
||||
|
||||
expect(loadWebMedia).toHaveBeenCalledWith(
|
||||
"/tmp/workspace/image.png",
|
||||
expect.objectContaining({
|
||||
localRoots: ["/tmp/workspace"],
|
||||
}),
|
||||
);
|
||||
expect(fetchRemoteMedia).not.toHaveBeenCalled();
|
||||
expect(uploadGoogleChatAttachmentMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
space: "spaces/AAA",
|
||||
filename: "image.png",
|
||||
contentType: "image/png",
|
||||
}),
|
||||
);
|
||||
expect(sendGoogleChatMessageMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
space: "spaces/AAA",
|
||||
text: "caption",
|
||||
}),
|
||||
);
|
||||
expect(result).toEqual({
|
||||
channel: "googlechat",
|
||||
messageId: "spaces/AAA/messages/msg-1",
|
||||
chatId: "spaces/AAA",
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps remote URL media fetch on fetchRemoteMedia with maxBytes cap", async () => {
|
||||
const loadWebMedia = vi.fn(async () => ({
|
||||
buffer: Buffer.from("should-not-be-used"),
|
||||
fileName: "unused.png",
|
||||
contentType: "image/png",
|
||||
}));
|
||||
const fetchRemoteMedia = vi.fn(async () => ({
|
||||
buffer: Buffer.from("remote-bytes"),
|
||||
fileName: "remote.png",
|
||||
contentType: "image/png",
|
||||
}));
|
||||
|
||||
setGoogleChatRuntime({
|
||||
media: { loadWebMedia },
|
||||
channel: {
|
||||
media: { fetchRemoteMedia },
|
||||
text: { chunkMarkdownText: (text: string) => [text] },
|
||||
},
|
||||
} as unknown as PluginRuntime);
|
||||
|
||||
uploadGoogleChatAttachmentMock.mockResolvedValue({
|
||||
attachmentUploadToken: "token-2",
|
||||
});
|
||||
sendGoogleChatMessageMock.mockResolvedValue({
|
||||
messageName: "spaces/AAA/messages/msg-2",
|
||||
});
|
||||
|
||||
const cfg: OpenClawConfig = {
|
||||
channels: {
|
||||
googlechat: {
|
||||
enabled: true,
|
||||
serviceAccount: {
|
||||
type: "service_account",
|
||||
client_email: "bot@example.com",
|
||||
private_key: "test-key",
|
||||
token_uri: "https://oauth2.googleapis.com/token",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = await googlechatPlugin.outbound?.sendMedia?.({
|
||||
cfg,
|
||||
to: "spaces/AAA",
|
||||
text: "caption",
|
||||
mediaUrl: "https://example.com/image.png",
|
||||
accountId: "default",
|
||||
});
|
||||
|
||||
expect(fetchRemoteMedia).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
url: "https://example.com/image.png",
|
||||
maxBytes: 20 * 1024 * 1024,
|
||||
}),
|
||||
);
|
||||
expect(loadWebMedia).not.toHaveBeenCalled();
|
||||
expect(uploadGoogleChatAttachmentMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
space: "spaces/AAA",
|
||||
filename: "remote.png",
|
||||
contentType: "image/png",
|
||||
}),
|
||||
);
|
||||
expect(sendGoogleChatMessageMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
space: "spaces/AAA",
|
||||
text: "caption",
|
||||
}),
|
||||
);
|
||||
expect(result).toEqual({
|
||||
channel: "googlechat",
|
||||
messageId: "spaces/AAA/messages/msg-2",
|
||||
chatId: "spaces/AAA",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -421,7 +421,16 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
|
||||
chatId: space,
|
||||
};
|
||||
},
|
||||
sendMedia: async ({ cfg, to, text, mediaUrl, accountId, replyToId, threadId }) => {
|
||||
sendMedia: async ({
|
||||
cfg,
|
||||
to,
|
||||
text,
|
||||
mediaUrl,
|
||||
mediaLocalRoots,
|
||||
accountId,
|
||||
replyToId,
|
||||
threadId,
|
||||
}) => {
|
||||
if (!mediaUrl) {
|
||||
throw new Error("Google Chat mediaUrl is required.");
|
||||
}
|
||||
@@ -443,10 +452,16 @@ export const googlechatPlugin: ChannelPlugin<ResolvedGoogleChatAccount> = {
|
||||
(cfg.channels?.["googlechat"] as { mediaMaxMb?: number } | undefined)?.mediaMaxMb,
|
||||
accountId,
|
||||
});
|
||||
const loaded = await runtime.channel.media.fetchRemoteMedia({
|
||||
url: mediaUrl,
|
||||
maxBytes: maxBytes ?? (account.config.mediaMaxMb ?? 20) * 1024 * 1024,
|
||||
});
|
||||
const effectiveMaxBytes = maxBytes ?? (account.config.mediaMaxMb ?? 20) * 1024 * 1024;
|
||||
const loaded = /^https?:\/\//i.test(mediaUrl)
|
||||
? await runtime.channel.media.fetchRemoteMedia({
|
||||
url: mediaUrl,
|
||||
maxBytes: effectiveMaxBytes,
|
||||
})
|
||||
: await runtime.media.loadWebMedia(mediaUrl, {
|
||||
maxBytes: effectiveMaxBytes,
|
||||
localRoots: mediaLocalRoots?.length ? mediaLocalRoots : undefined,
|
||||
});
|
||||
const upload = await uploadGoogleChatAttachment({
|
||||
account,
|
||||
space,
|
||||
|
||||
Reference in New Issue
Block a user