Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/JAVA/Openclaw/src/plugins/   (KI Agentensystem Version 22©)  Datei vom 26.3.2026 mit Größe 19 kB image not shown  

Quelle  commands.test.ts

  Sprache: JAVA
 

Spracherkennung für: .ts vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { createChannelTestPluginBase, createTestRegistry } from "../test-utils/channel-plugins.js";
import {
  __testing,
  clearPluginCommands,
  executePluginCommand,
  getPluginCommandSpecs,
  listProviderPluginCommandSpecs,
  listPluginCommands,
  matchPluginCommand,
  registerPluginCommand,
} from "./commands.js";
import { setActivePluginRegistry } from "./runtime.js";

type CommandsModule = typeof import("./commands.js");

const commandsModuleUrl = new URL("./commands.ts", import.meta.url).href;

async function importCommandsModule(cacheBust: string): Promise<CommandsModule> {
  return (await import(`${commandsModuleUrl}?t=${cacheBust}`)) as CommandsModule;
}

function createVoiceCommand(overrides: Partial<Parameters<typeof registerPluginCommand>[1]> = {}) {
  return {
    name: "voice",
    description: "Voice command",
    handler: async () => ({ text: "ok" }),
    ...overrides,
  };
}

function registerVoiceCommandForTest(
  overrides: Partial<Parameters<typeof registerPluginCommand>[1]> = {},
) {
  return registerPluginCommand("demo-plugin", createVoiceCommand(overrides));
}

function resolveBindingConversationFromCommand(
  params: Parameters<typeof __testing.resolveBindingConversationFromCommand>[0],
) {
  return __testing.resolveBindingConversationFromCommand(params);
}

function expectCommandMatch(
  commandBody: string,
  params: { name: string; pluginId: string; args: string },
) {
  expect(matchPluginCommand(commandBody)).toMatchObject({
    command: expect.objectContaining({
      name: params.name,
      pluginId: params.pluginId,
    }),
    args: params.args,
  });
}

function expectProviderCommandSpecs(
  provider: Parameters<typeof getPluginCommandSpecs>[0],
  expectedNames: readonly string[],
) {
  expect(getPluginCommandSpecs(provider)).toEqual(
    expectedNames.map((name) => ({
      name,
      description: "Demo command",
      acceptsArgs: false,
    })),
  );
}

function expectProviderCommandSpecCases(
  cases: ReadonlyArray<{
    provider: Parameters<typeof getPluginCommandSpecs>[0];
    expectedNames: readonly string[];
  }>,
) {
  cases.forEach(({ provider, expectedNames }) => {
    expectProviderCommandSpecs(provider, expectedNames);
  });
}

function expectUnsupportedBindingApiResult(result: { text?: string }) {
  expect(result.text).toBe(
    JSON.stringify({
      requested: {
        status: "error",
        message: "This command cannot bind the current conversation.",
      },
      current: null,
      detached: { removed: false },
    }),
  );
}

function expectBindingConversationCase(
  params: Parameters<typeof resolveBindingConversationFromCommand>[0],
  expected: ReturnType<typeof resolveBindingConversationFromCommand>,
) {
  expect(resolveBindingConversationFromCommand(params)).toEqual(expected);
}

