From acea28a9bb1651ebfe67bd6e48d272b45cc8c00e Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Sun, 29 Mar 2026 21:54:16 -0400 Subject: [PATCH] Telegram: fix extension-fast test seams --- extensions/telegram/src/accounts.test.ts | 6 +- .../bot-native-commands.session-meta.test.ts | 22 +++--- extensions/telegram/src/channel.test.ts | 69 ++++++++----------- test/helpers/channels/inbound-contract.ts | 40 +++-------- 4 files changed, 49 insertions(+), 88 deletions(-) diff --git a/extensions/telegram/src/accounts.test.ts b/extensions/telegram/src/accounts.test.ts index fd8e5421c5f..d012301f8b0 100644 --- a/extensions/telegram/src/accounts.test.ts +++ b/extensions/telegram/src/accounts.test.ts @@ -1,5 +1,5 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; -import * as subsystemModule from "openclaw/plugin-sdk/logging-core"; +import * as runtimeEnvModule from "openclaw/plugin-sdk/runtime-env"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { withEnv } from "../../../test/helpers/plugins/env.js"; import { @@ -32,12 +32,12 @@ function resolveAccountWithEnv( beforeEach(() => { vi.restoreAllMocks(); - vi.spyOn(subsystemModule, "createSubsystemLogger").mockImplementation(() => { + vi.spyOn(runtimeEnvModule, "createSubsystemLogger").mockImplementation(() => { const logger = { warn: warnMock, child: () => logger, }; - return logger as unknown as ReturnType; + return logger as unknown as ReturnType; }); }); diff --git a/extensions/telegram/src/bot-native-commands.session-meta.test.ts b/extensions/telegram/src/bot-native-commands.session-meta.test.ts index 3284c823ae0..d850ba64181 100644 --- a/extensions/telegram/src/bot-native-commands.session-meta.test.ts +++ b/extensions/telegram/src/bot-native-commands.session-meta.test.ts @@ -119,23 +119,17 @@ vi.mock("openclaw/plugin-sdk/reply-runtime", async (importOriginal) => { dispatchReplyWithBufferedBlockDispatcher: replyMocks.dispatchReplyWithBufferedBlockDispatcher, }; }); -vi.mock("../../../src/config/sessions.js", () => ({ - recordSessionMetaFromInbound: sessionMocks.recordSessionMetaFromInbound, - resolveStorePath: sessionMocks.resolveStorePath, -})); +vi.mock("../../../src/config/sessions.js", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + recordSessionMetaFromInbound: sessionMocks.recordSessionMetaFromInbound, + resolveStorePath: sessionMocks.resolveStorePath, + }; +}); vi.mock("../../../src/pairing/pairing-store.js", () => ({ readChannelAllowFromStore: vi.fn(async () => []), })); -vi.mock("openclaw/plugin-sdk/conversation-runtime", () => ({ - getSessionBindingService: () => ({ - bind: vi.fn(), - getCapabilities: vi.fn(), - listBySession: vi.fn(), - resolveByConversation: (ref: unknown) => sessionBindingMocks.resolveByConversation(ref), - touch: (bindingId: string, at?: number) => sessionBindingMocks.touch(bindingId, at), - unbind: vi.fn(), - }), -})); vi.mock("../../../src/plugins/commands.js", () => ({ getPluginCommandSpecs: vi.fn(() => []), matchPluginCommand: vi.fn(() => null), diff --git a/extensions/telegram/src/channel.test.ts b/extensions/telegram/src/channel.test.ts index b105bce01d2..2f4b41cfef5 100644 --- a/extensions/telegram/src/channel.test.ts +++ b/extensions/telegram/src/channel.test.ts @@ -8,6 +8,8 @@ import { telegramPlugin } from "./channel.js"; import * as monitorModule from "./monitor.js"; import * as probeModule from "./probe.js"; import { clearTelegramRuntime, setTelegramRuntime } from "./runtime.js"; +import * as sendModule from "./send.js"; +import * as tokenModule from "./token.js"; const probeTelegramMock = vi.hoisted(() => vi.fn()); const collectTelegramUnmentionedGroupIdsMock = vi.hoisted(() => vi.fn()); @@ -78,16 +80,7 @@ function installTelegramRuntime(telegram?: Record) { } as unknown as PluginRuntime); } -function installGatewayRuntime(params?: { - probeOk?: boolean; - botUsername?: string; - runtimeHelpers?: { - probeTelegram?: typeof probeModule.probeTelegram; - collectTelegramUnmentionedGroupIds?: typeof auditModule.collectTelegramUnmentionedGroupIds; - auditTelegramGroupMembership?: typeof auditModule.auditTelegramGroupMembership; - monitorTelegramProvider?: typeof monitorModule.monitorTelegramProvider; - }; -}) { +function installGatewayRuntime(params?: { probeOk?: boolean; botUsername?: string }) { const monitorTelegramProvider = vi .spyOn(monitorModule, "monitorTelegramProvider") .mockImplementation(async () => undefined); @@ -115,15 +108,6 @@ function installGatewayRuntime(params?: { groups: [], elapsedMs: 0, })); - installTelegramRuntime({ - probeTelegram: params?.runtimeHelpers?.probeTelegram ?? probeTelegram, - collectTelegramUnmentionedGroupIds: - params?.runtimeHelpers?.collectTelegramUnmentionedGroupIds ?? collectUnmentionedGroupIds, - auditTelegramGroupMembership: - params?.runtimeHelpers?.auditTelegramGroupMembership ?? auditGroupMembership, - monitorTelegramProvider: - params?.runtimeHelpers?.monitorTelegramProvider ?? monitorTelegramProvider, - }); return { monitorTelegramProvider, probeTelegram, @@ -152,17 +136,18 @@ function createOpsProxyAccount() { }; } -function installSendMessageRuntime( +function installSendMessageSpy( sendMessageTelegram: ReturnType, -): ReturnType { - installTelegramRuntime({ - sendMessageTelegram, - }); +): typeof sendMessageTelegram { + vi.spyOn(sendModule, "sendMessageTelegram").mockImplementation( + sendMessageTelegram as typeof sendModule.sendMessageTelegram, + ); return sendMessageTelegram; } afterEach(() => { clearTelegramRuntime(); + vi.restoreAllMocks(); vi.clearAllMocks(); }); @@ -307,7 +292,7 @@ describe("telegramPlugin duplicate token guard", () => { } }); - it("prefers runtime Telegram helpers over imported module mocks when runtime is set", async () => { + it("uses imported Telegram probe helpers even when runtime state is set", async () => { probeTelegramMock.mockReset(); const runtimeProbeTelegram = vi.fn(async () => ({ ok: true, @@ -334,16 +319,16 @@ describe("telegramPlugin duplicate token guard", () => { }), ).resolves.toEqual({ ok: true, - bot: { username: "runtimebot" }, - elapsedMs: 7, + bot: { username: "modulebot" }, + elapsedMs: 1, }); - expect(runtimeProbeTelegram).toHaveBeenCalledWith("token-ops", 4321, { + expect(probeTelegramMock).toHaveBeenCalledWith("token-ops", 4321, { accountId: "ops", proxyUrl: undefined, network: undefined, apiRoot: undefined, }); - expect(probeTelegramMock).not.toHaveBeenCalled(); + expect(runtimeProbeTelegram).not.toHaveBeenCalled(); }); it("passes account proxy and network settings into Telegram probes", async () => { @@ -420,8 +405,8 @@ describe("telegramPlugin duplicate token guard", () => { }); it("forwards mediaLocalRoots to sendMessageTelegram for outbound media sends", async () => { - const sendMessageTelegram = installSendMessageRuntime( - vi.fn(async () => ({ messageId: "tg-1" })), + const sendMessageTelegram = installSendMessageSpy( + vi.fn(async () => ({ messageId: "tg-1", chatId: "12345" })), ); const result = await telegramPlugin.outbound!.sendMedia!({ @@ -445,8 +430,8 @@ describe("telegramPlugin duplicate token guard", () => { }); it("preserves buttons for outbound text payload sends", async () => { - const sendMessageTelegram = installSendMessageRuntime( - vi.fn(async () => ({ messageId: "tg-2" })), + const sendMessageTelegram = installSendMessageSpy( + vi.fn(async () => ({ messageId: "tg-2", chatId: "12345" })), ); const result = await telegramPlugin.outbound!.sendPayload!({ @@ -475,13 +460,15 @@ describe("telegramPlugin duplicate token guard", () => { }); it("preserves accountId for pairing approval sends", async () => { - const sendMessageTelegram = vi.fn(async () => ({ messageId: "tg-pair" })); + const sendMessageTelegram = vi.fn(async () => ({ messageId: "tg-pair", chatId: "12345" })); const resolveTelegramToken = vi.fn(() => ({ token: "token-ops", source: "config" })); const cfg = createCfg(); - installTelegramRuntime({ - sendMessageTelegram, - resolveTelegramToken, - }); + vi.spyOn(sendModule, "sendMessageTelegram").mockImplementation( + sendMessageTelegram as typeof sendModule.sendMessageTelegram, + ); + vi.spyOn(tokenModule, "resolveTelegramToken").mockImplementation( + resolveTelegramToken as typeof tokenModule.resolveTelegramToken, + ); await telegramPlugin.pairing?.notifyApproval?.({ cfg, @@ -503,7 +490,7 @@ describe("telegramPlugin duplicate token guard", () => { }); it("sends outbound payload media lists and keeps buttons on the first message only", async () => { - const sendMessageTelegram = installSendMessageRuntime( + const sendMessageTelegram = installSendMessageSpy( vi .fn() .mockResolvedValueOnce({ messageId: "tg-3", chatId: "12345" }) @@ -690,8 +677,8 @@ describe("telegramPlugin duplicate token guard", () => { describe("telegramPlugin outbound sendPayload forceDocument", () => { it("forwards forceDocument to the underlying send call when channelData is present", async () => { - const sendMessageTelegram = installSendMessageRuntime( - vi.fn(async () => ({ messageId: "tg-fd" })), + const sendMessageTelegram = installSendMessageSpy( + vi.fn(async () => ({ messageId: "tg-fd", chatId: "12345" })), ); await telegramPlugin.outbound!.sendPayload!({ diff --git a/test/helpers/channels/inbound-contract.ts b/test/helpers/channels/inbound-contract.ts index 7bb275b493c..76ca415eccf 100644 --- a/test/helpers/channels/inbound-contract.ts +++ b/test/helpers/channels/inbound-contract.ts @@ -4,10 +4,7 @@ import { inboundCtxCapture } from "../../../src/channels/plugins/contracts/inbou import { expectChannelInboundContextContract } from "../../../src/channels/plugins/contracts/suites.js"; import type { OpenClawConfig } from "../../../src/config/config.js"; import type { ResolvedSlackAccount } from "../../../src/plugin-sdk/slack.js"; -import { - loadBundledPluginTestApiSync, - resolveRelativeBundledPluginPublicModuleId, -} from "../../../src/test-utils/bundled-plugin-public-surface.js"; +import { loadBundledPluginTestApiSync } from "../../../src/test-utils/bundled-plugin-public-surface.js"; import { withTempHome } from "../temp-home.js"; type SlackMessageEvent = { @@ -36,34 +33,17 @@ const { createInboundSlackTestContext, prepareSlackMessage } = loadBundledPlugin opts: { source: string }; }) => Promise; }>("slack"); -const { telegramHarnessModuleId, signalApiModuleId, whatsAppTestApiModuleId } = vi.hoisted(() => ({ - telegramHarnessModuleId: resolveRelativeBundledPluginPublicModuleId({ - fromModuleUrl: import.meta.url, - pluginId: "telegram", - artifactBasename: "src/bot-message-context.test-harness.js", - }), - signalApiModuleId: resolveRelativeBundledPluginPublicModuleId({ - fromModuleUrl: import.meta.url, - pluginId: "signal", - artifactBasename: "api.js", - }), - whatsAppTestApiModuleId: resolveRelativeBundledPluginPublicModuleId({ - fromModuleUrl: import.meta.url, - pluginId: "whatsapp", - artifactBasename: "test-api.js", - }), -})); - async function buildTelegramMessageContextForTest(params: { cfg: OpenClawConfig; message: Record; }): Promise<{ ctxPayload: MsgContext } | null | undefined> { - const telegramHarnessModule = (await import(telegramHarnessModuleId)) as { - buildTelegramMessageContextForTest: (params: { - cfg: OpenClawConfig; - message: Record; - }) => Promise<{ ctxPayload: MsgContext } | null | undefined>; - }; + const telegramHarnessModule = + (await import("../../../extensions/telegram/src/bot-message-context.test-harness.js")) as { + buildTelegramMessageContextForTest: (params: { + cfg: OpenClawConfig; + message: Record; + }) => Promise<{ ctxPayload: MsgContext } | null | undefined>; + }; return await telegramHarnessModule.buildTelegramMessageContextForTest(params); } @@ -108,7 +88,7 @@ vi.mock("openclaw/plugin-sdk/conversation-runtime", async (importOriginal) => { }; }); -vi.mock(signalApiModuleId, () => ({ +vi.mock("../../../extensions/signal/api.js", () => ({ sendMessageSignal: vi.fn(), sendTypingSignal: vi.fn(async () => true), sendReadReceiptSignal: vi.fn(async () => true), @@ -119,7 +99,7 @@ vi.mock("../../../src/pairing/pairing-store.js", () => ({ upsertChannelPairingRequest: vi.fn(), })); -vi.mock(whatsAppTestApiModuleId, async (importOriginal) => { +vi.mock("../../../extensions/whatsapp/test-api.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual,