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

Quelle  channel.test.ts

  Sprache: JAVA
 

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

import { beforeEach, describe, expect, it, vi } from "vitest";
import { createRuntimeEnv } from "../../../test/helpers/plugins/runtime-env.js";
import { slackPlugin } from "./channel.js";
import { slackOutbound } from "./outbound-adapter.js";
import * as probeModule from "./probe.js";
import type { OpenClawConfig } from "./runtime-api.js";
import { clearSlackRuntime, setSlackRuntime } from "./runtime.js";

const { handleSlackActionMock } = vi.hoisted(() => ({
  handleSlackActionMock: vi.fn(),
}));
const { sendMessageSlackMock } = vi.hoisted(() => ({
  sendMessageSlackMock: vi.fn(),
}));

vi.mock("./action-runtime.js", async () => {
  const actual = await vi.importActual<typeof import("./action-runtime.js")>("./action-runtime.js");
  return {
    ...actual,
    handleSlackAction: handleSlackActionMock,
  };
});

vi.mock("./send.runtime.js", () => ({
  sendMessageSlack: sendMessageSlackMock,
}));

beforeEach(async () => {
  handleSlackActionMock.mockReset();
  sendMessageSlackMock.mockReset();
  sendMessageSlackMock.mockResolvedValue({ messageId: "msg-1", channelId: "D123" });
  setSlackRuntime({
    channel: {
      slack: {
        handleSlackAction: handleSlackActionMock,
      },
    },
  } as never);
});

async function getSlackConfiguredState(cfg: OpenClawConfig) {
  const account = slackPlugin.config.resolveAccount(cfg, "default");
  return {
    configured: slackPlugin.config.isConfigured?.(account, cfg),
    snapshot: await slackPlugin.status?.buildAccountSnapshot?.({
      account,
      cfg,
      runtime: undefined,
    }),
  };
}

function requireSlackHandleAction() {
  const handleAction = slackPlugin.actions?.handleAction;
  if (!handleAction) {
    throw new Error("slack actions.handleAction unavailable");
  }
  return handleAction;
}

function requireSlackSendText() {
  const sendText = slackPlugin.outbound?.sendText;
  if (!sendText) {
    throw new Error("slack outbound.sendText unavailable");
  }
  return sendText;
}

function requireSlackSendMedia() {
  const sendMedia = slackPlugin.outbound?.sendMedia;
  if (!sendMedia) {
    throw new Error("slack outbound.sendMedia unavailable");
  }
  return sendMedia;
}

function requireSlackSendPayload() {
  const sendPayload = slackPlugin.outbound?.sendPayload ?? slackOutbound.sendPayload;
  if (!sendPayload) {
    throw new Error("slack outbound.sendPayload unavailable");
  }
  return sendPayload;
}

function requireSlackListPeers() {
  const listPeers = slackPlugin.directory?.listPeers;
  if (!listPeers) {
    throw new Error("slack directory.listPeers unavailable");
  }
  return listPeers;
}

