Telegram: fix extension-fast test seams

This commit is contained in:
Gustavo Madeira Santana
2026-03-29 21:54:16 -04:00
parent 5e4a64848f
commit acea28a9bb
4 changed files with 49 additions and 88 deletions

View File

@@ -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<typeof subsystemModule.createSubsystemLogger>;
return logger as unknown as ReturnType<typeof runtimeEnvModule.createSubsystemLogger>;
});
});

View File

@@ -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<typeof import("../../../src/config/sessions.js")>();
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),

View File

@@ -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<string, unknown>) {
} 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<typeof vi.fn>,
): ReturnType<typeof vi.fn> {
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!({

View File

@@ -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<SlackPrepareResult>;
}>("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<string, unknown>;
}): Promise<{ ctxPayload: MsgContext } | null | undefined> {
const telegramHarnessModule = (await import(telegramHarnessModuleId)) as {
buildTelegramMessageContextForTest: (params: {
cfg: OpenClawConfig;
message: Record<string, unknown>;
}) => 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<string, unknown>;
}) => 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<object>();
return {
...actual,