test: speed up setup surface tests

This commit is contained in:
Peter Steinberger
2026-04-07 13:15:44 +01:00
parent 6fdea7c755
commit dc854ec521
6 changed files with 205 additions and 254 deletions

View File

@@ -1,6 +1,21 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { describe, expect, it } from "vitest";
import { discordSetupWizard } from "./setup-surface.js";
import { createDiscordSetupWizardBase } from "./setup-core.js";
const discordSetupWizard = createDiscordSetupWizardBase({
promptAllowFrom: async ({ cfg }) => cfg,
resolveAllowFromEntries: async ({ entries }) =>
entries.map((entry) => ({
input: entry,
resolved: false,
id: null,
})),
resolveGroupAllowlist: async ({ entries }) =>
entries.map((entry) => ({
input: entry,
resolved: false,
})),
});
describe("discordSetupWizard.dmPolicy", () => {
it("reads the named-account DM policy instead of the channel root", () => {

View File

@@ -13,7 +13,9 @@ import {
import { createStartAccountContext } from "../../../test/helpers/plugins/start-account-context.js";
import type { OpenClawConfig, PluginRuntime, ResolvedLineAccount } from "../api.js";
import { linePlugin } from "./channel.js";
import { probeLineBot } from "./probe.js";
import { clearLineRuntime, setLineRuntime } from "./runtime.js";
import { lineSetupWizard } from "./setup-surface.js";
const { getBotInfoMock, MessagingApiClientMock } = vi.hoisted(() => {
const getBotInfoMock = vi.fn();
@@ -175,8 +177,6 @@ describe("line setup wizard", () => {
});
it("reads the named-account DM policy instead of the channel root", async () => {
const { lineSetupWizard } = await import("./setup-surface.js");
expect(
lineSetupWizard.dmPolicy?.getCurrent(
{
@@ -199,8 +199,6 @@ describe("line setup wizard", () => {
});
it("reports account-scoped config keys for named accounts", async () => {
const { lineSetupWizard } = await import("./setup-surface.js");
expect(lineSetupWizard.dmPolicy?.resolveConfigKeys?.({} as OpenClawConfig, "work")).toEqual({
policyKey: "channels.line.accounts.work.dmPolicy",
allowFromKey: "channels.line.accounts.work.allowFrom",
@@ -208,8 +206,6 @@ describe("line setup wizard", () => {
});
it("uses configured defaultAccount for omitted DM policy account context", async () => {
const { lineSetupWizard } = await import("./setup-surface.js");
const cfg = {
channels: {
line: {
@@ -244,8 +240,6 @@ describe("line setup wizard", () => {
});
it('writes open policy state to the named account and preserves inherited allowFrom with "*"', async () => {
const { lineSetupWizard } = await import("./setup-surface.js");
const next = lineSetupWizard.dmPolicy?.setPolicy(
{
channels: {
@@ -277,8 +271,6 @@ describe("line setup wizard", () => {
});
it("uses configured defaultAccount for omitted setup configured state", async () => {
const { lineSetupWizard } = await import("./setup-surface.js");
const configured = await lineSetupWizard.status.resolveConfigured({
cfg: {
channels: {
@@ -321,7 +313,6 @@ describe("probeLineBot", () => {
});
it("returns timeout when bot info stalls", async () => {
const { probeLineBot } = await import("./probe.js");
vi.useFakeTimers();
getBotInfoMock.mockImplementation(() => new Promise(() => {}));
@@ -334,7 +325,6 @@ describe("probeLineBot", () => {
});
it("returns bot info when available", async () => {
const { probeLineBot } = await import("./probe.js");
getBotInfoMock.mockResolvedValue({
displayName: "OpenClaw",
userId: "U123",
@@ -351,7 +341,6 @@ describe("probeLineBot", () => {
describe("linePlugin status.probeAccount", () => {
it("falls back to the direct probe helper when runtime is not initialized", async () => {
const { probeLineBot } = await import("./probe.js");
MessagingApiClientMock.mockReset();
MessagingApiClientMock.mockImplementation(function () {
return { getBotInfo: getBotInfoMock };

View File

@@ -5,7 +5,18 @@ import {
runSetupWizardFinalize,
type WizardPrompter,
} from "../../../test/helpers/plugins/setup-wizard.js";
import { slackSetupWizard } from "./setup-surface.js";
import { createSlackSetupWizardBase } from "./setup-core.js";
const slackSetupWizard = createSlackSetupWizardBase({
promptAllowFrom: async ({ cfg }) => cfg,
resolveAllowFromEntries: async ({ entries }) =>
entries.map((entry) => ({
input: entry,
resolved: false,
id: null,
})),
resolveGroupAllowlist: async ({ entries }) => entries,
});
describe("slackSetupWizard.finalize", () => {
const baseCfg = {

View File

@@ -0,0 +1,107 @@
import {
addWildcardAllowFrom,
applySetupAccountConfigPatch,
type ChannelSetupDmPolicy,
DEFAULT_ACCOUNT_ID,
type OpenClawConfig,
patchChannelConfigForAccount,
} from "openclaw/plugin-sdk/setup";
import { formatCliCommand, formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
import {
mergeTelegramAccountConfig,
resolveDefaultTelegramAccountId,
resolveTelegramAccount,
} from "./accounts.js";
import { promptTelegramAllowFromForAccount } from "./setup-core.js";
const channel = "telegram" as const;
export function ensureTelegramDefaultGroupMentionGate(
cfg: OpenClawConfig,
accountId: string,
): OpenClawConfig {
const resolved = resolveTelegramAccount({ cfg, accountId });
const wildcardGroup = resolved.config.groups?.["*"];
if (wildcardGroup?.requireMention !== undefined) {
return cfg;
}
return patchChannelConfigForAccount({
cfg,
channel,
accountId,
patch: {
groups: {
...resolved.config.groups,
"*": {
...wildcardGroup,
requireMention: true,
},
},
},
});
}
export function shouldShowTelegramDmAccessWarning(cfg: OpenClawConfig, accountId: string): boolean {
const merged = mergeTelegramAccountConfig(cfg, accountId);
const policy = merged.dmPolicy ?? "pairing";
const hasAllowFrom =
Array.isArray(merged.allowFrom) && merged.allowFrom.some((entry) => String(entry).trim());
return policy === "pairing" && !hasAllowFrom;
}
export function buildTelegramDmAccessWarningLines(accountId: string): string[] {
const configBase =
accountId === DEFAULT_ACCOUNT_ID
? "channels.telegram"
: `channels.telegram.accounts.${accountId}`;
return [
"Your bot is using DM policy: pairing.",
"Any Telegram user who discovers the bot can send pairing requests.",
"For private use, configure an allowlist with your Telegram user id:",
" " + formatCliCommand(`openclaw config set ${configBase}.dmPolicy "allowlist"`),
" " + formatCliCommand(`openclaw config set ${configBase}.allowFrom '["YOUR_USER_ID"]'`),
`Docs: ${formatDocsLink("/channels/pairing", "channels/pairing")}`,
];
}
export const telegramSetupDmPolicy: ChannelSetupDmPolicy = {
label: "Telegram",
channel,
policyKey: "channels.telegram.dmPolicy",
allowFromKey: "channels.telegram.allowFrom",
resolveConfigKeys: (cfg, accountId) =>
(accountId ?? resolveDefaultTelegramAccountId(cfg)) !== DEFAULT_ACCOUNT_ID
? {
policyKey: `channels.telegram.accounts.${accountId ?? resolveDefaultTelegramAccountId(cfg)}.dmPolicy`,
allowFromKey: `channels.telegram.accounts.${accountId ?? resolveDefaultTelegramAccountId(cfg)}.allowFrom`,
}
: {
policyKey: "channels.telegram.dmPolicy",
allowFromKey: "channels.telegram.allowFrom",
},
getCurrent: (cfg, accountId) =>
mergeTelegramAccountConfig(cfg, accountId ?? resolveDefaultTelegramAccountId(cfg)).dmPolicy ??
"pairing",
setPolicy: (cfg, policy, accountId) => {
const resolvedAccountId = accountId ?? resolveDefaultTelegramAccountId(cfg);
const merged = mergeTelegramAccountConfig(cfg, resolvedAccountId);
const patch = {
dmPolicy: policy,
...(policy === "open" ? { allowFrom: addWildcardAllowFrom(merged.allowFrom) } : {}),
};
return accountId == null && resolvedAccountId !== DEFAULT_ACCOUNT_ID
? applySetupAccountConfigPatch({
cfg,
channelKey: channel,
accountId: resolvedAccountId,
patch,
})
: patchChannelConfigForAccount({
cfg,
channel,
accountId: resolvedAccountId,
patch,
});
},
promptAllowFrom: promptTelegramAllowFromForAccount,
};

View File

@@ -1,171 +1,97 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup";
import { describe, expect, it, vi } from "vitest";
import {
createTestWizardPrompter,
runSetupWizardFinalize,
runSetupWizardPrepare,
} from "../../../test/helpers/plugins/setup-wizard.js";
import { resolveTelegramAllowFromEntries } from "./setup-core.js";
import { telegramSetupWizard } from "./setup-surface.js";
import {
buildTelegramDmAccessWarningLines,
ensureTelegramDefaultGroupMentionGate,
shouldShowTelegramDmAccessWarning,
telegramSetupDmPolicy,
} from "./setup-surface.helpers.js";
async function runPrepare(cfg: OpenClawConfig, accountId: string) {
return await runSetupWizardPrepare({
prepare: telegramSetupWizard.prepare,
cfg,
accountId,
options: {},
});
}
async function runFinalize(cfg: OpenClawConfig, accountId: string) {
const note = vi.fn(async () => undefined);
await runSetupWizardFinalize({
finalize: telegramSetupWizard.finalize,
cfg,
accountId,
prompter: createTestWizardPrompter({ note }),
});
return note;
}
function expectPreparedResult(
prepared: Awaited<ReturnType<typeof runPrepare>>,
): { cfg: OpenClawConfig } & Exclude<Awaited<ReturnType<typeof runPrepare>>, void | undefined> {
expect(prepared).toBeDefined();
if (
!prepared ||
typeof prepared !== "object" ||
!("cfg" in prepared) ||
prepared.cfg === undefined
) {
throw new Error("Expected prepare result with cfg");
}
return prepared as { cfg: OpenClawConfig } & Exclude<
Awaited<ReturnType<typeof runPrepare>>,
void | undefined
>;
}
describe("telegramSetupWizard.prepare", () => {
describe("ensureTelegramDefaultGroupMentionGate", () => {
it('adds groups["*"].requireMention=true for fresh setups', async () => {
const prepared = expectPreparedResult(
await runPrepare(
{
channels: {
telegram: {
botToken: "tok",
},
const cfg = ensureTelegramDefaultGroupMentionGate(
{
channels: {
telegram: {
botToken: "tok",
},
},
DEFAULT_ACCOUNT_ID,
),
},
DEFAULT_ACCOUNT_ID,
);
expect(prepared.cfg.channels?.telegram?.groups).toEqual({
expect(cfg.channels?.telegram?.groups).toEqual({
"*": { requireMention: true },
});
});
it("preserves an explicit wildcard group mention setting", async () => {
const prepared = expectPreparedResult(
await runPrepare(
{
channels: {
telegram: {
botToken: "tok",
groups: {
"*": { requireMention: false },
},
const cfg = ensureTelegramDefaultGroupMentionGate(
{
channels: {
telegram: {
botToken: "tok",
groups: {
"*": { requireMention: false },
},
},
},
DEFAULT_ACCOUNT_ID,
),
},
DEFAULT_ACCOUNT_ID,
);
expect(prepared.cfg.channels?.telegram?.groups).toEqual({
expect(cfg.channels?.telegram?.groups).toEqual({
"*": { requireMention: false },
});
});
});
describe("telegramSetupWizard.finalize", () => {
it("shows global config commands for the default account", async () => {
const note = await runFinalize(
{
channels: {
telegram: {
botToken: "tok",
},
},
},
DEFAULT_ACCOUNT_ID,
);
describe("telegram DM access warning helpers", () => {
it("shows global config commands for the default account", () => {
const lines = buildTelegramDmAccessWarningLines(DEFAULT_ACCOUNT_ID);
expect(note).toHaveBeenCalledWith(
expect.stringContaining('openclaw config set channels.telegram.dmPolicy "allowlist"'),
"Telegram DM access warning",
expect(lines.join("\n")).toContain(
'openclaw config set channels.telegram.dmPolicy "allowlist"',
);
expect(note).toHaveBeenCalledWith(
expect.stringContaining(`openclaw config set channels.telegram.allowFrom '["YOUR_USER_ID"]'`),
"Telegram DM access warning",
expect(lines.join("\n")).toContain(
`openclaw config set channels.telegram.allowFrom '["YOUR_USER_ID"]'`,
);
});
it("shows account-scoped config commands for named accounts", async () => {
const note = await runFinalize(
{
channels: {
telegram: {
accounts: {
alerts: {
botToken: "tok",
},
it("shows account-scoped config commands for named accounts", () => {
const lines = buildTelegramDmAccessWarningLines("alerts");
expect(lines.join("\n")).toContain(
'openclaw config set channels.telegram.accounts.alerts.dmPolicy "allowlist"',
);
expect(lines.join("\n")).toContain(
`openclaw config set channels.telegram.accounts.alerts.allowFrom '["YOUR_USER_ID"]'`,
);
});
it("skips the warning when an allowFrom entry already exists", () => {
expect(
shouldShowTelegramDmAccessWarning(
{
channels: {
telegram: {
botToken: "tok",
allowFrom: ["123"],
},
},
},
},
"alerts",
);
expect(note).toHaveBeenCalledWith(
expect.stringContaining(
'openclaw config set channels.telegram.accounts.alerts.dmPolicy "allowlist"',
DEFAULT_ACCOUNT_ID,
),
"Telegram DM access warning",
);
expect(note).toHaveBeenCalledWith(
expect.stringContaining(
`openclaw config set channels.telegram.accounts.alerts.allowFrom '["YOUR_USER_ID"]'`,
),
"Telegram DM access warning",
);
});
it("skips the warning when an allowFrom entry already exists", async () => {
const note = await runFinalize(
{
channels: {
telegram: {
botToken: "tok",
allowFrom: ["123"],
},
},
},
DEFAULT_ACCOUNT_ID,
);
expect(note).not.toHaveBeenCalled();
).toBe(false);
});
});
describe("telegramSetupWizard.dmPolicy", () => {
describe("telegramSetupDmPolicy", () => {
it("reads the named-account DM policy instead of the channel root", () => {
expect(
telegramSetupWizard.dmPolicy?.getCurrent(
telegramSetupDmPolicy.getCurrent?.(
{
channels: {
telegram: {
@@ -185,7 +111,7 @@ describe("telegramSetupWizard.dmPolicy", () => {
});
it("reports account-scoped config keys for named accounts", () => {
expect(telegramSetupWizard.dmPolicy?.resolveConfigKeys?.({}, "alerts")).toEqual({
expect(telegramSetupDmPolicy.resolveConfigKeys?.({}, "alerts")).toEqual({
policyKey: "channels.telegram.accounts.alerts.dmPolicy",
allowFromKey: "channels.telegram.accounts.alerts.allowFrom",
});
@@ -208,13 +134,13 @@ describe("telegramSetupWizard.dmPolicy", () => {
},
};
expect(telegramSetupWizard.dmPolicy?.getCurrent(cfg)).toBe("allowlist");
expect(telegramSetupWizard.dmPolicy?.resolveConfigKeys?.(cfg)).toEqual({
expect(telegramSetupDmPolicy.getCurrent?.(cfg)).toBe("allowlist");
expect(telegramSetupDmPolicy.resolveConfigKeys?.(cfg)).toEqual({
policyKey: "channels.telegram.accounts.alerts.dmPolicy",
allowFromKey: "channels.telegram.accounts.alerts.allowFrom",
});
const next = telegramSetupWizard.dmPolicy?.setPolicy(cfg, "open");
const next = telegramSetupDmPolicy.setPolicy?.(cfg, "open");
expect(next?.channels?.telegram?.dmPolicy).toBe("disabled");
expect(next?.channels?.telegram?.accounts?.alerts?.dmPolicy).toBe("open");
});
@@ -233,7 +159,7 @@ describe("telegramSetupWizard.dmPolicy", () => {
},
};
const next = telegramSetupWizard.dmPolicy?.setPolicy(cfg, "open", "alerts");
const next = telegramSetupDmPolicy.setPolicy?.(cfg, "open", "alerts");
expect(next?.channels?.telegram?.dmPolicy).toBeUndefined();
expect(next?.channels?.telegram?.accounts?.alerts?.dmPolicy).toBe("open");

View File

@@ -1,127 +1,30 @@
import {
addWildcardAllowFrom,
applySetupAccountConfigPatch,
createAllowFromSection,
createStandardChannelSetupStatus,
type ChannelSetupDmPolicy,
DEFAULT_ACCOUNT_ID,
hasConfiguredSecretInput,
type OpenClawConfig,
patchChannelConfigForAccount,
setSetupChannelEnabled,
splitSetupEntries,
} from "openclaw/plugin-sdk/setup";
import type { ChannelSetupWizard } from "openclaw/plugin-sdk/setup";
import { formatCliCommand, formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { inspectTelegramAccount } from "./account-inspect.js";
import {
listTelegramAccountIds,
mergeTelegramAccountConfig,
resolveDefaultTelegramAccountId,
resolveTelegramAccount,
} from "./accounts.js";
import { listTelegramAccountIds, resolveTelegramAccount } from "./accounts.js";
import {
parseTelegramAllowFromId,
promptTelegramAllowFromForAccount,
resolveTelegramAllowFromEntries,
TELEGRAM_TOKEN_HELP_LINES,
TELEGRAM_USER_ID_HELP_LINES,
telegramSetupAdapter,
} from "./setup-core.js";
import {
buildTelegramDmAccessWarningLines,
ensureTelegramDefaultGroupMentionGate,
shouldShowTelegramDmAccessWarning,
telegramSetupDmPolicy,
} from "./setup-surface.helpers.js";
const channel = "telegram" as const;
function ensureTelegramDefaultGroupMentionGate(
cfg: OpenClawConfig,
accountId: string,
): OpenClawConfig {
const resolved = resolveTelegramAccount({ cfg, accountId });
const wildcardGroup = resolved.config.groups?.["*"];
if (wildcardGroup?.requireMention !== undefined) {
return cfg;
}
return patchChannelConfigForAccount({
cfg,
channel,
accountId,
patch: {
groups: {
...resolved.config.groups,
"*": {
...wildcardGroup,
requireMention: true,
},
},
},
});
}
function shouldShowTelegramDmAccessWarning(cfg: OpenClawConfig, accountId: string): boolean {
const merged = mergeTelegramAccountConfig(cfg, accountId);
const policy = merged.dmPolicy ?? "pairing";
const hasAllowFrom =
Array.isArray(merged.allowFrom) && merged.allowFrom.some((e) => String(e).trim());
return policy === "pairing" && !hasAllowFrom;
}
function buildTelegramDmAccessWarningLines(accountId: string): string[] {
const configBase =
accountId === DEFAULT_ACCOUNT_ID
? "channels.telegram"
: `channels.telegram.accounts.${accountId}`;
return [
"Your bot is using DM policy: pairing.",
"Any Telegram user who discovers the bot can send pairing requests.",
"For private use, configure an allowlist with your Telegram user id:",
" " + formatCliCommand(`openclaw config set ${configBase}.dmPolicy "allowlist"`),
" " + formatCliCommand(`openclaw config set ${configBase}.allowFrom '["YOUR_USER_ID"]'`),
`Docs: ${formatDocsLink("/channels/pairing", "channels/pairing")}`,
];
}
const dmPolicy: ChannelSetupDmPolicy = {
label: "Telegram",
channel,
policyKey: "channels.telegram.dmPolicy",
allowFromKey: "channels.telegram.allowFrom",
resolveConfigKeys: (cfg, accountId) =>
(accountId ?? resolveDefaultTelegramAccountId(cfg)) !== DEFAULT_ACCOUNT_ID
? {
policyKey: `channels.telegram.accounts.${accountId ?? resolveDefaultTelegramAccountId(cfg)}.dmPolicy`,
allowFromKey: `channels.telegram.accounts.${accountId ?? resolveDefaultTelegramAccountId(cfg)}.allowFrom`,
}
: {
policyKey: "channels.telegram.dmPolicy",
allowFromKey: "channels.telegram.allowFrom",
},
getCurrent: (cfg, accountId) =>
mergeTelegramAccountConfig(cfg, accountId ?? resolveDefaultTelegramAccountId(cfg)).dmPolicy ??
"pairing",
setPolicy: (cfg, policy, accountId) => {
const resolvedAccountId = accountId ?? resolveDefaultTelegramAccountId(cfg);
const merged = mergeTelegramAccountConfig(cfg, resolvedAccountId);
const patch = {
dmPolicy: policy,
...(policy === "open" ? { allowFrom: addWildcardAllowFrom(merged.allowFrom) } : {}),
};
return accountId == null && resolvedAccountId !== DEFAULT_ACCOUNT_ID
? applySetupAccountConfigPatch({
cfg,
channelKey: channel,
accountId: resolvedAccountId,
patch,
})
: patchChannelConfigForAccount({
cfg,
channel,
accountId: resolvedAccountId,
patch,
});
},
promptAllowFrom: promptTelegramAllowFromForAccount,
};
export const telegramSetupWizard: ChannelSetupWizard = {
channel,
status: createStandardChannelSetupStatus({
@@ -204,7 +107,7 @@ export const telegramSetupWizard: ChannelSetupWizard = {
"Telegram DM access warning",
);
},
dmPolicy,
dmPolicy: telegramSetupDmPolicy,
disable: (cfg) => setSetupChannelEnabled(cfg, channel, false),
};