describe("slackPlugin actions", () => {
  it("prefers session lookup for announce target routing", () => {
    expect(slackPlugin.meta.preferSessionLookupForAnnounceTarget).toBe(true);
  });

  it("owns unified message tool discovery", () => {
    const discovery = slackPlugin.actions?.describeMessageTool({
      cfg: {
        channels: {
          slack: {
            botToken: "xoxb-test",
            appToken: "xapp-test",
            capabilities: { interactiveReplies: true },
          },
        },
      },
    });

    expect(discovery?.actions).toContain("send");
    expect(discovery?.capabilities).toEqual(expect.arrayContaining(["presentation"]));
    expect(discovery?.schema).toBeUndefined();
  });

  it("honors the selected Slack account during message tool discovery", () => {
    const cfg: OpenClawConfig = {
      channels: {
        slack: {
          botToken: "xoxb-root",
          appToken: "xapp-root",
          actions: {
            reactions: false,
            messages: false,
            pins: false,
            memberInfo: false,
            emojiList: false,
          },
          capabilities: {
            interactiveReplies: false,
          },
          accounts: {
            default: {
              botToken: "xoxb-default",
              appToken: "xapp-default",
              actions: {
                reactions: false,
                messages: false,
                pins: false,
                memberInfo: false,
                emojiList: false,
              },
              capabilities: {
                interactiveReplies: false,
              },
            },
            work: {
              botToken: "xoxb-work",
              appToken: "xapp-work",
              actions: {
                reactions: true,
                messages: true,
                pins: false,
                memberInfo: false,
                emojiList: false,
              },
              capabilities: {
                interactiveReplies: true,
              },
            },
          },
        },
      },
    };

    expect(slackPlugin.actions?.describeMessageTool?.({ cfg, accountId: "default" })).toMatchObject(
      {
        actions: ["send"],
        capabilities: ["presentation"],
      },
    );
    expect(slackPlugin.actions?.describeMessageTool?.({ cfg, accountId: "work" })).toMatchObject({
      actions: [
        "send",
        "react",
        "reactions",
        "read",
        "edit",
        "delete",
        "download-file",
        "upload-file",
      ],
      capabilities: expect.arrayContaining(["presentation"]),
    });
  });

  it("uses configured defaultAccount for pairing approval notifications", async () => {
    const cfg = {
      channels: {
        slack: {
          defaultAccount: "work",
          accounts: {
            work: {
              botToken: "xoxb-work",
            },
          },
        },
      },
    } as OpenClawConfig;
    setSlackRuntime({
      config: {
        loadConfig: () => cfg,
      },
    } as never);

    const notify = slackPlugin.pairing?.notifyApproval;
    if (!notify) {
      throw new Error("slack pairing notify unavailable");
    }

    await notify({
      cfg,
      id: "U12345678",
    });

    expect(sendMessageSlackMock).toHaveBeenCalledWith(
      "user:U12345678",
      expect.stringContaining("approved"),
      expect.objectContaining({
        accountId: "work",
        cfg,
        token: "xoxb-work",
      }),
    );
  });

  it("does not expose Slack-native message tool schema", () => {
    const discovery = slackPlugin.actions?.describeMessageTool({
      cfg: {
        channels: {
          slack: {
            botToken: "xoxb-test",
            appToken: "xapp-test",
          },
        },
      } as OpenClawConfig,
    });
    expect(discovery?.schema).toBeUndefined();
  });

  it("treats interactive reply payloads as structured Slack payloads", () => {
    const hasStructuredReplyPayload = slackPlugin.messaging?.hasStructuredReplyPayload;
    if (!hasStructuredReplyPayload) {
      throw new Error("slack messaging.hasStructuredReplyPayload unavailable");
    }

    expect(
      hasStructuredReplyPayload({
        payload: {
          text: "Choose",
          interactive: {
            blocks: [{ type: "buttons", buttons: [{ label: "Retry", value: "retry" }] }],
          },
        },
      }),
    ).toBe(true);
  });

  it("forwards read threadId to Slack action handler", async () => {
    handleSlackActionMock.mockResolvedValueOnce({ messages: [], hasMore: false });
    const handleAction = requireSlackHandleAction();

    await handleAction({
      action: "read",
      channel: "slack",
      accountId: "default",
      cfg: {},
      params: {
        channelId: "C123",
        threadId: "1712345678.123456",
      },
    });

    expect(handleSlackActionMock).toHaveBeenCalledWith(
      expect.objectContaining({
        action: "readMessages",
        channelId: "C123",
        threadId: "1712345678.123456",
      }),
      {},
      undefined,
    );
  });
});

