mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:20:43 +00:00
test: use synthetic outbound core fixtures
This commit is contained in:
@@ -15,15 +15,15 @@ class TestSeparator {
|
||||
constructor(readonly options: { divider: boolean; spacing: string }) {}
|
||||
}
|
||||
|
||||
class TestDiscordUiContainer {
|
||||
class TestRichUiContainer {
|
||||
constructor(readonly components: Array<TestTextDisplay | TestSeparator>) {}
|
||||
}
|
||||
|
||||
const discordCrossContextPlugin: Pick<
|
||||
const richCrossContextPlugin: Pick<
|
||||
ChannelPlugin,
|
||||
"id" | "meta" | "capabilities" | "config" | "messaging"
|
||||
> = {
|
||||
...createChannelTestPluginBase({ id: "discord" }),
|
||||
...createChannelTestPluginBase({ id: "rich-chat" }),
|
||||
messaging: {
|
||||
buildCrossContextComponents: ({ originLabel, message, cfg, accountId }) => {
|
||||
const trimmed = message.trim();
|
||||
@@ -35,7 +35,7 @@ const discordCrossContextPlugin: Pick<
|
||||
components.push(new TestTextDisplay(`*From ${originLabel}*`));
|
||||
void cfg;
|
||||
void accountId;
|
||||
return [new TestDiscordUiContainer(components)];
|
||||
return [new TestRichUiContainer(components)];
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -44,38 +44,38 @@ describe("getChannelMessageAdapter", () => {
|
||||
beforeEach(() => {
|
||||
setActivePluginRegistry(
|
||||
createTestRegistry([
|
||||
{ pluginId: "discord", plugin: discordCrossContextPlugin, source: "test" },
|
||||
{ pluginId: "rich-chat", plugin: richCrossContextPlugin, source: "test" },
|
||||
{
|
||||
pluginId: "telegram",
|
||||
plugin: createChannelTestPluginBase({ id: "telegram" }),
|
||||
pluginId: "plain-chat",
|
||||
plugin: createChannelTestPluginBase({ id: "plain-chat" }),
|
||||
source: "test",
|
||||
},
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("returns the default adapter for non-discord channels", () => {
|
||||
expect(getChannelMessageAdapter("telegram")).toEqual({
|
||||
it("returns the default adapter for channels without structured component support", () => {
|
||||
expect(getChannelMessageAdapter("plain-chat")).toEqual({
|
||||
supportsComponentsV2: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("returns the discord adapter with a cross-context component builder", () => {
|
||||
const adapter = getChannelMessageAdapter("discord");
|
||||
it("returns an adapter with a cross-context component builder", () => {
|
||||
const adapter = getChannelMessageAdapter("rich-chat");
|
||||
|
||||
expect(adapter.supportsComponentsV2).toBe(true);
|
||||
expect(adapter.buildCrossContextComponents).toBeTypeOf("function");
|
||||
|
||||
const components = adapter.buildCrossContextComponents?.({
|
||||
originLabel: "Telegram",
|
||||
originLabel: "Forum",
|
||||
message: "Hello from chat",
|
||||
cfg: {} as never,
|
||||
accountId: "primary",
|
||||
});
|
||||
const container = components?.[0] as TestDiscordUiContainer | undefined;
|
||||
const container = components?.[0] as TestRichUiContainer | undefined;
|
||||
|
||||
expect(components).toHaveLength(1);
|
||||
expect(container).toBeInstanceOf(TestDiscordUiContainer);
|
||||
expect(container).toBeInstanceOf(TestRichUiContainer);
|
||||
expect(container?.components).toEqual([
|
||||
expect.any(TestTextDisplay),
|
||||
expect.any(TestSeparator),
|
||||
@@ -86,7 +86,7 @@ describe("getChannelMessageAdapter", () => {
|
||||
it.each([
|
||||
{
|
||||
message: "Hello from chat",
|
||||
originLabel: "Telegram",
|
||||
originLabel: "Forum",
|
||||
accountId: "primary",
|
||||
expectedComponents: [
|
||||
expect.any(TestTextDisplay),
|
||||
@@ -96,20 +96,20 @@ describe("getChannelMessageAdapter", () => {
|
||||
},
|
||||
{
|
||||
message: " ",
|
||||
originLabel: "Signal",
|
||||
originLabel: "Pager",
|
||||
expectedComponents: [expect.any(TestTextDisplay)],
|
||||
},
|
||||
])(
|
||||
"builds cross-context components for %j",
|
||||
({ message, originLabel, accountId, expectedComponents }) => {
|
||||
const adapter = getChannelMessageAdapter("discord");
|
||||
const adapter = getChannelMessageAdapter("rich-chat");
|
||||
const components = adapter.buildCrossContextComponents?.({
|
||||
originLabel,
|
||||
message,
|
||||
cfg: {} as never,
|
||||
...(accountId ? { accountId } : {}),
|
||||
});
|
||||
const container = components?.[0] as TestDiscordUiContainer | undefined;
|
||||
const container = components?.[0] as TestRichUiContainer | undefined;
|
||||
|
||||
expect(components).toHaveLength(1);
|
||||
expect(container?.components).toEqual(expectedComponents);
|
||||
|
||||
@@ -83,7 +83,7 @@ describe("outbound channel resolution", () => {
|
||||
typeof value === "string" ? value.trim().toLowerCase() : undefined,
|
||||
);
|
||||
isDeliverableMessageChannelMock.mockImplementation((value?: string) =>
|
||||
["telegram", "discord", "slack"].includes(String(value)),
|
||||
["alpha", "beta", "gamma"].includes(String(value)),
|
||||
);
|
||||
getActivePluginRegistryMock.mockReturnValue({ channels: [] });
|
||||
getActivePluginChannelRegistryMock.mockReturnValue({ channels: [] });
|
||||
@@ -100,7 +100,7 @@ describe("outbound channel resolution", () => {
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ input: " Telegram ", expected: "telegram" },
|
||||
{ input: " Alpha ", expected: "alpha" },
|
||||
{ input: "unknown", expected: undefined },
|
||||
{ input: null, expected: undefined },
|
||||
])("normalizes deliverable outbound channel for %j", async ({ input, expected }) => {
|
||||
@@ -109,13 +109,13 @@ describe("outbound channel resolution", () => {
|
||||
});
|
||||
|
||||
it("returns the already-registered plugin without bootstrapping", async () => {
|
||||
const plugin = { id: "telegram" };
|
||||
const plugin = { id: "alpha" };
|
||||
getLoadedChannelPluginMock.mockReturnValueOnce(plugin);
|
||||
const channelResolution = await importChannelResolution("existing-plugin");
|
||||
|
||||
expect(
|
||||
channelResolution.resolveOutboundChannelPlugin({
|
||||
channel: "telegram",
|
||||
channel: "alpha",
|
||||
cfg: {} as never,
|
||||
}),
|
||||
).toBe(plugin);
|
||||
@@ -123,7 +123,7 @@ describe("outbound channel resolution", () => {
|
||||
});
|
||||
|
||||
it("falls back to the active registry when getChannelPlugin misses", async () => {
|
||||
const plugin = { id: "telegram" };
|
||||
const plugin = { id: "alpha" };
|
||||
getChannelPluginMock.mockReturnValue(undefined);
|
||||
getActivePluginRegistryMock.mockReturnValue({
|
||||
channels: [{ plugin }],
|
||||
@@ -135,20 +135,20 @@ describe("outbound channel resolution", () => {
|
||||
|
||||
expect(
|
||||
channelResolution.resolveOutboundChannelPlugin({
|
||||
channel: "telegram",
|
||||
channel: "alpha",
|
||||
cfg: {} as never,
|
||||
}),
|
||||
).toBe(plugin);
|
||||
});
|
||||
|
||||
it("bootstraps plugins once per registry key and returns the newly loaded plugin", async () => {
|
||||
const plugin = { id: "telegram" };
|
||||
const plugin = { id: "alpha" };
|
||||
getLoadedChannelPluginMock.mockReturnValueOnce(undefined).mockReturnValueOnce(plugin);
|
||||
const channelResolution = await importChannelResolution("bootstrap-success");
|
||||
|
||||
expect(
|
||||
channelResolution.resolveOutboundChannelPlugin({
|
||||
channel: "telegram",
|
||||
channel: "alpha",
|
||||
cfg: { channels: {} } as never,
|
||||
}),
|
||||
).toBe(plugin);
|
||||
@@ -156,7 +156,7 @@ describe("outbound channel resolution", () => {
|
||||
|
||||
getChannelPluginMock.mockReturnValue(undefined);
|
||||
channelResolution.resolveOutboundChannelPlugin({
|
||||
channel: "telegram",
|
||||
channel: "alpha",
|
||||
cfg: { channels: {} } as never,
|
||||
});
|
||||
expect(resolveRuntimePluginRegistryMock).toHaveBeenCalledTimes(1);
|
||||
@@ -164,19 +164,19 @@ describe("outbound channel resolution", () => {
|
||||
});
|
||||
|
||||
it("bootstraps when the active registry has other channels but not the requested one", async () => {
|
||||
const plugin = { id: "telegram" };
|
||||
const plugin = { id: "alpha" };
|
||||
getLoadedChannelPluginMock.mockReturnValueOnce(undefined).mockReturnValueOnce(plugin);
|
||||
getActivePluginRegistryMock.mockReturnValue({
|
||||
channels: [{ plugin: { id: "discord" } }],
|
||||
channels: [{ plugin: { id: "beta" } }],
|
||||
});
|
||||
getActivePluginChannelRegistryMock.mockReturnValue({
|
||||
channels: [{ plugin: { id: "discord" } }],
|
||||
channels: [{ plugin: { id: "beta" } }],
|
||||
});
|
||||
const channelResolution = await importChannelResolution("bootstrap-missing-target");
|
||||
|
||||
expect(
|
||||
channelResolution.resolveOutboundChannelPlugin({
|
||||
channel: "telegram",
|
||||
channel: "alpha",
|
||||
cfg: { channels: {} } as never,
|
||||
}),
|
||||
).toBe(plugin);
|
||||
@@ -192,13 +192,13 @@ describe("outbound channel resolution", () => {
|
||||
|
||||
expect(
|
||||
channelResolution.resolveOutboundChannelPlugin({
|
||||
channel: "telegram",
|
||||
channel: "alpha",
|
||||
cfg: { channels: {} } as never,
|
||||
}),
|
||||
).toBeUndefined();
|
||||
|
||||
channelResolution.resolveOutboundChannelPlugin({
|
||||
channel: "telegram",
|
||||
channel: "alpha",
|
||||
cfg: { channels: {} } as never,
|
||||
});
|
||||
expect(resolveRuntimePluginRegistryMock).toHaveBeenCalledTimes(2);
|
||||
@@ -209,14 +209,14 @@ describe("outbound channel resolution", () => {
|
||||
const channelResolution = await importChannelResolution("channel-version-change");
|
||||
|
||||
channelResolution.resolveOutboundChannelPlugin({
|
||||
channel: "telegram",
|
||||
channel: "alpha",
|
||||
cfg: { channels: {} } as never,
|
||||
});
|
||||
expect(resolveRuntimePluginRegistryMock).toHaveBeenCalledTimes(1);
|
||||
|
||||
getActivePluginChannelRegistryVersionMock.mockReturnValue(2);
|
||||
channelResolution.resolveOutboundChannelPlugin({
|
||||
channel: "telegram",
|
||||
channel: "alpha",
|
||||
cfg: { channels: {} } as never,
|
||||
});
|
||||
expect(resolveRuntimePluginRegistryMock).toHaveBeenCalledTimes(2);
|
||||
|
||||
@@ -5,11 +5,20 @@ const mocks = vi.hoisted(() => ({
|
||||
resolveOutboundChannelPlugin: vi.fn(),
|
||||
}));
|
||||
|
||||
const deliverableChannelIds = vi.hoisted(() => ["alpha", "beta", "gamma", "delta", "muted"]);
|
||||
|
||||
vi.mock("../../channels/plugins/index.js", () => ({
|
||||
getLoadedChannelPlugin: vi.fn(),
|
||||
listChannelPlugins: mocks.listChannelPlugins,
|
||||
}));
|
||||
|
||||
vi.mock("../../utils/message-channel.js", () => ({
|
||||
listDeliverableMessageChannels: () => deliverableChannelIds,
|
||||
isDeliverableMessageChannel: (value: string) => deliverableChannelIds.includes(value),
|
||||
normalizeMessageChannel: (value?: string | null) =>
|
||||
typeof value === "string" ? value.trim().toLowerCase() : undefined,
|
||||
}));
|
||||
|
||||
vi.mock("./channel-resolution.js", () => ({
|
||||
resolveOutboundChannelPlugin: mocks.resolveOutboundChannelPlugin,
|
||||
}));
|
||||
@@ -70,37 +79,37 @@ describe("listConfiguredMessageChannels", () => {
|
||||
|
||||
it.each([
|
||||
{
|
||||
plugins: [makePlugin({ id: "not-a-channel" }), makePlugin({ id: "slack", accountIds: [] })],
|
||||
plugins: [makePlugin({ id: "not-a-channel" }), makePlugin({ id: "alpha", accountIds: [] })],
|
||||
expected: [],
|
||||
expectedErrors: 0,
|
||||
},
|
||||
{
|
||||
plugins: [
|
||||
makePlugin({
|
||||
id: "discord",
|
||||
id: "beta",
|
||||
resolveAccount: () => ({ enabled: true }),
|
||||
}),
|
||||
],
|
||||
expected: ["discord"],
|
||||
expected: ["beta"],
|
||||
expectedErrors: 0,
|
||||
},
|
||||
{
|
||||
plugins: [
|
||||
makePlugin({
|
||||
id: "telegram",
|
||||
id: "gamma",
|
||||
accountIds: ["disabled", "enabled"],
|
||||
resolveAccount: (accountId) =>
|
||||
accountId === "disabled" ? { enabled: false } : { enabled: true },
|
||||
isConfigured: (account) => (account as { enabled?: boolean }).enabled === true,
|
||||
}),
|
||||
],
|
||||
expected: ["telegram"],
|
||||
expected: ["gamma"],
|
||||
expectedErrors: 0,
|
||||
},
|
||||
{
|
||||
plugins: [
|
||||
makePlugin({
|
||||
id: "signal",
|
||||
id: "muted",
|
||||
resolveAccount: () => ({ token: "x" }),
|
||||
isEnabled: () => false,
|
||||
isConfigured: () => true,
|
||||
@@ -112,7 +121,7 @@ describe("listConfiguredMessageChannels", () => {
|
||||
{
|
||||
plugins: [
|
||||
makePlugin({
|
||||
id: "discord",
|
||||
id: "beta",
|
||||
resolveAccount: () => {
|
||||
throw new Error("boom");
|
||||
},
|
||||
@@ -136,9 +145,9 @@ describe("resolveMessageChannelSelection", () => {
|
||||
|
||||
it.each([
|
||||
{
|
||||
params: { cfg: {} as never, channel: "telegram" },
|
||||
params: { cfg: {} as never, channel: "alpha" },
|
||||
expected: {
|
||||
channel: "telegram",
|
||||
channel: "alpha",
|
||||
configured: [],
|
||||
source: "explicit",
|
||||
},
|
||||
@@ -146,12 +155,12 @@ describe("resolveMessageChannelSelection", () => {
|
||||
{
|
||||
setup: () => {
|
||||
const isConfigured = vi.fn(async () => true);
|
||||
mocks.listChannelPlugins.mockReturnValue([makePlugin({ id: "slack", isConfigured })]);
|
||||
mocks.listChannelPlugins.mockReturnValue([makePlugin({ id: "beta", isConfigured })]);
|
||||
return { isConfigured };
|
||||
},
|
||||
params: { cfg: {} as never, channel: "slack" },
|
||||
params: { cfg: {} as never, channel: "beta" },
|
||||
expected: {
|
||||
channel: "slack",
|
||||
channel: "beta",
|
||||
configured: [],
|
||||
source: "explicit",
|
||||
},
|
||||
@@ -160,17 +169,17 @@ describe("resolveMessageChannelSelection", () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
params: { cfg: {} as never, channel: "channel:C123", fallbackChannel: "slack" },
|
||||
params: { cfg: {} as never, channel: "channel:C123", fallbackChannel: "beta" },
|
||||
expected: {
|
||||
channel: "slack",
|
||||
channel: "beta",
|
||||
configured: [],
|
||||
source: "tool-context-fallback",
|
||||
},
|
||||
},
|
||||
{
|
||||
params: { cfg: {} as never, fallbackChannel: "signal" },
|
||||
params: { cfg: {} as never, fallbackChannel: "gamma" },
|
||||
expected: {
|
||||
channel: "signal",
|
||||
channel: "gamma",
|
||||
configured: [],
|
||||
source: "tool-context-fallback",
|
||||
},
|
||||
@@ -178,25 +187,25 @@ describe("resolveMessageChannelSelection", () => {
|
||||
{
|
||||
setup: () => {
|
||||
mocks.listChannelPlugins.mockReturnValue([
|
||||
makePlugin({ id: "discord", isConfigured: async () => true }),
|
||||
makePlugin({ id: "delta", isConfigured: async () => true }),
|
||||
]);
|
||||
},
|
||||
params: { cfg: {} as never },
|
||||
expected: {
|
||||
channel: "discord",
|
||||
configured: ["discord"],
|
||||
channel: "delta",
|
||||
configured: ["delta"],
|
||||
source: "single-configured",
|
||||
},
|
||||
},
|
||||
{
|
||||
setup: () => {
|
||||
mocks.resolveOutboundChannelPlugin.mockImplementation(({ channel }: { channel: string }) =>
|
||||
channel === "slack" ? { id: "slack" } : undefined,
|
||||
channel === "beta" ? { id: "beta" } : undefined,
|
||||
);
|
||||
},
|
||||
params: { cfg: {} as never, channel: "discord", fallbackChannel: "slack" },
|
||||
params: { cfg: {} as never, channel: "alpha", fallbackChannel: "beta" },
|
||||
expected: {
|
||||
channel: "slack",
|
||||
channel: "beta",
|
||||
configured: [],
|
||||
source: "tool-context-fallback",
|
||||
},
|
||||
@@ -216,8 +225,8 @@ describe("resolveMessageChannelSelection", () => {
|
||||
setup: () => {
|
||||
mocks.resolveOutboundChannelPlugin.mockReturnValue(undefined);
|
||||
},
|
||||
params: { cfg: {} as never, channel: "discord" },
|
||||
expectedMessage: "Channel is unavailable: discord",
|
||||
params: { cfg: {} as never, channel: "alpha" },
|
||||
expectedMessage: "Channel is unavailable: alpha",
|
||||
},
|
||||
{
|
||||
params: { cfg: {} as never },
|
||||
@@ -226,13 +235,12 @@ describe("resolveMessageChannelSelection", () => {
|
||||
{
|
||||
setup: () => {
|
||||
mocks.listChannelPlugins.mockReturnValue([
|
||||
makePlugin({ id: "discord", isConfigured: async () => true }),
|
||||
makePlugin({ id: "telegram", isConfigured: async () => true }),
|
||||
makePlugin({ id: "beta", isConfigured: async () => true }),
|
||||
makePlugin({ id: "gamma", isConfigured: async () => true }),
|
||||
]);
|
||||
},
|
||||
params: { cfg: {} as never },
|
||||
expectedMessage:
|
||||
"Channel is required when multiple channels are configured: discord, telegram",
|
||||
expectedMessage: "Channel is required when multiple channels are configured: beta, gamma",
|
||||
},
|
||||
])("rejects invalid channel selection for %j", async ({ setup, params, expectedMessage }) => {
|
||||
setup?.();
|
||||
|
||||
@@ -32,27 +32,27 @@ vi.mock("./message-action-threading.js", async () => {
|
||||
await import("./message-action-threading.test-helpers.js");
|
||||
return createOutboundThreadingMock();
|
||||
});
|
||||
const telegramConfig = {
|
||||
const pollerConfig = {
|
||||
channels: {
|
||||
telegram: {
|
||||
botToken: "telegram-test",
|
||||
poller: {
|
||||
botToken: "poller-test",
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
const telegramPollTestPlugin: ChannelPlugin = {
|
||||
id: "telegram",
|
||||
const pollerTestPlugin: ChannelPlugin = {
|
||||
id: "poller",
|
||||
meta: {
|
||||
id: "telegram",
|
||||
label: "Telegram",
|
||||
selectionLabel: "Telegram",
|
||||
docsPath: "/channels/telegram",
|
||||
blurb: "Telegram poll test plugin.",
|
||||
id: "poller",
|
||||
label: "Poller",
|
||||
selectionLabel: "Poller",
|
||||
docsPath: "/channels/poller",
|
||||
blurb: "Poller test plugin.",
|
||||
},
|
||||
capabilities: { chatTypes: ["direct", "group"] },
|
||||
config: {
|
||||
listAccountIds: () => ["default"],
|
||||
resolveAccount: () => ({ botToken: "telegram-test" }),
|
||||
resolveAccount: () => ({ botToken: "poller-test" }),
|
||||
isConfigured: () => true,
|
||||
},
|
||||
outbound: {
|
||||
@@ -119,9 +119,9 @@ describe("runMessageAction poll handling", () => {
|
||||
setActivePluginRegistry(
|
||||
createTestRegistry([
|
||||
{
|
||||
pluginId: "telegram",
|
||||
pluginId: "poller",
|
||||
source: "test",
|
||||
plugin: telegramPollTestPlugin,
|
||||
plugin: pollerTestPlugin,
|
||||
},
|
||||
]),
|
||||
);
|
||||
@@ -146,10 +146,10 @@ describe("runMessageAction poll handling", () => {
|
||||
it("requires at least two poll options", async () => {
|
||||
await expect(
|
||||
runPollAction({
|
||||
cfg: telegramConfig,
|
||||
cfg: pollerConfig,
|
||||
actionParams: {
|
||||
channel: "telegram",
|
||||
target: "telegram:123",
|
||||
channel: "poller",
|
||||
target: "poller:123",
|
||||
pollQuestion: "Lunch?",
|
||||
pollOption: ["Pizza"],
|
||||
},
|
||||
@@ -160,16 +160,16 @@ describe("runMessageAction poll handling", () => {
|
||||
|
||||
it("passes shared poll fields and auto threadId to executePollAction", async () => {
|
||||
const call = await runPollAction({
|
||||
cfg: telegramConfig,
|
||||
cfg: pollerConfig,
|
||||
actionParams: {
|
||||
channel: "telegram",
|
||||
target: "telegram:123",
|
||||
channel: "poller",
|
||||
target: "poller:123",
|
||||
pollQuestion: "Lunch?",
|
||||
pollOption: ["Pizza", "Sushi"],
|
||||
pollDurationHours: 2,
|
||||
},
|
||||
toolContext: {
|
||||
currentChannelId: "telegram:123",
|
||||
currentChannelId: "poller:123",
|
||||
currentThreadTs: "42",
|
||||
},
|
||||
});
|
||||
@@ -181,10 +181,10 @@ describe("runMessageAction poll handling", () => {
|
||||
|
||||
it("expands maxSelections when pollMulti is enabled", async () => {
|
||||
const call = await runPollAction({
|
||||
cfg: telegramConfig,
|
||||
cfg: pollerConfig,
|
||||
actionParams: {
|
||||
channel: "telegram",
|
||||
target: "telegram:123",
|
||||
channel: "poller",
|
||||
target: "poller:123",
|
||||
pollQuestion: "Lunch?",
|
||||
pollOption: ["Pizza", "Sushi", "Soup"],
|
||||
pollMulti: true,
|
||||
@@ -196,10 +196,10 @@ describe("runMessageAction poll handling", () => {
|
||||
|
||||
it("defaults maxSelections to one choice when pollMulti is omitted", async () => {
|
||||
const call = await runPollAction({
|
||||
cfg: telegramConfig,
|
||||
cfg: pollerConfig,
|
||||
actionParams: {
|
||||
channel: "telegram",
|
||||
target: "telegram:123",
|
||||
channel: "poller",
|
||||
target: "poller:123",
|
||||
pollQuestion: "Lunch?",
|
||||
pollOption: ["Pizza", "Sushi", "Soup"],
|
||||
},
|
||||
|
||||
@@ -8,24 +8,24 @@ import {
|
||||
const ensureOutboundSessionEntry = vi.fn(async () => undefined);
|
||||
const resolveOutboundSessionRoute = vi.fn();
|
||||
|
||||
const slackConfig = {
|
||||
const workspaceConfig = {
|
||||
channels: {
|
||||
slack: {
|
||||
workspace: {
|
||||
botToken: "xoxb-test",
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
const telegramConfig = {
|
||||
const forumConfig = {
|
||||
channels: {
|
||||
telegram: {
|
||||
botToken: "telegram-test",
|
||||
forum: {
|
||||
botToken: "forum-test",
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
const defaultTelegramToolContext = {
|
||||
currentChannelId: "telegram:123",
|
||||
const defaultForumToolContext = {
|
||||
currentChannelId: "forum:123",
|
||||
currentThreadTs: "42",
|
||||
} as const;
|
||||
|
||||
@@ -40,17 +40,17 @@ describe("message action threading helpers", () => {
|
||||
name: "exact channel id",
|
||||
target: "channel:C123",
|
||||
threadTs: "111.222",
|
||||
expectedSessionKey: "agent:main:slack:channel:c123:thread:111.222",
|
||||
expectedSessionKey: "agent:main:workspace:channel:c123:thread:111.222",
|
||||
},
|
||||
{
|
||||
name: "case-insensitive channel id",
|
||||
target: "channel:c123",
|
||||
threadTs: "333.444",
|
||||
expectedSessionKey: "agent:main:slack:channel:c123:thread:333.444",
|
||||
expectedSessionKey: "agent:main:workspace:channel:c123:thread:333.444",
|
||||
},
|
||||
] as const)("prepares outbound routes for slack using $name", async (testCase) => {
|
||||
] as const)("prepares outbound routes for workspace using $name", async (testCase) => {
|
||||
const actionParams: Record<string, unknown> = {
|
||||
channel: "slack",
|
||||
channel: "workspace",
|
||||
target: testCase.target,
|
||||
message: "hi",
|
||||
};
|
||||
@@ -65,8 +65,8 @@ describe("message action threading helpers", () => {
|
||||
});
|
||||
|
||||
const result = await prepareOutboundMirrorRoute({
|
||||
cfg: slackConfig,
|
||||
channel: "slack",
|
||||
cfg: workspaceConfig,
|
||||
channel: "workspace",
|
||||
to: testCase.target,
|
||||
actionParams,
|
||||
toolContext: {
|
||||
@@ -89,30 +89,30 @@ describe("message action threading helpers", () => {
|
||||
it.each([
|
||||
{
|
||||
name: "injects threadId for matching target",
|
||||
target: "telegram:123",
|
||||
target: "forum:123",
|
||||
expectedThreadId: "42",
|
||||
},
|
||||
{
|
||||
name: "injects threadId for prefixed group target",
|
||||
target: "telegram:group:123",
|
||||
target: "forum:group:123",
|
||||
expectedThreadId: "42",
|
||||
},
|
||||
{
|
||||
name: "skips threadId when target chat differs",
|
||||
target: "telegram:999",
|
||||
target: "forum:999",
|
||||
expectedThreadId: undefined,
|
||||
},
|
||||
] as const)("telegram auto-threading: $name", (testCase) => {
|
||||
] as const)("forum auto-threading: $name", (testCase) => {
|
||||
const actionParams: Record<string, unknown> = {
|
||||
channel: "telegram",
|
||||
channel: "forum",
|
||||
target: testCase.target,
|
||||
message: "hi",
|
||||
};
|
||||
|
||||
const resolved = resolveAndApplyOutboundThreadId(actionParams, {
|
||||
cfg: telegramConfig,
|
||||
cfg: forumConfig,
|
||||
to: testCase.target,
|
||||
toolContext: defaultTelegramToolContext,
|
||||
toolContext: defaultForumToolContext,
|
||||
resolveAutoThreadId: ({ to, toolContext }) =>
|
||||
to.includes("123") ? toolContext?.currentThreadTs : undefined,
|
||||
});
|
||||
@@ -121,18 +121,18 @@ describe("message action threading helpers", () => {
|
||||
expect(resolved).toBe(testCase.expectedThreadId);
|
||||
});
|
||||
|
||||
it("uses explicit telegram threadId when provided", () => {
|
||||
it("uses explicit forum threadId when provided", () => {
|
||||
const actionParams: Record<string, unknown> = {
|
||||
channel: "telegram",
|
||||
target: "telegram:123",
|
||||
channel: "forum",
|
||||
target: "forum:123",
|
||||
message: "hi",
|
||||
threadId: "999",
|
||||
};
|
||||
|
||||
const resolved = resolveAndApplyOutboundThreadId(actionParams, {
|
||||
cfg: telegramConfig,
|
||||
to: "telegram:123",
|
||||
toolContext: defaultTelegramToolContext,
|
||||
cfg: forumConfig,
|
||||
to: "forum:123",
|
||||
toolContext: defaultForumToolContext,
|
||||
resolveAutoThreadId: () => "42",
|
||||
});
|
||||
|
||||
@@ -143,16 +143,16 @@ describe("message action threading helpers", () => {
|
||||
it("passes explicit replyTo into auto-thread resolution", () => {
|
||||
const resolveAutoThreadId = vi.fn(() => "thread-777");
|
||||
const actionParams: Record<string, unknown> = {
|
||||
channel: "telegram",
|
||||
target: "telegram:123",
|
||||
channel: "forum",
|
||||
target: "forum:123",
|
||||
message: "hi",
|
||||
replyTo: "777",
|
||||
};
|
||||
|
||||
const resolved = resolveAndApplyOutboundThreadId(actionParams, {
|
||||
cfg: telegramConfig,
|
||||
to: "telegram:123",
|
||||
toolContext: defaultTelegramToolContext,
|
||||
cfg: forumConfig,
|
||||
to: "forum:123",
|
||||
toolContext: defaultForumToolContext,
|
||||
resolveAutoThreadId,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user