beforeEach(() => {
  setActivePluginRegistry(
    createTestRegistry([
      {
        pluginId: "telegram",
        source: "test",
        plugin: {
          ...createChannelTestPluginBase({ id: "telegram", label: "Telegram" }),
          commands: {
            nativeCommandsAutoEnabled: true,
          },
          bindings: {
            selfParentConversationByDefault: true,
            resolveCommandConversation: ({
              threadId,
              originatingTo,
              commandTo,
              fallbackTo,
            }: {
              threadId?: string;
              originatingTo?: string;
              commandTo?: string;
              fallbackTo?: string;
            }) => {
              const rawTarget = [commandTo, originatingTo, fallbackTo].find(Boolean)?.trim();
              if (!rawTarget || rawTarget.startsWith("slash:")) {
                return null;
              }
              const normalized = rawTarget.replace(/^telegram:/i, "");
              const topicMatch = /^(.*?):topic:(\d+)$/i.exec(normalized);
              if (topicMatch?.[1]) {
                return {
                  conversationId: `${topicMatch[1]}:topic:${threadId ?? topicMatch[2]}`,
                  parentConversationId: topicMatch[1],
                };
              }
              return { conversationId: normalized };
            },
          },
        },
      },
      {
        pluginId: "discord",
        source: "test",
        plugin: {
          ...createChannelTestPluginBase({ id: "discord", label: "Discord" }),
          commands: {
            nativeCommandsAutoEnabled: true,
          },
          bindings: {
            resolveCommandConversation: ({
              threadId,
              threadParentId,
              originatingTo,
              commandTo,
              fallbackTo,
            }: {
              threadId?: string;
              threadParentId?: string;
              originatingTo?: string;
              commandTo?: string;
              fallbackTo?: string;
            }) => {
              const rawTarget = [originatingTo, commandTo, fallbackTo].find(Boolean)?.trim();
              if (!rawTarget || rawTarget.startsWith("slash:")) {
                return null;
              }
              const normalized = rawTarget.replace(/^discord:/i, "");
              if (/^\d+$/.test(normalized)) {
                return { conversationId: `user:${normalized}` };
              }
              if (threadId) {
                const baseConversationId =
                  originatingTo?.trim()?.replace(/^discord:/i, "") ||
                  commandTo?.trim()?.replace(/^discord:/i, "") ||
                  fallbackTo?.trim()?.replace(/^discord:/i, "");
                return {
                  conversationId: baseConversationId || threadId,
                  ...(threadParentId ? { parentConversationId: threadParentId } : {}),
                };
              }
              if (normalized.startsWith("channel:") || normalized.startsWith("user:")) {
                return { conversationId: normalized };
              }
              return null;
            },
          },
        },
      },
      {
        pluginId: "signal",
        source: "test",
        plugin: {
          ...createChannelTestPluginBase({ id: "signal", label: "Signal" }),
          commands: {
            nativeCommandsAutoEnabled: true,
          },
          bindings: {
            resolveCommandConversation: ({ senderId }: { senderId?: string }) => {
              const normalizedSenderId = senderId?.trim();
              return normalizedSenderId ? { conversationId: `dm:${normalizedSenderId}` } : null;
            },
          },
        },
      },
      {
        pluginId: "slack",
        source: "test",
        plugin: {
          ...createChannelTestPluginBase({
            id: "slack",
            label: "Slack",
            capabilities: { nativeCommands: true, chatTypes: ["direct", "group"] },
          }),
        },
      },
    ]),
  );
});

afterEach(() => {
  clearPluginCommands();
});