describe("slackPlugin status", () => {
  it("uses the direct Slack probe helper when runtime is not initialized", async () => {
    const probeSpy = vi.spyOn(probeModule, "probeSlack").mockResolvedValueOnce({
      ok: true,
      status: 200,
      bot: { id: "B1", name: "openclaw-bot" },
      team: { id: "T1", name: "OpenClaw" },
    });
    clearSlackRuntime();
    const cfg = {
      channels: {
        slack: {
          botToken: "xoxb-test",
          appToken: "xapp-test",
        },
      },
    } as OpenClawConfig;
    const account = slackPlugin.config.resolveAccount(cfg, "default");

    const result = await slackPlugin.status!.probeAccount!({
      account,
      timeoutMs: 2500,
      cfg,
    });

    expect(probeSpy).toHaveBeenCalledWith("xoxb-test", 2500);
    expect(result).toEqual({
      ok: true,
      status: 200,
      bot: { id: "B1", name: "openclaw-bot" },
      team: { id: "T1", name: "OpenClaw" },
    });
  });

  it("recovers thread routing from mixed-case Slack session keys", async () => {
    const resolveRoute = slackPlugin.messaging?.resolveOutboundSessionRoute;
    if (!resolveRoute) {
      throw new Error("slack messaging.resolveOutboundSessionRoute unavailable");
    }

    const route = await resolveRoute({
      cfg: {} as OpenClawConfig,
      agentId: "main",
      target: "channel:C1",
      currentSessionKey: "agent:main:slack:channel:C1:thread:1712345678.123456",
    });

    expect(route).toMatchObject({
      sessionKey: "agent:main:slack:channel:c1:thread:1712345678.123456",
      baseSessionKey: "agent:main:slack:channel:c1",
      threadId: "1712345678.123456",
    });
  });
});

describe("slackPlugin security", () => {
  it("normalizes dm allowlist entries with trimmed prefixes", () => {
    const resolveDmPolicy = slackPlugin.security?.resolveDmPolicy;
    if (!resolveDmPolicy) {
      throw new Error("resolveDmPolicy unavailable");
    }

    const result = resolveDmPolicy({
      cfg: {
        channels: {
          slack: {
            dm: { policy: "allowlist", allowFrom: ["  slack:U123  "] },
          },
        },
      } as OpenClawConfig,
      account: slackPlugin.config.resolveAccount(
        {
          channels: {
            slack: {
              botToken: "xoxb-test",
              appToken: "xapp-test",
              dm: { policy: "allowlist", allowFrom: ["  slack:U123  "] },
            },
          },
        } as OpenClawConfig,
        "default",
      ),
    });
    if (!result) {
      throw new Error("slack resolveDmPolicy returned null");
    }

    expect(result.policy).toBe("allowlist");
    expect(result.allowFrom).toEqual(["  slack:U123  "]);
    expect(result.normalizeEntry?.("  slack:U123  ")).toBe("U123");
    expect(result.normalizeEntry?.("  user:U999  ")).toBe("U999");
  });
});

