| import { beforeEach, describe, expect, it, vi } from "vitest"; |
|
|
| const mocks = vi.hoisted(() => ({ |
| getChannelPlugin: vi.fn(), |
| resolveOutboundTarget: vi.fn(), |
| deliverOutboundPayloads: vi.fn(), |
| loadOpenClawPlugins: vi.fn(), |
| })); |
|
|
| vi.mock("../../channels/plugins/index.js", () => ({ |
| normalizeChannelId: (channel?: string) => channel?.trim().toLowerCase() ?? undefined, |
| getChannelPlugin: mocks.getChannelPlugin, |
| listChannelPlugins: () => [], |
| })); |
|
|
| vi.mock("../../agents/agent-scope.js", () => ({ |
| resolveDefaultAgentId: () => "main", |
| resolveSessionAgentId: ({ |
| sessionKey, |
| }: { |
| sessionKey?: string; |
| config?: unknown; |
| agentId?: string; |
| }) => { |
| const match = sessionKey?.match(/^agent:([^:]+)/i); |
| return match?.[1] ?? "main"; |
| }, |
| resolveAgentWorkspaceDir: () => "/tmp/openclaw-test-workspace", |
| })); |
|
|
| vi.mock("../../config/plugin-auto-enable.js", () => ({ |
| applyPluginAutoEnable: ({ config }: { config: unknown }) => ({ config, changes: [] }), |
| })); |
|
|
| vi.mock("../../plugins/loader.js", () => ({ |
| loadOpenClawPlugins: mocks.loadOpenClawPlugins, |
| })); |
|
|
| vi.mock("./targets.js", () => ({ |
| resolveOutboundTarget: mocks.resolveOutboundTarget, |
| })); |
|
|
| vi.mock("./deliver.js", () => ({ |
| deliverOutboundPayloads: mocks.deliverOutboundPayloads, |
| })); |
|
|
| import { setActivePluginRegistry } from "../../plugins/runtime.js"; |
| import { createTestRegistry } from "../../test-utils/channel-plugins.js"; |
| import { sendMessage } from "./message.js"; |
|
|
| describe("sendMessage", () => { |
| beforeEach(() => { |
| setActivePluginRegistry(createTestRegistry([])); |
| mocks.getChannelPlugin.mockClear(); |
| mocks.resolveOutboundTarget.mockClear(); |
| mocks.deliverOutboundPayloads.mockClear(); |
| mocks.loadOpenClawPlugins.mockClear(); |
|
|
| mocks.getChannelPlugin.mockReturnValue({ |
| outbound: { deliveryMode: "direct" }, |
| }); |
| mocks.resolveOutboundTarget.mockImplementation(({ to }: { to: string }) => ({ ok: true, to })); |
| mocks.deliverOutboundPayloads.mockResolvedValue([{ channel: "mattermost", messageId: "m1" }]); |
| }); |
|
|
| it("passes explicit agentId to outbound delivery for scoped media roots", async () => { |
| await sendMessage({ |
| cfg: {}, |
| channel: "telegram", |
| to: "123456", |
| content: "hi", |
| agentId: "work", |
| }); |
|
|
| expect(mocks.deliverOutboundPayloads).toHaveBeenCalledWith( |
| expect.objectContaining({ |
| session: expect.objectContaining({ agentId: "work" }), |
| channel: "telegram", |
| to: "123456", |
| }), |
| ); |
| }); |
|
|
| it("propagates the send idempotency key into mirrored transcript delivery", async () => { |
| await sendMessage({ |
| cfg: {}, |
| channel: "telegram", |
| to: "123456", |
| content: "hi", |
| idempotencyKey: "idem-send-1", |
| mirror: { |
| sessionKey: "agent:main:telegram:dm:123456", |
| }, |
| }); |
|
|
| expect(mocks.deliverOutboundPayloads).toHaveBeenCalledWith( |
| expect.objectContaining({ |
| mirror: expect.objectContaining({ |
| sessionKey: "agent:main:telegram:dm:123456", |
| text: "hi", |
| idempotencyKey: "idem-send-1", |
| }), |
| }), |
| ); |
| }); |
|
|
| it("recovers telegram plugin resolution so message/send does not fail with Unknown channel: telegram", async () => { |
| const telegramPlugin = { |
| outbound: { deliveryMode: "direct" }, |
| }; |
| mocks.getChannelPlugin |
| .mockReturnValueOnce(undefined) |
| .mockReturnValueOnce(telegramPlugin) |
| .mockReturnValue(telegramPlugin); |
|
|
| await expect( |
| sendMessage({ |
| cfg: { channels: { telegram: { botToken: "test-token" } } }, |
| channel: "telegram", |
| to: "123456", |
| content: "hi", |
| }), |
| ).resolves.toMatchObject({ |
| channel: "telegram", |
| to: "123456", |
| via: "direct", |
| }); |
|
|
| expect(mocks.loadOpenClawPlugins).toHaveBeenCalledTimes(1); |
| }); |
| }); |
|
|