mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-23 06:41:44 +00:00
test: collapse googlechat helper suites
This commit is contained in:
@@ -1,131 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { OpenClawConfig } from "../runtime-api.js";
|
||||
import { resolveGoogleChatAccount } from "./accounts.js";
|
||||
|
||||
describe("resolveGoogleChatAccount", () => {
|
||||
it("inherits shared defaults from accounts.default for named accounts", () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
channels: {
|
||||
googlechat: {
|
||||
accounts: {
|
||||
default: {
|
||||
audienceType: "app-url",
|
||||
audience: "https://example.com/googlechat",
|
||||
webhookPath: "/googlechat",
|
||||
},
|
||||
andy: {
|
||||
serviceAccountFile: "/tmp/andy-sa.json",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resolved = resolveGoogleChatAccount({ cfg, accountId: "andy" });
|
||||
expect(resolved.config.audienceType).toBe("app-url");
|
||||
expect(resolved.config.audience).toBe("https://example.com/googlechat");
|
||||
expect(resolved.config.webhookPath).toBe("/googlechat");
|
||||
expect(resolved.config.serviceAccountFile).toBe("/tmp/andy-sa.json");
|
||||
});
|
||||
|
||||
it("prefers top-level and account overrides over accounts.default", () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
channels: {
|
||||
googlechat: {
|
||||
audienceType: "project-number",
|
||||
audience: "1234567890",
|
||||
accounts: {
|
||||
default: {
|
||||
audienceType: "app-url",
|
||||
audience: "https://default.example.com/googlechat",
|
||||
webhookPath: "/googlechat-default",
|
||||
},
|
||||
april: {
|
||||
webhookPath: "/googlechat-april",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resolved = resolveGoogleChatAccount({ cfg, accountId: "april" });
|
||||
expect(resolved.config.audienceType).toBe("project-number");
|
||||
expect(resolved.config.audience).toBe("1234567890");
|
||||
expect(resolved.config.webhookPath).toBe("/googlechat-april");
|
||||
});
|
||||
|
||||
it("does not inherit disabled state from accounts.default for named accounts", () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
channels: {
|
||||
googlechat: {
|
||||
accounts: {
|
||||
default: {
|
||||
enabled: false,
|
||||
audienceType: "app-url",
|
||||
audience: "https://example.com/googlechat",
|
||||
},
|
||||
andy: {
|
||||
serviceAccountFile: "/tmp/andy-sa.json",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resolved = resolveGoogleChatAccount({ cfg, accountId: "andy" });
|
||||
expect(resolved.enabled).toBe(true);
|
||||
expect(resolved.config.enabled).toBeUndefined();
|
||||
expect(resolved.config.audienceType).toBe("app-url");
|
||||
});
|
||||
|
||||
it("does not inherit default-account credentials into named accounts", () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
channels: {
|
||||
googlechat: {
|
||||
accounts: {
|
||||
default: {
|
||||
serviceAccountRef: {
|
||||
source: "env",
|
||||
provider: "test",
|
||||
id: "default-sa",
|
||||
},
|
||||
audienceType: "app-url",
|
||||
audience: "https://example.com/googlechat",
|
||||
},
|
||||
andy: {
|
||||
serviceAccountFile: "/tmp/andy-sa.json",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resolved = resolveGoogleChatAccount({ cfg, accountId: "andy" });
|
||||
expect(resolved.credentialSource).toBe("file");
|
||||
expect(resolved.credentialsFile).toBe("/tmp/andy-sa.json");
|
||||
expect(resolved.config.audienceType).toBe("app-url");
|
||||
});
|
||||
|
||||
it("does not inherit dangerous name matching from accounts.default", () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
channels: {
|
||||
googlechat: {
|
||||
accounts: {
|
||||
default: {
|
||||
dangerouslyAllowNameMatching: true,
|
||||
audienceType: "app-url",
|
||||
audience: "https://example.com/googlechat",
|
||||
},
|
||||
andy: {
|
||||
serviceAccountFile: "/tmp/andy-sa.json",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resolved = resolveGoogleChatAccount({ cfg, accountId: "andy" });
|
||||
expect(resolved.config.dangerouslyAllowNameMatching).toBeUndefined();
|
||||
expect(resolved.config.audienceType).toBe("app-url");
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
createDirectoryTestRuntime,
|
||||
expectDirectorySurface,
|
||||
@@ -7,6 +7,8 @@ import type { OpenClawConfig, PluginRuntime } from "../runtime-api.js";
|
||||
|
||||
const uploadGoogleChatAttachmentMock = vi.hoisted(() => vi.fn());
|
||||
const sendGoogleChatMessageMock = vi.hoisted(() => vi.fn());
|
||||
const resolveGoogleChatAccountMock = vi.hoisted(() => vi.fn());
|
||||
const resolveGoogleChatOutboundSpaceMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("./api.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./api.js")>();
|
||||
@@ -17,9 +19,39 @@ vi.mock("./api.js", async (importOriginal) => {
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./accounts.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./accounts.js")>();
|
||||
return {
|
||||
...actual,
|
||||
resolveGoogleChatAccount: resolveGoogleChatAccountMock,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./targets.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./targets.js")>();
|
||||
return {
|
||||
...actual,
|
||||
resolveGoogleChatOutboundSpace: resolveGoogleChatOutboundSpaceMock,
|
||||
};
|
||||
});
|
||||
|
||||
const accountsActual = await vi.importActual<typeof import("./accounts.js")>("./accounts.js");
|
||||
const targetsActual = await vi.importActual<typeof import("./targets.js")>("./targets.js");
|
||||
|
||||
resolveGoogleChatAccountMock.mockImplementation(accountsActual.resolveGoogleChatAccount);
|
||||
resolveGoogleChatOutboundSpaceMock.mockImplementation(targetsActual.resolveGoogleChatOutboundSpace);
|
||||
|
||||
import { googlechatPlugin } from "./channel.js";
|
||||
import { setGoogleChatRuntime } from "./runtime.js";
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
resolveGoogleChatAccountMock.mockImplementation(accountsActual.resolveGoogleChatAccount);
|
||||
resolveGoogleChatOutboundSpaceMock.mockImplementation(
|
||||
targetsActual.resolveGoogleChatOutboundSpace,
|
||||
);
|
||||
});
|
||||
|
||||
function createGoogleChatCfg(): OpenClawConfig {
|
||||
return {
|
||||
channels: {
|
||||
@@ -162,6 +194,188 @@ describe("googlechatPlugin outbound sendMedia", () => {
|
||||
});
|
||||
});
|
||||
|
||||
const resolveTarget = googlechatPlugin.outbound?.resolveTarget;
|
||||
|
||||
describe("googlechatPlugin outbound resolveTarget", () => {
|
||||
it("resolves valid chat targets", () => {
|
||||
if (!resolveTarget) {
|
||||
throw new Error("Expected googlechatPlugin.outbound.resolveTarget to be defined");
|
||||
}
|
||||
|
||||
const result = resolveTarget({
|
||||
to: "spaces/AAA",
|
||||
mode: "explicit",
|
||||
allowFrom: [],
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) {
|
||||
throw result.error;
|
||||
}
|
||||
expect(result.to).toBe("spaces/AAA");
|
||||
});
|
||||
|
||||
it("resolves email targets", () => {
|
||||
if (!resolveTarget) {
|
||||
throw new Error("Expected googlechatPlugin.outbound.resolveTarget to be defined");
|
||||
}
|
||||
|
||||
const result = resolveTarget({
|
||||
to: "user@example.com",
|
||||
mode: "explicit",
|
||||
allowFrom: [],
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) {
|
||||
throw result.error;
|
||||
}
|
||||
expect(result.to).toBe("users/user@example.com");
|
||||
});
|
||||
|
||||
it("errors on invalid targets", () => {
|
||||
if (!resolveTarget) {
|
||||
throw new Error("Expected googlechatPlugin.outbound.resolveTarget to be defined");
|
||||
}
|
||||
|
||||
const result = resolveTarget({
|
||||
to: " ",
|
||||
mode: "explicit",
|
||||
allowFrom: [],
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
if (result.ok) {
|
||||
throw new Error("Expected invalid target to fail");
|
||||
}
|
||||
expect(result.error).toBeDefined();
|
||||
});
|
||||
|
||||
it("errors when no target is provided", () => {
|
||||
if (!resolveTarget) {
|
||||
throw new Error("Expected googlechatPlugin.outbound.resolveTarget to be defined");
|
||||
}
|
||||
|
||||
const result = resolveTarget({
|
||||
to: undefined,
|
||||
mode: "explicit",
|
||||
allowFrom: [],
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
if (result.ok) {
|
||||
throw new Error("Expected missing target to fail");
|
||||
}
|
||||
expect(result.error).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("googlechatPlugin outbound cfg threading", () => {
|
||||
it("threads resolved cfg into sendText account resolution", async () => {
|
||||
const cfg = {
|
||||
channels: {
|
||||
googlechat: {
|
||||
serviceAccount: {
|
||||
type: "service_account",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const account = {
|
||||
accountId: "default",
|
||||
config: {},
|
||||
credentialSource: "inline",
|
||||
};
|
||||
resolveGoogleChatAccountMock.mockReturnValue(account);
|
||||
resolveGoogleChatOutboundSpaceMock.mockResolvedValue("spaces/AAA");
|
||||
sendGoogleChatMessageMock.mockResolvedValue({
|
||||
messageName: "spaces/AAA/messages/msg-1",
|
||||
});
|
||||
|
||||
await googlechatPlugin.outbound?.sendText?.({
|
||||
cfg: cfg as never,
|
||||
to: "users/123",
|
||||
text: "hello",
|
||||
accountId: "default",
|
||||
});
|
||||
|
||||
expect(resolveGoogleChatAccountMock).toHaveBeenCalledWith({
|
||||
cfg,
|
||||
accountId: "default",
|
||||
});
|
||||
expect(sendGoogleChatMessageMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
account,
|
||||
space: "spaces/AAA",
|
||||
text: "hello",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("threads resolved cfg into sendMedia account and media loading path", async () => {
|
||||
const cfg = {
|
||||
channels: {
|
||||
googlechat: {
|
||||
serviceAccount: {
|
||||
type: "service_account",
|
||||
},
|
||||
mediaMaxMb: 8,
|
||||
},
|
||||
},
|
||||
};
|
||||
const account = {
|
||||
accountId: "default",
|
||||
config: { mediaMaxMb: 20 },
|
||||
credentialSource: "inline",
|
||||
};
|
||||
const { fetchRemoteMedia } = setupRuntimeMediaMocks({
|
||||
loadFileName: "unused.png",
|
||||
loadBytes: "should-not-be-used",
|
||||
});
|
||||
|
||||
resolveGoogleChatAccountMock.mockReturnValue(account);
|
||||
resolveGoogleChatOutboundSpaceMock.mockResolvedValue("spaces/AAA");
|
||||
uploadGoogleChatAttachmentMock.mockResolvedValue({
|
||||
attachmentUploadToken: "token-1",
|
||||
});
|
||||
sendGoogleChatMessageMock.mockResolvedValue({
|
||||
messageName: "spaces/AAA/messages/msg-2",
|
||||
});
|
||||
|
||||
await googlechatPlugin.outbound?.sendMedia?.({
|
||||
cfg: cfg as never,
|
||||
to: "users/123",
|
||||
text: "photo",
|
||||
mediaUrl: "https://example.com/file.png",
|
||||
accountId: "default",
|
||||
});
|
||||
|
||||
expect(resolveGoogleChatAccountMock).toHaveBeenCalledWith({
|
||||
cfg,
|
||||
accountId: "default",
|
||||
});
|
||||
expect(fetchRemoteMedia).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
url: "https://example.com/file.png",
|
||||
maxBytes: 8 * 1024 * 1024,
|
||||
}),
|
||||
);
|
||||
expect(uploadGoogleChatAttachmentMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
account,
|
||||
space: "spaces/AAA",
|
||||
filename: "remote.png",
|
||||
}),
|
||||
);
|
||||
expect(sendGoogleChatMessageMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
account,
|
||||
attachments: [{ attachmentUploadToken: "token-1", contentName: "remote.png" }],
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("googlechat directory", () => {
|
||||
const runtimeEnv = createDirectoryTestRuntime() as never;
|
||||
|
||||
|
||||
@@ -1,235 +0,0 @@
|
||||
import { installCommonResolveTargetErrorCases } from "openclaw/plugin-sdk/testing";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const runtimeMocks = vi.hoisted(() => ({
|
||||
chunkMarkdownText: vi.fn((text: string) => [text]),
|
||||
fetchRemoteMedia: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../runtime-api.js", () => ({
|
||||
getChatChannelMeta: () => ({ id: "googlechat", label: "Google Chat" }),
|
||||
missingTargetError: (provider: string, hint: string) =>
|
||||
new Error(`Delivering to ${provider} requires target ${hint}`),
|
||||
GoogleChatConfigSchema: {},
|
||||
DEFAULT_ACCOUNT_ID: "default",
|
||||
PAIRING_APPROVED_MESSAGE: "Approved",
|
||||
applyAccountNameToChannelSection: vi.fn(),
|
||||
buildChannelConfigSchema: vi.fn(),
|
||||
deleteAccountFromConfigSection: vi.fn(),
|
||||
formatPairingApproveHint: vi.fn(),
|
||||
migrateBaseNameToDefaultAccount: vi.fn(),
|
||||
normalizeAccountId: vi.fn(),
|
||||
resolveChannelMediaMaxBytes: vi.fn(),
|
||||
resolveGoogleChatGroupRequireMention: vi.fn(),
|
||||
setAccountEnabledInConfigSection: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./accounts.js", () => ({
|
||||
listGoogleChatAccountIds: vi.fn(),
|
||||
resolveDefaultGoogleChatAccountId: vi.fn(),
|
||||
resolveGoogleChatAccount: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./actions.js", () => ({
|
||||
googlechatMessageActions: [],
|
||||
}));
|
||||
|
||||
vi.mock("./api.js", () => ({
|
||||
sendGoogleChatMessage: vi.fn(),
|
||||
uploadGoogleChatAttachment: vi.fn(),
|
||||
probeGoogleChat: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./monitor.js", () => ({
|
||||
resolveGoogleChatWebhookPath: vi.fn(),
|
||||
startGoogleChatMonitor: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./setup-core.js", () => ({
|
||||
googlechatSetupAdapter: {},
|
||||
}));
|
||||
|
||||
vi.mock("./setup-surface.js", () => ({
|
||||
googlechatSetupWizard: {},
|
||||
}));
|
||||
|
||||
vi.mock("./runtime.js", () => ({
|
||||
getGoogleChatRuntime: vi.fn(() => ({
|
||||
channel: {
|
||||
text: { chunkMarkdownText: runtimeMocks.chunkMarkdownText },
|
||||
media: { fetchRemoteMedia: runtimeMocks.fetchRemoteMedia },
|
||||
},
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("./targets.js", () => ({
|
||||
normalizeGoogleChatTarget: (raw?: string | null) => {
|
||||
if (!raw?.trim()) return undefined;
|
||||
if (raw === "invalid-target") return undefined;
|
||||
const trimmed = raw.trim().replace(/^(googlechat|google-chat|gchat):/i, "");
|
||||
if (trimmed.startsWith("spaces/")) return trimmed;
|
||||
if (trimmed.includes("@")) return `users/${trimmed.toLowerCase()}`;
|
||||
return `users/${trimmed}`;
|
||||
},
|
||||
isGoogleChatUserTarget: (value: string) => value.startsWith("users/"),
|
||||
isGoogleChatSpaceTarget: (value: string) => value.startsWith("spaces/"),
|
||||
resolveGoogleChatOutboundSpace: vi.fn(),
|
||||
}));
|
||||
|
||||
import { resolveChannelMediaMaxBytes } from "../runtime-api.js";
|
||||
import { resolveGoogleChatAccount } from "./accounts.js";
|
||||
import { sendGoogleChatMessage, uploadGoogleChatAttachment } from "./api.js";
|
||||
import { googlechatPlugin } from "./channel.js";
|
||||
import { resolveGoogleChatOutboundSpace } from "./targets.js";
|
||||
|
||||
const resolveTarget = googlechatPlugin.outbound!.resolveTarget!;
|
||||
|
||||
describe("googlechat resolveTarget", () => {
|
||||
it("should resolve valid target", () => {
|
||||
const result = resolveTarget({
|
||||
to: "spaces/AAA",
|
||||
mode: "explicit",
|
||||
allowFrom: [],
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) {
|
||||
throw result.error;
|
||||
}
|
||||
expect(result.to).toBe("spaces/AAA");
|
||||
});
|
||||
|
||||
it("should resolve email target", () => {
|
||||
const result = resolveTarget({
|
||||
to: "user@example.com",
|
||||
mode: "explicit",
|
||||
allowFrom: [],
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) {
|
||||
throw result.error;
|
||||
}
|
||||
expect(result.to).toBe("users/user@example.com");
|
||||
});
|
||||
|
||||
installCommonResolveTargetErrorCases({
|
||||
resolveTarget,
|
||||
implicitAllowFrom: ["spaces/BBB"],
|
||||
});
|
||||
});
|
||||
|
||||
describe("googlechat outbound cfg threading", () => {
|
||||
beforeEach(() => {
|
||||
runtimeMocks.fetchRemoteMedia.mockReset();
|
||||
runtimeMocks.chunkMarkdownText.mockClear();
|
||||
vi.mocked(resolveGoogleChatAccount).mockReset();
|
||||
vi.mocked(resolveGoogleChatOutboundSpace).mockReset();
|
||||
vi.mocked(resolveChannelMediaMaxBytes).mockReset();
|
||||
vi.mocked(uploadGoogleChatAttachment).mockReset();
|
||||
vi.mocked(sendGoogleChatMessage).mockReset();
|
||||
});
|
||||
|
||||
it("threads resolved cfg into sendText account resolution", async () => {
|
||||
const cfg = {
|
||||
channels: {
|
||||
googlechat: {
|
||||
serviceAccount: {
|
||||
type: "service_account",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const account = {
|
||||
accountId: "default",
|
||||
config: {},
|
||||
credentialSource: "inline",
|
||||
};
|
||||
vi.mocked(resolveGoogleChatAccount).mockReturnValue(account as any);
|
||||
vi.mocked(resolveGoogleChatOutboundSpace).mockResolvedValue("spaces/AAA");
|
||||
vi.mocked(sendGoogleChatMessage).mockResolvedValue({
|
||||
messageName: "spaces/AAA/messages/msg-1",
|
||||
} as any);
|
||||
|
||||
await googlechatPlugin.outbound!.sendText!({
|
||||
cfg: cfg as any,
|
||||
to: "users/123",
|
||||
text: "hello",
|
||||
accountId: "default",
|
||||
});
|
||||
|
||||
expect(resolveGoogleChatAccount).toHaveBeenCalledWith({
|
||||
cfg,
|
||||
accountId: "default",
|
||||
});
|
||||
expect(sendGoogleChatMessage).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
account,
|
||||
space: "spaces/AAA",
|
||||
text: "hello",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("threads resolved cfg into sendMedia account and media loading path", async () => {
|
||||
const cfg = {
|
||||
channels: {
|
||||
googlechat: {
|
||||
serviceAccount: {
|
||||
type: "service_account",
|
||||
},
|
||||
mediaMaxMb: 8,
|
||||
},
|
||||
},
|
||||
};
|
||||
const account = {
|
||||
accountId: "default",
|
||||
config: { mediaMaxMb: 20 },
|
||||
credentialSource: "inline",
|
||||
};
|
||||
vi.mocked(resolveGoogleChatAccount).mockReturnValue(account as any);
|
||||
vi.mocked(resolveGoogleChatOutboundSpace).mockResolvedValue("spaces/AAA");
|
||||
vi.mocked(resolveChannelMediaMaxBytes).mockReturnValue(1024);
|
||||
runtimeMocks.fetchRemoteMedia.mockResolvedValueOnce({
|
||||
buffer: Buffer.from("file"),
|
||||
fileName: "file.png",
|
||||
contentType: "image/png",
|
||||
});
|
||||
vi.mocked(uploadGoogleChatAttachment).mockResolvedValue({
|
||||
attachmentUploadToken: "token-1",
|
||||
} as any);
|
||||
vi.mocked(sendGoogleChatMessage).mockResolvedValue({
|
||||
messageName: "spaces/AAA/messages/msg-2",
|
||||
} as any);
|
||||
|
||||
await googlechatPlugin.outbound!.sendMedia!({
|
||||
cfg: cfg as any,
|
||||
to: "users/123",
|
||||
text: "photo",
|
||||
mediaUrl: "https://example.com/file.png",
|
||||
accountId: "default",
|
||||
});
|
||||
|
||||
expect(resolveGoogleChatAccount).toHaveBeenCalledWith({
|
||||
cfg,
|
||||
accountId: "default",
|
||||
});
|
||||
expect(runtimeMocks.fetchRemoteMedia).toHaveBeenCalledWith({
|
||||
url: "https://example.com/file.png",
|
||||
maxBytes: 1024,
|
||||
});
|
||||
expect(uploadGoogleChatAttachment).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
account,
|
||||
space: "spaces/AAA",
|
||||
filename: "file.png",
|
||||
}),
|
||||
);
|
||||
expect(sendGoogleChatMessage).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
account,
|
||||
attachments: [{ attachmentUploadToken: "token-1", contentName: "file.png" }],
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
waitForStartedMocks,
|
||||
} from "../../../test/helpers/extensions/start-account-lifecycle.js";
|
||||
import type { OpenClawConfig } from "../runtime-api.js";
|
||||
import type { ResolvedGoogleChatAccount } from "./accounts.js";
|
||||
import { resolveGoogleChatAccount, type ResolvedGoogleChatAccount } from "./accounts.js";
|
||||
import { googlechatPlugin } from "./channel.js";
|
||||
import { googlechatSetupAdapter } from "./setup-core.js";
|
||||
|
||||
@@ -184,3 +184,131 @@ describe("googlechat setup", () => {
|
||||
expectLifecyclePatch(patches, { running: false });
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveGoogleChatAccount", () => {
|
||||
it("inherits shared defaults from accounts.default for named accounts", () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
channels: {
|
||||
googlechat: {
|
||||
accounts: {
|
||||
default: {
|
||||
audienceType: "app-url",
|
||||
audience: "https://example.com/googlechat",
|
||||
webhookPath: "/googlechat",
|
||||
},
|
||||
andy: {
|
||||
serviceAccountFile: "/tmp/andy-sa.json",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resolved = resolveGoogleChatAccount({ cfg, accountId: "andy" });
|
||||
expect(resolved.config.audienceType).toBe("app-url");
|
||||
expect(resolved.config.audience).toBe("https://example.com/googlechat");
|
||||
expect(resolved.config.webhookPath).toBe("/googlechat");
|
||||
expect(resolved.config.serviceAccountFile).toBe("/tmp/andy-sa.json");
|
||||
});
|
||||
|
||||
it("prefers top-level and account overrides over accounts.default", () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
channels: {
|
||||
googlechat: {
|
||||
audienceType: "project-number",
|
||||
audience: "1234567890",
|
||||
accounts: {
|
||||
default: {
|
||||
audienceType: "app-url",
|
||||
audience: "https://default.example.com/googlechat",
|
||||
webhookPath: "/googlechat-default",
|
||||
},
|
||||
april: {
|
||||
webhookPath: "/googlechat-april",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resolved = resolveGoogleChatAccount({ cfg, accountId: "april" });
|
||||
expect(resolved.config.audienceType).toBe("project-number");
|
||||
expect(resolved.config.audience).toBe("1234567890");
|
||||
expect(resolved.config.webhookPath).toBe("/googlechat-april");
|
||||
});
|
||||
|
||||
it("does not inherit disabled state from accounts.default for named accounts", () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
channels: {
|
||||
googlechat: {
|
||||
accounts: {
|
||||
default: {
|
||||
enabled: false,
|
||||
audienceType: "app-url",
|
||||
audience: "https://example.com/googlechat",
|
||||
},
|
||||
andy: {
|
||||
serviceAccountFile: "/tmp/andy-sa.json",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resolved = resolveGoogleChatAccount({ cfg, accountId: "andy" });
|
||||
expect(resolved.enabled).toBe(true);
|
||||
expect(resolved.config.enabled).toBeUndefined();
|
||||
expect(resolved.config.audienceType).toBe("app-url");
|
||||
});
|
||||
|
||||
it("does not inherit default-account credentials into named accounts", () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
channels: {
|
||||
googlechat: {
|
||||
accounts: {
|
||||
default: {
|
||||
serviceAccountRef: {
|
||||
source: "env",
|
||||
provider: "test",
|
||||
id: "default-sa",
|
||||
},
|
||||
audienceType: "app-url",
|
||||
audience: "https://example.com/googlechat",
|
||||
},
|
||||
andy: {
|
||||
serviceAccountFile: "/tmp/andy-sa.json",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resolved = resolveGoogleChatAccount({ cfg, accountId: "andy" });
|
||||
expect(resolved.credentialSource).toBe("file");
|
||||
expect(resolved.credentialsFile).toBe("/tmp/andy-sa.json");
|
||||
expect(resolved.config.audienceType).toBe("app-url");
|
||||
});
|
||||
|
||||
it("does not inherit dangerous name matching from accounts.default", () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
channels: {
|
||||
googlechat: {
|
||||
accounts: {
|
||||
default: {
|
||||
dangerouslyAllowNameMatching: true,
|
||||
audienceType: "app-url",
|
||||
audience: "https://example.com/googlechat",
|
||||
},
|
||||
andy: {
|
||||
serviceAccountFile: "/tmp/andy-sa.json",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resolved = resolveGoogleChatAccount({ cfg, accountId: "andy" });
|
||||
expect(resolved.config.dangerouslyAllowNameMatching).toBeUndefined();
|
||||
expect(resolved.config.audienceType).toBe("app-url");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user