describe("slackPlugin outbound", () => {
  const cfg = {
    channels: {
      slack: {
        botToken: "xoxb-test",
        appToken: "xapp-test",
      },
    },
  };

  it("treats ACP block text as visible delivered output", () => {
    expect(
      slackPlugin.outbound?.shouldTreatDeliveredTextAsVisible?.({
        kind: "block",
        text: "hello",
      }),
    ).toBe(true);
    expect(
      slackPlugin.outbound?.shouldTreatDeliveredTextAsVisible?.({
        kind: "tool",
        text: "hello",
      }),
    ).toBe(false);
  });

  it("advertises the 8000-character Slack default chunk limit", () => {
    expect(slackOutbound.textChunkLimit).toBe(8000);
    expect(slackPlugin.outbound?.textChunkLimit).toBe(8000);
  });

  it("uses threadId as threadTs fallback for sendText", async () => {
    const sendSlack = vi.fn().mockResolvedValue({ messageId: "m-text" });
    const sendText = requireSlackSendText();

    const result = await sendText({
      cfg,
      to: "C123",
      text: "hello",
      accountId: "default",
      threadId: "1712345678.123456",
      deps: { sendSlack },
    });

    expect(sendSlack).toHaveBeenCalledWith(
      "C123",
      "hello",
      expect.objectContaining({
        threadTs: "1712345678.123456",
      }),
    );
    expect(result).toEqual({ channel: "slack", messageId: "m-text" });
  });

  it("prefers replyToId over threadId for sendMedia", async () => {
    const sendSlack = vi.fn().mockResolvedValue({ messageId: "m-media" });
    const sendMedia = requireSlackSendMedia();

    const result = await sendMedia({
      cfg,
      to: "C999",
      text: "caption",
      mediaUrl: "https://example.com/image.png",
      accountId: "default",
      replyToId: "1712000000.000001",
      threadId: "1712345678.123456",
      deps: { sendSlack },
    });

    expect(sendSlack).toHaveBeenCalledWith(
      "C999",
      "caption",
      expect.objectContaining({
        mediaUrl: "https://example.com/image.png",
        threadTs: "1712000000.000001",
      }),
    );
    expect(result).toEqual({ channel: "slack", messageId: "m-media" });
  });

  it("falls back to threadId when replyToId is not a Slack thread timestamp", async () => {
    const sendSlack = vi.fn().mockResolvedValue({ messageId: "m-text" });
    const sendText = requireSlackSendText();

    const result = await sendText({
      cfg,
      to: "C123",
      text: "hello",
      accountId: "default",
      replyToId: "msg-internal-1",
      threadId: "1712345678.123456",
      deps: { sendSlack },
    });

    expect(sendSlack).toHaveBeenCalledWith(
      "C123",
      "hello",
      expect.objectContaining({
        threadTs: "1712345678.123456",
      }),
    );
    expect(result).toEqual({ channel: "slack", messageId: "m-text" });
  });

  it("does not stringify numeric Slack thread ids", async () => {
    const sendSlack = vi.fn().mockResolvedValue({ messageId: "m-text" });
    const sendText = requireSlackSendText();

    await sendText({
      cfg,
      to: "C123",
      text: "hello",
      accountId: "default",
      threadId: 1712345678.123456,
      deps: { sendSlack },
    });

    expect(sendSlack).toHaveBeenCalledWith(
      "C123",
      "hello",
      expect.objectContaining({
        threadTs: undefined,
      }),
    );
  });

  it("falls back to auto-thread lookup when replyToId is not a Slack thread timestamp", () => {
    const resolveAutoThreadId = slackPlugin.threading?.resolveAutoThreadId;
    if (!resolveAutoThreadId) {
      throw new Error("slack threading.resolveAutoThreadId unavailable");
    }

    const threadId = resolveAutoThreadId({
      cfg,
      to: "channel:C123",
      replyToId: "msg-internal-1",
      toolContext: {
        currentChannelId: "C123",
        currentThreadTs: "1712345678.123456",
        replyToMode: "all",
      },
    });

    expect(threadId).toBe("1712345678.123456");
  });

  it("does not recover invalid Slack auto-thread anchors", () => {
    const resolveAutoThreadId = slackPlugin.threading?.resolveAutoThreadId;
    if (!resolveAutoThreadId) {
      throw new Error("slack threading.resolveAutoThreadId unavailable");
    }

    const threadId = resolveAutoThreadId({
      cfg,
      to: "channel:C123",
      replyToId: "msg-internal-1",
      toolContext: {
        currentChannelId: "C123",
        currentThreadTs: "thread-root",
        replyToMode: "all",
      },
    });

    expect(threadId).toBeUndefined();
  });

  it("does not stringify numeric thread ids in tool context", () => {
    const buildToolContext = slackPlugin.threading?.buildToolContext;
    if (!buildToolContext) {
      throw new Error("slack threading.buildToolContext unavailable");
    }

    const context = buildToolContext({
      cfg,
      context: {
        To: "channel:C123",
        MessageThreadId: 1712345678.123456,
      },
    });

    expect(context?.currentThreadTs).toBeUndefined();
  });

  it("falls back to threadId in reply transport when replyToId is not a Slack thread timestamp", () => {
    const resolveReplyTransport = slackPlugin.threading?.resolveReplyTransport;
    if (!resolveReplyTransport) {
      throw new Error("slack threading.resolveReplyTransport unavailable");
    }

    expect(
      resolveReplyTransport({
        cfg,
        replyToId: "msg-internal-1",
        threadId: "1712345678.123456",
      }),
    ).toEqual({
      replyToId: "1712345678.123456",
      threadId: null,
    });
  });

  it("forwards mediaLocalRoots for sendMedia", async () => {
    const sendSlack = vi.fn().mockResolvedValue({ messageId: "m-media-local" });
    const sendMedia = requireSlackSendMedia();
    const mediaLocalRoots = ["/tmp/workspace"];

    const result = await sendMedia({
      cfg,
      to: "C999",
      text: "caption",
      mediaUrl: "/tmp/workspace/image.png",
      mediaLocalRoots,
      accountId: "default",
      deps: { sendSlack },
    });

    expect(sendSlack).toHaveBeenCalledWith(
      "C999",
      "caption",
      expect.objectContaining({
        mediaUrl: "/tmp/workspace/image.png",
        mediaLocalRoots,
      }),
    );
    expect(result).toEqual({ channel: "slack", messageId: "m-media-local" });
  });

  it("sends block payload media first, then the final block message", async () => {
    const sendSlack = vi
      .fn()
      .mockResolvedValueOnce({ messageId: "m-media-1" })
      .mockResolvedValueOnce({ messageId: "m-media-2" })
      .mockResolvedValueOnce({ messageId: "m-final" });
    const sendPayload = requireSlackSendPayload();

    const result = await sendPayload({
      cfg,
      to: "C999",
      text: "",
      payload: {
        text: "hello",
        mediaUrls: ["https://example.com/1.png", "https://example.com/2.png"],
        presentation: {
          blocks: [{ type: "text", text: "Block body" }],
        },
      },
      accountId: "default",
      deps: { sendSlack },
      mediaLocalRoots: ["/tmp/media"],
    });

    expect(sendSlack).toHaveBeenCalledTimes(3);
    expect(sendSlack).toHaveBeenNthCalledWith(
      1,
      "C999",
      "",
      expect.objectContaining({
        mediaUrl: "https://example.com/1.png",
        mediaLocalRoots: ["/tmp/media"],
      }),
    );
    expect(sendSlack).toHaveBeenNthCalledWith(
      2,
      "C999",
      "",
      expect.objectContaining({
        mediaUrl: "https://example.com/2.png",
        mediaLocalRoots: ["/tmp/media"],
      }),
    );
    expect(sendSlack).toHaveBeenNthCalledWith(
      3,
      "C999",
      "hello",
      expect.objectContaining({
        blocks: [
          {
            type: "section",
            text: {
              type: "mrkdwn",
              text: "Block body",
            },
          },
        ],
      }),
    );
    expect(result).toEqual({ channel: "slack", messageId: "m-final" });
  });

  it("renders shared interactive payloads into Slack Block Kit via plugin outbound", async () => {
    const sendSlack = vi.fn().mockResolvedValue({ messageId: "m-interactive" });
    const sendPayload = requireSlackSendPayload();

    const result = await sendPayload({
      cfg,
      to: "user:U123",
      text: "",
      payload: {
        text: "Slack interactive smoke.",
        interactive: {
          blocks: [
            {
              type: "text",
              text: "Slack interactive smoke.",
            },
            {
              type: "buttons",
              buttons: [
                { label: "Approve", value: "approve" },
                { label: "Reject", value: "reject" },
              ],
            },
            {
              type: "select",
              placeholder: "Choose a target",
              options: [
                { label: "Canary", value: "canary" },
                { label: "Production", value: "production" },
              ],
            },
          ],
        },
      },
      accountId: "default",
      deps: { sendSlack },
    });

    expect(sendSlack).toHaveBeenCalledWith(
      "user:U123",
      "Slack interactive smoke.",
      expect.objectContaining({
        blocks: [
          expect.objectContaining({
            type: "section",
          }),
          expect.objectContaining({
            type: "actions",
            elements: [
              expect.objectContaining({ type: "button", value: "approve" }),
              expect.objectContaining({ type: "button", value: "reject" }),
            ],
          }),
          expect.objectContaining({
            type: "actions",
            elements: [
              expect.objectContaining({
                type: "static_select",
                options: [
                  expect.objectContaining({ value: "canary" }),
                  expect.objectContaining({ value: "production" }),
                ],
              }),
            ],
          }),
        ],
      }),
    );
    expect(result).toEqual({ channel: "slack", messageId: "m-interactive" });
  });
});

