diff --git a/src/infra/outbound/outbound.test.ts b/src/infra/outbound/outbound.test.ts index ea9afb231f3..8ec62fc129e 100644 --- a/src/infra/outbound/outbound.test.ts +++ b/src/infra/outbound/outbound.test.ts @@ -2,8 +2,6 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { telegramPlugin } from "../../../extensions/telegram/src/channel.js"; -import { whatsappPlugin } from "../../../extensions/whatsapp/src/channel.js"; import type { ReplyPayload } from "../../auto-reply/types.js"; import type { OpenClawConfig } from "../../config/config.js"; import { setActivePluginRegistry } from "../../plugins/runtime.js"; @@ -39,7 +37,7 @@ import { normalizeOutboundPayloads, normalizeOutboundPayloadsForJson, } from "./payloads.js"; -import { resolveOutboundTarget } from "./targets.js"; +import { runResolveOutboundTargetCoreTests } from "./targets.shared-test.js"; describe("delivery-queue", () => { let tmpDir: string; @@ -914,102 +912,4 @@ describe("formatOutboundPayloadLog", () => { }); }); -describe("resolveOutboundTarget", () => { - beforeEach(() => { - setActivePluginRegistry( - createTestRegistry([ - { pluginId: "whatsapp", plugin: whatsappPlugin, source: "test" }, - { pluginId: "telegram", plugin: telegramPlugin, source: "test" }, - ]), - ); - }); - - afterEach(() => { - setActivePluginRegistry(createTestRegistry()); - }); - - it.each([ - { - name: "normalizes whatsapp target when provided", - input: { channel: "whatsapp" as const, to: " (555) 123-4567 " }, - expected: { ok: true as const, to: "+5551234567" }, - }, - { - name: "keeps whatsapp group targets", - input: { channel: "whatsapp" as const, to: "120363401234567890@g.us" }, - expected: { ok: true as const, to: "120363401234567890@g.us" }, - }, - { - name: "normalizes prefixed/uppercase whatsapp group targets", - input: { - channel: "whatsapp" as const, - to: " WhatsApp:120363401234567890@G.US ", - }, - expected: { ok: true as const, to: "120363401234567890@g.us" }, - }, - { - name: "rejects whatsapp with empty target in explicit mode even with cfg allowFrom", - input: { - channel: "whatsapp" as const, - to: "", - cfg: { channels: { whatsapp: { allowFrom: ["+1555"] } } } as OpenClawConfig, - mode: "explicit" as const, - }, - expectedErrorIncludes: "WhatsApp", - }, - { - name: "rejects whatsapp with empty target and allowFrom (no silent fallback)", - input: { channel: "whatsapp" as const, to: "", allowFrom: ["+1555"] }, - expectedErrorIncludes: "WhatsApp", - }, - { - name: "rejects whatsapp with empty target and prefixed allowFrom (no silent fallback)", - input: { - channel: "whatsapp" as const, - to: "", - allowFrom: ["whatsapp:(555) 123-4567"], - }, - expectedErrorIncludes: "WhatsApp", - }, - { - name: "rejects invalid whatsapp target", - input: { channel: "whatsapp" as const, to: "wat" }, - expectedErrorIncludes: "WhatsApp", - }, - { - name: "rejects whatsapp without to when allowFrom missing", - input: { channel: "whatsapp" as const, to: " " }, - expectedErrorIncludes: "WhatsApp", - }, - { - name: "rejects whatsapp allowFrom fallback when invalid", - input: { channel: "whatsapp" as const, to: "", allowFrom: ["wat"] }, - expectedErrorIncludes: "WhatsApp", - }, - ])("$name", ({ input, expected, expectedErrorIncludes }) => { - const res = resolveOutboundTarget(input); - if (expected) { - expect(res).toEqual(expected); - return; - } - expect(res.ok).toBe(false); - if (!res.ok) { - expect(res.error.message).toContain(expectedErrorIncludes); - } - }); - - it("rejects invalid non-whatsapp targets", () => { - const cases = [ - { input: { channel: "telegram" as const, to: " " }, expectedErrorIncludes: "Telegram" }, - { input: { channel: "webchat" as const, to: "x" }, expectedErrorIncludes: "WebChat" }, - ] as const; - - for (const testCase of cases) { - const res = resolveOutboundTarget(testCase.input); - expect(res.ok).toBe(false); - if (!res.ok) { - expect(res.error.message).toContain(testCase.expectedErrorIncludes); - } - } - }); -}); +runResolveOutboundTargetCoreTests(); diff --git a/src/infra/outbound/targets.shared-test.ts b/src/infra/outbound/targets.shared-test.ts new file mode 100644 index 00000000000..91c2ca9b84d --- /dev/null +++ b/src/infra/outbound/targets.shared-test.ts @@ -0,0 +1,119 @@ +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { telegramPlugin } from "../../../extensions/telegram/src/channel.js"; +import { whatsappPlugin } from "../../../extensions/whatsapp/src/channel.js"; +import { setActivePluginRegistry } from "../../plugins/runtime.js"; +import { createTestRegistry } from "../../test-utils/channel-plugins.js"; +import { resolveOutboundTarget } from "./targets.js"; + +export function installResolveOutboundTargetPluginRegistryHooks(): void { + beforeEach(() => { + setActivePluginRegistry( + createTestRegistry([ + { pluginId: "whatsapp", plugin: whatsappPlugin, source: "test" }, + { pluginId: "telegram", plugin: telegramPlugin, source: "test" }, + ]), + ); + }); + + afterEach(() => { + setActivePluginRegistry(createTestRegistry()); + }); +} + +export function runResolveOutboundTargetCoreTests(): void { + describe("resolveOutboundTarget", () => { + installResolveOutboundTargetPluginRegistryHooks(); + + it("rejects whatsapp with empty target even when allowFrom configured", () => { + const cfg = { + channels: { whatsapp: { allowFrom: ["+1555"] } }, + }; + const res = resolveOutboundTarget({ + channel: "whatsapp", + to: "", + cfg, + mode: "explicit", + }); + expect(res.ok).toBe(false); + if (!res.ok) { + expect(res.error.message).toContain("WhatsApp"); + } + }); + + it.each([ + { + name: "normalizes whatsapp target when provided", + input: { channel: "whatsapp" as const, to: " (555) 123-4567 " }, + expected: { ok: true as const, to: "+5551234567" }, + }, + { + name: "keeps whatsapp group targets", + input: { channel: "whatsapp" as const, to: "120363401234567890@g.us" }, + expected: { ok: true as const, to: "120363401234567890@g.us" }, + }, + { + name: "normalizes prefixed/uppercase whatsapp group targets", + input: { + channel: "whatsapp" as const, + to: " WhatsApp:120363401234567890@G.US ", + }, + expected: { ok: true as const, to: "120363401234567890@g.us" }, + }, + { + name: "rejects whatsapp with empty target and allowFrom (no silent fallback)", + input: { channel: "whatsapp" as const, to: "", allowFrom: ["+1555"] }, + expectedErrorIncludes: "WhatsApp", + }, + { + name: "rejects whatsapp with empty target and prefixed allowFrom (no silent fallback)", + input: { + channel: "whatsapp" as const, + to: "", + allowFrom: ["whatsapp:(555) 123-4567"], + }, + expectedErrorIncludes: "WhatsApp", + }, + { + name: "rejects invalid whatsapp target", + input: { channel: "whatsapp" as const, to: "wat" }, + expectedErrorIncludes: "WhatsApp", + }, + { + name: "rejects whatsapp without to when allowFrom missing", + input: { channel: "whatsapp" as const, to: " " }, + expectedErrorIncludes: "WhatsApp", + }, + { + name: "rejects whatsapp allowFrom fallback when invalid", + input: { channel: "whatsapp" as const, to: "", allowFrom: ["wat"] }, + expectedErrorIncludes: "WhatsApp", + }, + ])("$name", ({ input, expected, expectedErrorIncludes }) => { + const res = resolveOutboundTarget(input); + if (expected) { + expect(res).toEqual(expected); + return; + } + expect(res.ok).toBe(false); + if (!res.ok) { + expect(res.error.message).toContain(expectedErrorIncludes); + } + }); + + it("rejects telegram with missing target", () => { + const res = resolveOutboundTarget({ channel: "telegram", to: " " }); + expect(res.ok).toBe(false); + if (!res.ok) { + expect(res.error.message).toContain("Telegram"); + } + }); + + it("rejects webchat delivery", () => { + const res = resolveOutboundTarget({ channel: "webchat", to: "x" }); + expect(res.ok).toBe(false); + if (!res.ok) { + expect(res.error.message).toContain("WebChat"); + } + }); + }); +} diff --git a/src/infra/outbound/targets.test.ts b/src/infra/outbound/targets.test.ts index e4649c3c07d..ac9fa08b1e7 100644 --- a/src/infra/outbound/targets.test.ts +++ b/src/infra/outbound/targets.test.ts @@ -1,22 +1,56 @@ -import { beforeEach, describe, expect, it } from "vitest"; -import { telegramPlugin } from "../../../extensions/telegram/src/channel.js"; -import { whatsappPlugin } from "../../../extensions/whatsapp/src/channel.js"; +import { describe, expect, it } from "vitest"; import type { OpenClawConfig } from "../../config/config.js"; -import { setActivePluginRegistry } from "../../plugins/runtime.js"; -import { createTestRegistry } from "../../test-utils/channel-plugins.js"; import { resolveOutboundTarget, resolveSessionDeliveryTarget } from "./targets.js"; +import { + installResolveOutboundTargetPluginRegistryHooks, + runResolveOutboundTargetCoreTests, +} from "./targets.shared-test.js"; -describe("resolveOutboundTarget", () => { - beforeEach(() => { - setActivePluginRegistry( - createTestRegistry([ - { pluginId: "whatsapp", plugin: whatsappPlugin, source: "test" }, - { pluginId: "telegram", plugin: telegramPlugin, source: "test" }, - ]), - ); +runResolveOutboundTargetCoreTests(); + +describe("resolveOutboundTarget defaultTo config fallback", () => { + installResolveOutboundTargetPluginRegistryHooks(); + + it("uses whatsapp defaultTo when no explicit target is provided", () => { + const cfg: OpenClawConfig = { + channels: { whatsapp: { defaultTo: "+15551234567", allowFrom: ["*"] } }, + }; + const res = resolveOutboundTarget({ + channel: "whatsapp", + to: undefined, + cfg, + mode: "implicit", + }); + expect(res).toEqual({ ok: true, to: "+15551234567" }); }); - it("rejects whatsapp with empty target even when allowFrom configured", () => { + it("uses telegram defaultTo when no explicit target is provided", () => { + const cfg: OpenClawConfig = { + channels: { telegram: { defaultTo: "123456789" } }, + }; + const res = resolveOutboundTarget({ + channel: "telegram", + to: "", + cfg, + mode: "implicit", + }); + expect(res).toEqual({ ok: true, to: "123456789" }); + }); + + it("explicit --reply-to overrides defaultTo", () => { + const cfg: OpenClawConfig = { + channels: { whatsapp: { defaultTo: "+15551234567", allowFrom: ["*"] } }, + }; + const res = resolveOutboundTarget({ + channel: "whatsapp", + to: "+15559999999", + cfg, + mode: "explicit", + }); + expect(res).toEqual({ ok: true, to: "+15559999999" }); + }); + + it("still errors when no defaultTo and no explicit target", () => { const cfg: OpenClawConfig = { channels: { whatsapp: { allowFrom: ["+1555"] } }, }; @@ -24,142 +58,9 @@ describe("resolveOutboundTarget", () => { channel: "whatsapp", to: "", cfg, - mode: "explicit", + mode: "implicit", }); expect(res.ok).toBe(false); - if (!res.ok) { - expect(res.error.message).toContain("WhatsApp"); - } - }); - - it.each([ - { - name: "normalizes whatsapp target when provided", - input: { channel: "whatsapp" as const, to: " (555) 123-4567 " }, - expected: { ok: true as const, to: "+5551234567" }, - }, - { - name: "keeps whatsapp group targets", - input: { channel: "whatsapp" as const, to: "120363401234567890@g.us" }, - expected: { ok: true as const, to: "120363401234567890@g.us" }, - }, - { - name: "normalizes prefixed/uppercase whatsapp group targets", - input: { - channel: "whatsapp" as const, - to: " WhatsApp:120363401234567890@G.US ", - }, - expected: { ok: true as const, to: "120363401234567890@g.us" }, - }, - { - name: "rejects whatsapp with empty target and allowFrom (no silent fallback)", - input: { channel: "whatsapp" as const, to: "", allowFrom: ["+1555"] }, - expectedErrorIncludes: "WhatsApp", - }, - { - name: "rejects whatsapp with empty target and prefixed allowFrom (no silent fallback)", - input: { - channel: "whatsapp" as const, - to: "", - allowFrom: ["whatsapp:(555) 123-4567"], - }, - expectedErrorIncludes: "WhatsApp", - }, - { - name: "rejects invalid whatsapp target", - input: { channel: "whatsapp" as const, to: "wat" }, - expectedErrorIncludes: "WhatsApp", - }, - { - name: "rejects whatsapp without to when allowFrom missing", - input: { channel: "whatsapp" as const, to: " " }, - expectedErrorIncludes: "WhatsApp", - }, - { - name: "rejects whatsapp allowFrom fallback when invalid", - input: { channel: "whatsapp" as const, to: "", allowFrom: ["wat"] }, - expectedErrorIncludes: "WhatsApp", - }, - ])("$name", ({ input, expected, expectedErrorIncludes }) => { - const res = resolveOutboundTarget(input); - if (expected) { - expect(res).toEqual(expected); - return; - } - expect(res.ok).toBe(false); - if (!res.ok) { - expect(res.error.message).toContain(expectedErrorIncludes); - } - }); - - it("rejects telegram with missing target", () => { - const res = resolveOutboundTarget({ channel: "telegram", to: " " }); - expect(res.ok).toBe(false); - if (!res.ok) { - expect(res.error.message).toContain("Telegram"); - } - }); - - it("rejects webchat delivery", () => { - const res = resolveOutboundTarget({ channel: "webchat", to: "x" }); - expect(res.ok).toBe(false); - if (!res.ok) { - expect(res.error.message).toContain("WebChat"); - } - }); - - describe("defaultTo config fallback", () => { - it("uses whatsapp defaultTo when no explicit target is provided", () => { - const cfg: OpenClawConfig = { - channels: { whatsapp: { defaultTo: "+15551234567", allowFrom: ["*"] } }, - }; - const res = resolveOutboundTarget({ - channel: "whatsapp", - to: undefined, - cfg, - mode: "implicit", - }); - expect(res).toEqual({ ok: true, to: "+15551234567" }); - }); - - it("uses telegram defaultTo when no explicit target is provided", () => { - const cfg: OpenClawConfig = { - channels: { telegram: { defaultTo: "123456789" } }, - }; - const res = resolveOutboundTarget({ - channel: "telegram", - to: "", - cfg, - mode: "implicit", - }); - expect(res).toEqual({ ok: true, to: "123456789" }); - }); - - it("explicit --reply-to overrides defaultTo", () => { - const cfg: OpenClawConfig = { - channels: { whatsapp: { defaultTo: "+15551234567", allowFrom: ["*"] } }, - }; - const res = resolveOutboundTarget({ - channel: "whatsapp", - to: "+15559999999", - cfg, - mode: "explicit", - }); - expect(res).toEqual({ ok: true, to: "+15559999999" }); - }); - - it("still errors when no defaultTo and no explicit target", () => { - const cfg: OpenClawConfig = { - channels: { whatsapp: { allowFrom: ["+1555"] } }, - }; - const res = resolveOutboundTarget({ - channel: "whatsapp", - to: "", - cfg, - mode: "implicit", - }); - expect(res.ok).toBe(false); - }); }); });