describe("registerPluginCommand", () => {
  it.each([
    {
      name: "rejects invalid command names",
      command: {
        // Runtime plugin payloads are untyped; guard at boundary.
        name: undefined as unknown as string,
        description: "Demo",
        handler: async () => ({ text: "ok" }),
      },
      expected: {
        ok: false,
        error: "Command name must be a string",
      },
    },
    {
      name: "rejects invalid command descriptions",
      command: {
        name: "demo",
        description: undefined as unknown as string,
        handler: async () => ({ text: "ok" }),
      },
      expected: {
        ok: false,
        error: "Command description must be a string",
      },
    },
  ] as const)("$name", ({ command, expected }) => {
    expect(registerPluginCommand("demo-plugin", command)).toEqual(expected);
  });

  it("normalizes command metadata for downstream consumers", () => {
    const result = registerPluginCommand("demo-plugin", {
      name: "  demo_cmd  ",
      description: "  Demo command  ",
      handler: async () => ({ text: "ok" }),
    });
    expect(result).toEqual({ ok: true });
    expect(listPluginCommands()).toEqual([
      {
        name: "demo_cmd",
        description: "Demo command",
        pluginId: "demo-plugin",
        acceptsArgs: false,
      },
    ]);
    expect(getPluginCommandSpecs()).toEqual([
      {
        name: "demo_cmd",
        description: "Demo command",
        acceptsArgs: false,
      },
    ]);
  });

  it("matches underscore aliases for hyphenated command names", () => {
    registerPluginCommand("demo-plugin", {
      name: "active-memory",
      description: "Active Memory command",
      acceptsArgs: true,
      handler: async () => ({ text: "ok" }),
    });

    expect(matchPluginCommand("/active_memory status")).toMatchObject({
      command: expect.objectContaining({
        name: "active-memory",
        pluginId: "demo-plugin",
      }),
      args: "status",
    });
  });

  it("supports provider-specific native command aliases", () => {
    const result = registerVoiceCommandForTest({
      nativeNames: {
        default: "talkvoice",
        discord: "discordvoice",
      },
      description: "Demo command",
    });

    expect(result).toEqual({ ok: true });
    expectProviderCommandSpecCases([
      { provider: undefined, expectedNames: ["talkvoice"] },
      { provider: "discord", expectedNames: ["discordvoice"] },
      { provider: "telegram", expectedNames: ["talkvoice"] },
      { provider: "slack", expectedNames: [] },
    ]);
  });

  it("allows Slack to resolve provider-native plugin specs without changing shared native gating", () => {
    const result = registerVoiceCommandForTest({
      nativeNames: {
        default: "talkvoice",
        discord: "discordvoice",
      },
      description: "Demo command",
    });

    expect(result).toEqual({ ok: true });
    expect(listProviderPluginCommandSpecs("slack")).toEqual([
      {
        name: "talkvoice",
        description: "Demo command",
        acceptsArgs: false,
      },
    ]);
  });

  it("accepts native progress metadata on plugin commands", () => {
    const result = registerVoiceCommandForTest({
      nativeProgressMessages: { telegram: "Running voice command..." },
      description: "Demo command",
    });

    expect(result).toEqual({ ok: true });
    expect(matchPluginCommand("/voice")).toMatchObject({
      command: expect.objectContaining({
        nativeProgressMessages: { telegram: "Running voice command..." },
      }),
    });
  });

  it("rejects empty native progress metadata", () => {
    const result = registerVoiceCommandForTest({
      nativeProgressMessages: { telegram: "   " },
      description: "Demo command",
    });

    expect(result).toEqual({
      ok: false,
      error: 'Native progress message "telegram" cannot be empty',
    });
  });

  it("shares plugin commands across duplicate module instances", async () => {
    const first = await importCommandsModule(`first-${Date.now()}`);
    const second = await importCommandsModule(`second-${Date.now()}`);

    first.clearPluginCommands();

    expect(
      first.registerPluginCommand(
        "demo-plugin",
        createVoiceCommand({
          nativeNames: {
            telegram: "voice",
          },
        }),
      ),
    ).toEqual({ ok: true });

    expect(second.getPluginCommandSpecs("telegram")).toEqual([
      {
        name: "voice",
        description: "Voice command",
        acceptsArgs: false,
      },
    ]);
    expect(second.matchPluginCommand("/voice")).toMatchObject({
      command: expect.objectContaining({
        name: "voice",
        pluginId: "demo-plugin",
      }),
    });

    second.clearPluginCommands();
  });

  it.each(["/talkvoice now", "/discordvoice now"] as const)(
    "matches provider-specific native alias %s back to the canonical command",
    (commandBody) => {
      const result = registerVoiceCommandForTest({
        nativeNames: {
          default: "talkvoice",
          discord: "discordvoice",
        },
        description: "Demo command",
        acceptsArgs: true,
      });

      expect(result).toEqual({ ok: true });
      expectCommandMatch(commandBody, {
        name: "voice",
        pluginId: "demo-plugin",
        args: "now",
      });
    },
  );

  it.each([
    {
      name: "rejects provider aliases that collide with another registered command",
      setup: () =>
        registerPluginCommand(
          "demo-plugin",
          createVoiceCommand({
            nativeNames: {
              telegram: "pair_device",
            },
          }),
        ),
      candidate: {
        name: "pair",
        nativeNames: {
          telegram: "pair_device",
        },
        description: "Pair command",
        handler: async () => ({ text: "ok" }),
      },
      expected: {
        ok: false,
        error: 'Command "pair_device" already registered by plugin "demo-plugin"',
      },
    },
    {
      name: "rejects reserved provider aliases",
      candidate: createVoiceCommand({
        nativeNames: {
          telegram: "help",
        },
      }),
      expected: {
        ok: false,
        error:
          'Native command alias "telegram" invalid: Command name "help" is reserved by a built-in command',
      },
    },
  ] as const)("$name", ({ setup, candidate, expected }) => {
    setup?.();
    expect(registerPluginCommand("other-plugin", candidate)).toEqual(expected);
  });

  it.each([
    {
      name: "resolves Discord DM command bindings with the user target prefix intact",
      params: {
        channel: "discord",
        from: "discord:1177378744822943744",
        to: "slash:1177378744822943744",
        accountId: "default",
      },
      expected: {
        channel: "discord",
        accountId: "default",
        conversationId: "user:1177378744822943744",
      },
    },
    {
      name: "resolves Discord guild command bindings with the channel target prefix intact",
      params: {
        channel: "discord",
        from: "discord:channel:1480554272859881494",
        accountId: "default",
      },
      expected: {
        channel: "discord",
        accountId: "default",
        conversationId: "channel:1480554272859881494",
      },
    },
    {
      name: "resolves Discord thread command bindings with parent channel context intact",
      params: {
        channel: "discord",
        from: "discord:channel:1480554272859881494",
        accountId: "default",
        messageThreadId: "thread-42",
        threadParentId: "channel-parent-7",
      },
      expected: {
        channel: "discord",
        accountId: "default",
        conversationId: "channel:1480554272859881494",
        parentConversationId: "channel-parent-7",
        threadId: "thread-42",
      },
    },
    {
      name: "does not resolve binding conversations for unsupported command channels",
      params: {
        channel: "slack",
        from: "slack:U123",
        to: "C456",
        accountId: "default",
      },
      expected: null,
    },
    {
      name: "resolves sender-keyed command bindings when only senderId is available",
      params: {
        channel: "signal",
        senderId: "signal-user-42",
        accountId: "default",
      },
      expected: {
        channel: "signal",
        accountId: "default",
        conversationId: "dm:signal-user-42",
      },
    },
  ] as const)("$name", ({ params, expected }) => {
    expectBindingConversationCase(params, expected);
  });

  it("does not expose binding APIs to plugin commands on unsupported channels", async () => {
    const handler = async (ctx: {
      requestConversationBinding: (params: { summary: string }) => Promise<unknown>;
      getCurrentConversationBinding: () => Promise<unknown>;
      detachConversationBinding: () => Promise<unknown>;
    }) => {
      const requested = await ctx.requestConversationBinding({
        summary: "Bind this conversation.",
      });
      const current = await ctx.getCurrentConversationBinding();
      const detached = await ctx.detachConversationBinding();
      return {
        text: JSON.stringify({
          requested,
          current,
          detached,
        }),
      };
    };
    registerPluginCommand(
      "demo-plugin",
      {
        name: "bindcheck",
        description: "Demo command",
        acceptsArgs: false,
        handler,
      },
      { pluginRoot: "/plugins/demo-plugin" },
    );

    const result = await executePluginCommand({
      command: {
        name: "bindcheck",
        description: "Demo command",
        acceptsArgs: false,
        handler,
        pluginId: "demo-plugin",
        pluginRoot: "/plugins/demo-plugin",
      },
      channel: "slack",
      senderId: "U123",
      isAuthorizedSender: true,
      commandBody: "/bindcheck",
      config: {} as never,
      from: "slack:U123",
      to: "C456",
      accountId: "default",
    });

    expectUnsupportedBindingApiResult(result);
  });

  it("passes host session identity through to the plugin command context", async () => {
    let receivedCtx:
      | {
          sessionKey?: string;
          sessionId?: string;
        }
      | undefined;
    const handler = async (ctx: { sessionKey?: string; sessionId?: string }) => {
      receivedCtx = ctx;
      return { text: "ok" };
    };

    const result = await executePluginCommand({
      command: {
        name: "sessioncheck",
        description: "Demo command",
        acceptsArgs: false,
        handler,
        pluginId: "demo-plugin",
      },
      channel: "whatsapp",
      senderId: "U123",
      isAuthorizedSender: true,
      sessionKey: "agent:main:whatsapp:direct:123",
      sessionId: "session-123",
      commandBody: "/sessioncheck",
      config: {} as never,
    });

    expect(result).toEqual({ text: "ok" });
    expect(receivedCtx).toMatchObject({
      sessionKey: "agent:main:whatsapp:direct:123",
      sessionId: "session-123",
    });
  });

  it("passes the effective default account to plugin command handlers when accountId is omitted", async () => {
    setActivePluginRegistry(
      createTestRegistry([
        {
          pluginId: "line",
          source: "test",
          plugin: {
            ...createChannelTestPluginBase({
              id: "line",
              label: "LINE",
              config: {
                listAccountIds: () => ["default", "work"],
                defaultAccountId: () => "work",
                resolveAccount: (_cfg, accountId) => ({ accountId: accountId ?? "work" }),
              },
            }),
            bindings: {
              resolveCommandConversation: ({
                originatingTo,
                commandTo,
                fallbackTo,
              }: {
                originatingTo?: string;
                commandTo?: string;
                fallbackTo?: string;
              }) => {
                const rawTarget = [originatingTo, commandTo, fallbackTo].find(Boolean)?.trim();
                if (!rawTarget) {
                  return null;
                }
                return {
                  conversationId: rawTarget.replace(/^line:/i, "").replace(/^user:/i, ""),
                };
              },
            },
          },
        },
      ]),
    );

    let receivedCtx:
      | {
          accountId?: string;
        }
      | undefined;
    const handler = async (ctx: { accountId?: string }) => {
      receivedCtx = ctx;
      return { text: "ok" };
    };

    const result = await executePluginCommand({
      command: {
        name: "accountcheck",
        description: "Demo command",
        acceptsArgs: false,
        handler,
        pluginId: "demo-plugin",
      },
      channel: "line",
      senderId: "U123",
      isAuthorizedSender: true,
      commandBody: "/accountcheck",
      config: {} as never,
      from: "line:user:U1234567890abcdef1234567890abcdef",
    });

    expect(result).toEqual({ text: "ok" });
    expect(receivedCtx).toMatchObject({
      accountId: "work",
    });
  });
});

¤ Dauer der Verarbeitung: 0.29 Sekunden  (vorverarbeitet am  2026-04-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.