describe("slackPlugin directory", () => {
  it("lists configured peers without throwing a ReferenceError", async () => {
    const listPeers = requireSlackListPeers();

    await expect(
      listPeers({
        cfg: {
          channels: {
            slack: {
              dms: {
                U123: {},
              },
            },
          },
        },
        runtime: createRuntimeEnv(),
      }),
    ).resolves.toEqual([{ id: "user:u123", kind: "user" }]);
  });
});

describe("slackPlugin agentPrompt", () => {
  it("tells agents interactive replies are disabled by default", () => {
    const hints = slackPlugin.agentPrompt?.messageToolHints?.({
      cfg: {
        channels: {
          slack: {
            botToken: "xoxb-test",
            appToken: "xapp-test",
          },
        },
      },
    });

    expect(hints).toEqual([
      "- Slack interactive replies are disabled. If needed, ask to set `channels.slack.capabilities.interactiveReplies=true` (or the same under `channels.slack.accounts.<account>.capabilities`).",
    ]);
  });

  it("shows Slack interactive reply directives when enabled", () => {
    const hints = slackPlugin.agentPrompt?.messageToolHints?.({
      cfg: {
        channels: {
          slack: {
            botToken: "xoxb-test",
            appToken: "xapp-test",
            capabilities: { interactiveReplies: true },
          },
        },
      },
    });

    expect(hints).toContain(
      "- Prefer Slack buttons/selects for 2-5 discrete choices or parameter picks instead of asking the user to type one.",
    );
    expect(hints).toContain(
      "- Slack interactive replies: use `[[slack_buttons: Label:value, Other:other]]` to add action buttons that route clicks back as Slack interaction system events.",
    );
    expect(hints).toContain(
      "- Slack selects: use `[[slack_select: Placeholder | Label:value, Other:other]]` to add a static select menu that routes the chosen value back as a Slack interaction system event.",
    );
  });
});

