Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
288
extensions/telegram/src/bot-native-commands.registry.test.ts
Normal file
288
extensions/telegram/src/bot-native-commands.registry.test.ts
Normal file
@@ -0,0 +1,288 @@
|
||||
// Telegram tests cover bot native commands.registry plugin behavior.
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import { clearPluginCommands, registerPluginCommand } from "openclaw/plugin-sdk/plugin-runtime";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
let registerTelegramNativeCommands: typeof import("./bot-native-commands.js").registerTelegramNativeCommands;
|
||||
let setActivePluginRegistry: typeof import("openclaw/plugin-sdk/plugin-test-runtime").setActivePluginRegistry;
|
||||
let createCommandBot: typeof import("./bot-native-commands.menu-test-support.js").createCommandBot;
|
||||
let createNativeCommandTestParams: typeof import("./bot-native-commands.menu-test-support.js").createNativeCommandTestParams;
|
||||
let createPrivateCommandContext: typeof import("./bot-native-commands.menu-test-support.js").createPrivateCommandContext;
|
||||
let deliverReplies: typeof import("./bot-native-commands.menu-test-support.js").deliverReplies;
|
||||
let editMessageTelegram: typeof import("./bot-native-commands.menu-test-support.js").editMessageTelegram;
|
||||
let resetNativeCommandMenuMocks: typeof import("./bot-native-commands.menu-test-support.js").resetNativeCommandMenuMocks;
|
||||
let waitForRegisteredCommands: typeof import("./bot-native-commands.menu-test-support.js").waitForRegisteredCommands;
|
||||
|
||||
function createTelegramPluginRegistry() {
|
||||
return {
|
||||
plugins: [],
|
||||
tools: [],
|
||||
hooks: [],
|
||||
typedHooks: [],
|
||||
channels: [
|
||||
{
|
||||
pluginId: "telegram",
|
||||
source: "test",
|
||||
plugin: {
|
||||
id: "telegram",
|
||||
meta: {
|
||||
id: "telegram",
|
||||
label: "Telegram",
|
||||
selectionLabel: "Telegram",
|
||||
docsPath: "/channels/telegram",
|
||||
blurb: "test stub.",
|
||||
},
|
||||
capabilities: { chatTypes: ["direct"] },
|
||||
config: {
|
||||
listAccountIds: () => ["default"],
|
||||
resolveAccount: () => ({}),
|
||||
},
|
||||
commands: {
|
||||
nativeCommandsAutoEnabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
channelSetups: [
|
||||
{
|
||||
pluginId: "telegram",
|
||||
source: "test",
|
||||
enabled: true,
|
||||
plugin: {
|
||||
id: "telegram",
|
||||
},
|
||||
},
|
||||
],
|
||||
providers: [],
|
||||
speechProviders: [],
|
||||
mediaUnderstandingProviders: [],
|
||||
imageGenerationProviders: [],
|
||||
videoGenerationProviders: [],
|
||||
webFetchProviders: [],
|
||||
webSearchProviders: [],
|
||||
migrationProviders: [],
|
||||
gatewayHandlers: {},
|
||||
httpRoutes: [],
|
||||
cliRegistrars: [],
|
||||
services: [],
|
||||
commands: [],
|
||||
conversationBindingResolvedHandlers: [],
|
||||
diagnostics: [],
|
||||
};
|
||||
}
|
||||
|
||||
function registerPairPluginCommand(params?: {
|
||||
nativeNames?: { telegram?: string; discord?: string };
|
||||
nativeProgressMessages?: { telegram?: string; default?: string };
|
||||
}) {
|
||||
expect(
|
||||
registerPluginCommand("demo-plugin", {
|
||||
name: "pair",
|
||||
...(params?.nativeNames ? { nativeNames: params.nativeNames } : {}),
|
||||
...(params?.nativeProgressMessages
|
||||
? { nativeProgressMessages: params.nativeProgressMessages }
|
||||
: {}),
|
||||
description: "Pair device",
|
||||
acceptsArgs: true,
|
||||
requireAuth: false,
|
||||
handler: async ({ args }) => ({ text: `paired:${args ?? ""}` }),
|
||||
}),
|
||||
).toEqual({ ok: true });
|
||||
}
|
||||
|
||||
async function registerPairMenu(params: {
|
||||
bot: ReturnType<typeof createCommandBot>["bot"];
|
||||
setMyCommands: ReturnType<typeof createCommandBot>["setMyCommands"];
|
||||
nativeNames?: { telegram?: string; discord?: string };
|
||||
nativeProgressMessages?: { telegram?: string; default?: string };
|
||||
}) {
|
||||
registerPairPluginCommand({
|
||||
...(params.nativeNames ? { nativeNames: params.nativeNames } : {}),
|
||||
...(params.nativeProgressMessages
|
||||
? { nativeProgressMessages: params.nativeProgressMessages }
|
||||
: {}),
|
||||
});
|
||||
|
||||
registerTelegramNativeCommands({
|
||||
...createNativeCommandTestParams({}),
|
||||
bot: params.bot,
|
||||
});
|
||||
|
||||
return await waitForRegisteredCommands(params.setMyCommands);
|
||||
}
|
||||
|
||||
function requireCommandHandler(
|
||||
commandHandlers: ReturnType<typeof createCommandBot>["commandHandlers"],
|
||||
commandName: string,
|
||||
) {
|
||||
const handler = commandHandlers.get(commandName);
|
||||
if (!handler) {
|
||||
throw new Error(`expected ${commandName} command handler`);
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
function expectRegisteredCommand(
|
||||
commands: Array<{ command: string; description: string }>,
|
||||
expected: { command: string; description: string },
|
||||
): void {
|
||||
expect(
|
||||
commands.some(
|
||||
(command) =>
|
||||
command.command === expected.command && command.description === expected.description,
|
||||
),
|
||||
).toBe(true);
|
||||
}
|
||||
|
||||
function expectLastDeliveredReplyText(text: string): void {
|
||||
const calls = deliverReplies.mock.calls as unknown[][];
|
||||
const payload = calls.at(-1)?.[0] as { replies?: Array<{ text?: string }> } | undefined;
|
||||
expect(payload?.replies?.map((reply) => reply.text)).toEqual([text]);
|
||||
}
|
||||
|
||||
function mockCall(mock: { mock: { calls: unknown[][] } }, index: number): unknown[] {
|
||||
const call = mock.mock.calls[index];
|
||||
if (!call) {
|
||||
throw new Error(`expected mock call ${index}`);
|
||||
}
|
||||
return call;
|
||||
}
|
||||
|
||||
describe("registerTelegramNativeCommands real plugin registry", () => {
|
||||
beforeAll(async () => {
|
||||
({ setActivePluginRegistry } = await import("openclaw/plugin-sdk/plugin-test-runtime"));
|
||||
({ registerTelegramNativeCommands } = await import("./bot-native-commands.js"));
|
||||
({
|
||||
createCommandBot,
|
||||
createNativeCommandTestParams,
|
||||
createPrivateCommandContext,
|
||||
deliverReplies,
|
||||
editMessageTelegram,
|
||||
resetNativeCommandMenuMocks,
|
||||
waitForRegisteredCommands,
|
||||
} = await import("./bot-native-commands.menu-test-support.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
setActivePluginRegistry(createTelegramPluginRegistry() as never);
|
||||
clearPluginCommands();
|
||||
resetNativeCommandMenuMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clearPluginCommands();
|
||||
});
|
||||
|
||||
it("registers and executes plugin commands through the real plugin registry", async () => {
|
||||
const { bot, commandHandlers, sendMessage, setMyCommands } = createCommandBot();
|
||||
|
||||
const registeredCommands = await registerPairMenu({ bot, setMyCommands });
|
||||
expectRegisteredCommand(registeredCommands, { command: "pair", description: "Pair device" });
|
||||
|
||||
const handler = requireCommandHandler(commandHandlers, "pair");
|
||||
|
||||
await handler(createPrivateCommandContext({ match: "now" }));
|
||||
|
||||
expectLastDeliveredReplyText("paired:now");
|
||||
expect(sendMessage).not.toHaveBeenCalledWith(123, "Command not found.");
|
||||
});
|
||||
|
||||
it("uses plugin command metadata to send and edit a Telegram progress placeholder", async () => {
|
||||
const { bot, commandHandlers, setMyCommands, sendMessage } = createCommandBot();
|
||||
|
||||
await registerPairMenu({
|
||||
bot,
|
||||
setMyCommands,
|
||||
nativeProgressMessages: {
|
||||
telegram:
|
||||
"Running pair now...\n\nI'll edit this message with the final result when it's ready.",
|
||||
},
|
||||
});
|
||||
|
||||
const handler = requireCommandHandler(commandHandlers, "pair");
|
||||
|
||||
await handler(createPrivateCommandContext({ match: "now" }));
|
||||
|
||||
const sendCall = mockCall(sendMessage, 0);
|
||||
expect(sendCall[0]).toBe(100);
|
||||
expect(sendCall[1]).toContain("Running pair now");
|
||||
expect(sendCall[2]).toBeUndefined();
|
||||
const editCall = mockCall(editMessageTelegram, 0);
|
||||
expect(editCall[0]).toBe(100);
|
||||
expect(editCall[1]).toBe(999);
|
||||
expect(editCall[2]).toBe("paired:now");
|
||||
expect((editCall[3] as { accountId?: string }).accountId).toBe("default");
|
||||
expect(deliverReplies).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("round-trips Telegram native aliases through the real plugin registry", async () => {
|
||||
const { bot, commandHandlers, sendMessage, setMyCommands } = createCommandBot();
|
||||
|
||||
const registeredCommands = await registerPairMenu({
|
||||
bot,
|
||||
setMyCommands,
|
||||
nativeNames: {
|
||||
telegram: "pair_device",
|
||||
discord: "pairdiscord",
|
||||
},
|
||||
});
|
||||
expectRegisteredCommand(registeredCommands, {
|
||||
command: "pair_device",
|
||||
description: "Pair device",
|
||||
});
|
||||
|
||||
const handler = requireCommandHandler(commandHandlers, "pair_device");
|
||||
|
||||
await handler(createPrivateCommandContext({ match: "now", messageId: 2 }));
|
||||
|
||||
expectLastDeliveredReplyText("paired:now");
|
||||
expect(sendMessage).not.toHaveBeenCalledWith(123, "Command not found.");
|
||||
});
|
||||
|
||||
it("keeps real plugin command handlers available when native menu registration is disabled", () => {
|
||||
const { bot, commandHandlers, setMyCommands } = createCommandBot();
|
||||
|
||||
registerPairPluginCommand();
|
||||
|
||||
registerTelegramNativeCommands({
|
||||
...createNativeCommandTestParams({}, { accountId: "default" }),
|
||||
bot,
|
||||
nativeEnabled: false,
|
||||
});
|
||||
|
||||
expect(setMyCommands).not.toHaveBeenCalled();
|
||||
expect(commandHandlers.has("pair")).toBe(true);
|
||||
});
|
||||
|
||||
it("allows requireAuth:false plugin commands for unauthorized senders through the real registry", async () => {
|
||||
const { bot, commandHandlers, sendMessage, setMyCommands } = createCommandBot();
|
||||
|
||||
registerPairPluginCommand();
|
||||
|
||||
registerTelegramNativeCommands({
|
||||
...createNativeCommandTestParams({
|
||||
commands: { allowFrom: { telegram: ["999"] } } as OpenClawConfig["commands"],
|
||||
}),
|
||||
bot,
|
||||
allowFrom: ["999"],
|
||||
nativeEnabled: false,
|
||||
});
|
||||
|
||||
expect(setMyCommands).not.toHaveBeenCalled();
|
||||
|
||||
const handler = requireCommandHandler(commandHandlers, "pair");
|
||||
|
||||
await handler(
|
||||
createPrivateCommandContext({
|
||||
match: "now",
|
||||
messageId: 10,
|
||||
date: 123456,
|
||||
userId: 111,
|
||||
username: "nope",
|
||||
}),
|
||||
);
|
||||
|
||||
expectLastDeliveredReplyText("paired:now");
|
||||
expect(sendMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user