describe("slackPlugin outbound new targets", () => {
  const cfg = {
    channels: {
      slack: {
        botToken: "xoxb-test",
        appToken: "xapp-test",
      },
    },
  };

  it("sends to a new user target via DM without erroring", async () => {
    const sendSlack = vi.fn().mockResolvedValue({ messageId: "m-new-user", channelId: "D999" });
    const sendText = requireSlackSendText();

    const result = await sendText({
      cfg,
      to: "user:U99NEW",
      text: "hello new user",
      accountId: "default",
      deps: { sendSlack },
    });

    expect(sendSlack).toHaveBeenCalledWith(
      "user:U99NEW",
      "hello new user",
      expect.objectContaining({ cfg }),
    );
    expect(result).toEqual({ channel: "slack", messageId: "m-new-user", channelId: "D999" });
  });

  it("sends to a new channel target without erroring", async () => {
    const sendSlack = vi.fn().mockResolvedValue({ messageId: "m-new-chan", channelId: "C555" });
    const sendText = requireSlackSendText();

    const result = await sendText({
      cfg,
      to: "channel:C555NEW",
      text: "hello channel",
      accountId: "default",
      deps: { sendSlack },
    });

    expect(sendSlack).toHaveBeenCalledWith(
      "channel:C555NEW",
      "hello channel",
      expect.objectContaining({ cfg }),
    );
    expect(result).toEqual({ channel: "slack", messageId: "m-new-chan", channelId: "C555" });
  });

  it("sends media to a new user target without erroring", async () => {
    const sendSlack = vi.fn().mockResolvedValue({ messageId: "m-new-media", channelId: "D888" });
    const sendMedia = requireSlackSendMedia();

    const result = await sendMedia({
      cfg,
      to: "user:U88NEW",
      text: "here is a file",
      mediaUrl: "https://example.com/file.png",
      accountId: "default",
      deps: { sendSlack },
    });

    expect(sendSlack).toHaveBeenCalledWith(
      "user:U88NEW",
      "here is a file",
      expect.objectContaining({
        cfg,
        mediaUrl: "https://example.com/file.png",
      }),
    );
    expect(result).toEqual({ channel: "slack", messageId: "m-new-media", channelId: "D888" });
  });
});

describe("slackPlugin config", () => {
  it("treats HTTP mode accounts with bot token + signing secret as configured", async () => {
    const cfg: OpenClawConfig = {
      channels: {
        slack: {
          mode: "http",
          botToken: "xoxb-http",
          signingSecret: "secret-http", // pragma: allowlist secret
        },
      },
    };

    const { configured, snapshot } = await getSlackConfiguredState(cfg);

    expect(configured).toBe(true);
    expect(snapshot?.configured).toBe(true);
  });

  it("keeps socket mode requiring app token", async () => {
    const cfg: OpenClawConfig = {
      channels: {
        slack: {
          mode: "socket",
          botToken: "xoxb-socket",
        },
      },
    };

    const { configured, snapshot } = await getSlackConfiguredState(cfg);

    expect(configured).toBe(false);
    expect(snapshot?.configured).toBe(false);
  });

  it("does not mark partial configured-unavailable token status as configured", async () => {
    const snapshot = await slackPlugin.status?.buildAccountSnapshot?.({
      account: {
        accountId: "default",
        name: "Default",
        enabled: true,
        configured: false,
        botTokenStatus: "configured_unavailable",
        appTokenStatus: "missing",
        botTokenSource: "config",
        appTokenSource: "none",
        config: {},
      } as never,
      cfg: {} as OpenClawConfig,
      runtime: undefined,
    });

    expect(snapshot?.configured).toBe(false);
    expect(snapshot?.botTokenStatus).toBe("configured_unavailable");
    expect(snapshot?.appTokenStatus).toBe("missing");
  });

  it("keeps HTTP mode signing-secret unavailable accounts configured in snapshots", async () => {
    const snapshot = await slackPlugin.status?.buildAccountSnapshot?.({
      account: {
        accountId: "default",
        name: "Default",
        enabled: true,
        configured: true,
        mode: "http",
        botTokenStatus: "available",
        signingSecretStatus: "configured_unavailable", // pragma: allowlist secret
        botTokenSource: "config",
        signingSecretSource: "config", // pragma: allowlist secret
        config: {
          mode: "http",
          botToken: "xoxb-http",
          signingSecret: { source: "env", provider: "default", id: "SLACK_SIGNING_SECRET" },
        },
      } as never,
      cfg: {} as OpenClawConfig,
      runtime: undefined,
    });

    expect(snapshot?.configured).toBe(true);
    expect(snapshot?.botTokenStatus).toBe("available");
    expect(snapshot?.signingSecretStatus).toBe("configured_unavailable");
  });
});

¤ Dauer der Verarbeitung: 0.27 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.