mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-11 23:53:43 +00:00
test(commands): dedupe command and onboarding test cases
This commit is contained in:
@@ -25,6 +25,10 @@ import {
|
||||
const runtime = createTestRuntime();
|
||||
let clackPrompterModule: typeof import("../wizard/clack-prompter.js");
|
||||
|
||||
function formatChannelStatusJoined(channelAccounts: Record<string, unknown>) {
|
||||
return formatGatewayChannelsStatusLines({ channelAccounts }).join("\n");
|
||||
}
|
||||
|
||||
describe("channels command", () => {
|
||||
beforeAll(async () => {
|
||||
clackPrompterModule = await import("../wizard/clack-prompter.js");
|
||||
@@ -45,23 +49,53 @@ describe("channels command", () => {
|
||||
setDefaultChannelPluginRegistryForTests();
|
||||
});
|
||||
|
||||
it("adds a non-default telegram account", async () => {
|
||||
configMocks.readConfigFileSnapshot.mockResolvedValue({ ...baseConfigSnapshot });
|
||||
await channelsAddCommand(
|
||||
{ channel: "telegram", account: "alerts", token: "123:abc" },
|
||||
runtime,
|
||||
{ hasFlags: true },
|
||||
);
|
||||
|
||||
function getWrittenConfig<T>(): T {
|
||||
expect(configMocks.writeConfigFile).toHaveBeenCalledTimes(1);
|
||||
const next = configMocks.writeConfigFile.mock.calls[0]?.[0] as {
|
||||
return configMocks.writeConfigFile.mock.calls[0]?.[0] as T;
|
||||
}
|
||||
|
||||
async function runRemoveWithConfirm(
|
||||
args: Parameters<typeof channelsRemoveCommand>[0],
|
||||
): Promise<void> {
|
||||
const prompt = { confirm: vi.fn().mockResolvedValue(true) };
|
||||
const promptSpy = vi
|
||||
.spyOn(clackPrompterModule, "createClackPrompter")
|
||||
.mockReturnValue(prompt as never);
|
||||
try {
|
||||
await channelsRemoveCommand(args, runtime, { hasFlags: true });
|
||||
} finally {
|
||||
promptSpy.mockRestore();
|
||||
}
|
||||
}
|
||||
|
||||
async function addTelegramAccount(account: string, token: string): Promise<void> {
|
||||
await channelsAddCommand({ channel: "telegram", account, token }, runtime, {
|
||||
hasFlags: true,
|
||||
});
|
||||
}
|
||||
|
||||
async function addAlertsTelegramAccount(token: string): Promise<{
|
||||
channels?: {
|
||||
telegram?: {
|
||||
enabled?: boolean;
|
||||
accounts?: Record<string, { botToken?: string }>;
|
||||
};
|
||||
};
|
||||
}> {
|
||||
await addTelegramAccount("alerts", token);
|
||||
return getWrittenConfig<{
|
||||
channels?: {
|
||||
telegram?: {
|
||||
enabled?: boolean;
|
||||
accounts?: Record<string, { botToken?: string }>;
|
||||
};
|
||||
};
|
||||
};
|
||||
}>();
|
||||
}
|
||||
|
||||
it("adds a non-default telegram account", async () => {
|
||||
configMocks.readConfigFileSnapshot.mockResolvedValue({ ...baseConfigSnapshot });
|
||||
const next = await addAlertsTelegramAccount("123:abc");
|
||||
expect(next.channels?.telegram?.enabled).toBe(true);
|
||||
expect(next.channels?.telegram?.accounts?.alerts?.botToken).toBe("123:abc");
|
||||
});
|
||||
@@ -83,13 +117,9 @@ describe("channels command", () => {
|
||||
},
|
||||
});
|
||||
|
||||
await channelsAddCommand(
|
||||
{ channel: "telegram", account: "alerts", token: "alerts-token" },
|
||||
runtime,
|
||||
{ hasFlags: true },
|
||||
);
|
||||
await addTelegramAccount("alerts", "alerts-token");
|
||||
|
||||
const next = configMocks.writeConfigFile.mock.calls[0]?.[0] as {
|
||||
const next = getWrittenConfig<{
|
||||
channels?: {
|
||||
telegram?: {
|
||||
botToken?: string;
|
||||
@@ -109,7 +139,7 @@ describe("channels command", () => {
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
}>();
|
||||
expect(next.channels?.telegram?.accounts?.default).toEqual({
|
||||
botToken: "legacy-token",
|
||||
dmPolicy: "allowlist",
|
||||
@@ -137,20 +167,7 @@ describe("channels command", () => {
|
||||
},
|
||||
});
|
||||
|
||||
await channelsAddCommand(
|
||||
{ channel: "telegram", account: "alerts", token: "alerts-token" },
|
||||
runtime,
|
||||
{ hasFlags: true },
|
||||
);
|
||||
|
||||
const next = configMocks.writeConfigFile.mock.calls[0]?.[0] as {
|
||||
channels?: {
|
||||
telegram?: {
|
||||
enabled?: boolean;
|
||||
accounts?: Record<string, { botToken?: string }>;
|
||||
};
|
||||
};
|
||||
};
|
||||
const next = await addAlertsTelegramAccount("alerts-token");
|
||||
expect(next.channels?.telegram?.enabled).toBe(true);
|
||||
expect(next.channels?.telegram?.accounts?.default).toEqual({});
|
||||
expect(next.channels?.telegram?.accounts?.alerts?.botToken).toBe("alerts-token");
|
||||
@@ -169,12 +186,11 @@ describe("channels command", () => {
|
||||
{ hasFlags: true },
|
||||
);
|
||||
|
||||
expect(configMocks.writeConfigFile).toHaveBeenCalledTimes(1);
|
||||
const next = configMocks.writeConfigFile.mock.calls[0]?.[0] as {
|
||||
const next = getWrittenConfig<{
|
||||
channels?: {
|
||||
slack?: { enabled?: boolean; botToken?: string; appToken?: string };
|
||||
};
|
||||
};
|
||||
}>();
|
||||
expect(next.channels?.slack?.enabled).toBe(true);
|
||||
expect(next.channels?.slack?.botToken).toBe("xoxb-1");
|
||||
expect(next.channels?.slack?.appToken).toBe("xapp-1");
|
||||
@@ -199,12 +215,11 @@ describe("channels command", () => {
|
||||
hasFlags: true,
|
||||
});
|
||||
|
||||
expect(configMocks.writeConfigFile).toHaveBeenCalledTimes(1);
|
||||
const next = configMocks.writeConfigFile.mock.calls[0]?.[0] as {
|
||||
const next = getWrittenConfig<{
|
||||
channels?: {
|
||||
discord?: { accounts?: Record<string, { token?: string }> };
|
||||
};
|
||||
};
|
||||
}>();
|
||||
expect(next.channels?.discord?.accounts?.work).toBeUndefined();
|
||||
expect(next.channels?.discord?.accounts?.default?.token).toBe("d0");
|
||||
});
|
||||
@@ -217,11 +232,11 @@ describe("channels command", () => {
|
||||
{ hasFlags: true },
|
||||
);
|
||||
|
||||
const next = configMocks.writeConfigFile.mock.calls[0]?.[0] as {
|
||||
const next = getWrittenConfig<{
|
||||
channels?: {
|
||||
whatsapp?: { accounts?: Record<string, { name?: string }> };
|
||||
};
|
||||
};
|
||||
}>();
|
||||
expect(next.channels?.whatsapp?.accounts?.family?.name).toBe("Family Phone");
|
||||
});
|
||||
|
||||
@@ -250,13 +265,13 @@ describe("channels command", () => {
|
||||
{ hasFlags: true },
|
||||
);
|
||||
|
||||
const next = configMocks.writeConfigFile.mock.calls[0]?.[0] as {
|
||||
const next = getWrittenConfig<{
|
||||
channels?: {
|
||||
signal?: {
|
||||
accounts?: Record<string, { account?: string; name?: string }>;
|
||||
};
|
||||
};
|
||||
};
|
||||
}>();
|
||||
expect(next.channels?.signal?.accounts?.lab?.account).toBe("+15555550123");
|
||||
expect(next.channels?.signal?.accounts?.lab?.name).toBe("Lab");
|
||||
expect(next.channels?.signal?.accounts?.default?.name).toBe("Primary");
|
||||
@@ -270,20 +285,12 @@ describe("channels command", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const prompt = { confirm: vi.fn().mockResolvedValue(true) };
|
||||
const promptSpy = vi
|
||||
.spyOn(clackPrompterModule, "createClackPrompter")
|
||||
.mockReturnValue(prompt as never);
|
||||
await runRemoveWithConfirm({ channel: "discord", account: "default" });
|
||||
|
||||
await channelsRemoveCommand({ channel: "discord", account: "default" }, runtime, {
|
||||
hasFlags: true,
|
||||
});
|
||||
|
||||
const next = configMocks.writeConfigFile.mock.calls[0]?.[0] as {
|
||||
const next = getWrittenConfig<{
|
||||
channels?: { discord?: { enabled?: boolean } };
|
||||
};
|
||||
}>();
|
||||
expect(next.channels?.discord?.enabled).toBe(false);
|
||||
promptSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("includes external auth profiles in JSON output", async () => {
|
||||
@@ -348,14 +355,14 @@ describe("channels command", () => {
|
||||
{ hasFlags: true },
|
||||
);
|
||||
|
||||
const next = configMocks.writeConfigFile.mock.calls[0]?.[0] as {
|
||||
const next = getWrittenConfig<{
|
||||
channels?: {
|
||||
telegram?: {
|
||||
name?: string;
|
||||
accounts?: Record<string, { botToken?: string; name?: string }>;
|
||||
};
|
||||
};
|
||||
};
|
||||
}>();
|
||||
expect(next.channels?.telegram?.name).toBeUndefined();
|
||||
expect(next.channels?.telegram?.accounts?.default?.name).toBe("Primary Bot");
|
||||
});
|
||||
@@ -377,14 +384,14 @@ describe("channels command", () => {
|
||||
hasFlags: true,
|
||||
});
|
||||
|
||||
const next = configMocks.writeConfigFile.mock.calls[0]?.[0] as {
|
||||
const next = getWrittenConfig<{
|
||||
channels?: {
|
||||
discord?: {
|
||||
name?: string;
|
||||
accounts?: Record<string, { name?: string; token?: string }>;
|
||||
};
|
||||
};
|
||||
};
|
||||
}>();
|
||||
expect(next.channels?.discord?.name).toBeUndefined();
|
||||
expect(next.channels?.discord?.accounts?.default?.name).toBe("Primary Bot");
|
||||
expect(next.channels?.discord?.accounts?.work?.token).toBe("d1");
|
||||
@@ -405,8 +412,9 @@ describe("channels command", () => {
|
||||
expect(telegramIndex).toBeLessThan(whatsappIndex);
|
||||
});
|
||||
|
||||
it("surfaces Discord privileged intent issues in channels status output", () => {
|
||||
const lines = formatGatewayChannelsStatusLines({
|
||||
it.each([
|
||||
{
|
||||
name: "surfaces Discord privileged intent issues in channels status output",
|
||||
channelAccounts: {
|
||||
discord: [
|
||||
{
|
||||
@@ -417,14 +425,14 @@ describe("channels command", () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(lines.join("\n")).toMatch(/Warnings:/);
|
||||
expect(lines.join("\n")).toMatch(/Message Content Intent is disabled/i);
|
||||
expect(lines.join("\n")).toMatch(/Run: (?:openclaw|openclaw)( --profile isolated)? doctor/);
|
||||
});
|
||||
|
||||
it("surfaces Discord permission audit issues in channels status output", () => {
|
||||
const lines = formatGatewayChannelsStatusLines({
|
||||
patterns: [
|
||||
/Warnings:/,
|
||||
/Message Content Intent is disabled/i,
|
||||
/Run: (?:openclaw|openclaw)( --profile isolated)? doctor/,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "surfaces Discord permission audit issues in channels status output",
|
||||
channelAccounts: {
|
||||
discord: [
|
||||
{
|
||||
@@ -444,14 +452,10 @@ describe("channels command", () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(lines.join("\n")).toMatch(/Warnings:/);
|
||||
expect(lines.join("\n")).toMatch(/permission audit/i);
|
||||
expect(lines.join("\n")).toMatch(/Channel 111/i);
|
||||
});
|
||||
|
||||
it("surfaces Telegram privacy-mode hints when allowUnmentionedGroups is enabled", () => {
|
||||
const lines = formatGatewayChannelsStatusLines({
|
||||
patterns: [/Warnings:/, /permission audit/i, /Channel 111/i],
|
||||
},
|
||||
{
|
||||
name: "surfaces Telegram privacy-mode hints when allowUnmentionedGroups is enabled",
|
||||
channelAccounts: {
|
||||
telegram: [
|
||||
{
|
||||
@@ -462,54 +466,54 @@ describe("channels command", () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(lines.join("\n")).toMatch(/Warnings:/);
|
||||
expect(lines.join("\n")).toMatch(/Telegram Bot API privacy mode/i);
|
||||
patterns: [/Warnings:/, /Telegram Bot API privacy mode/i],
|
||||
},
|
||||
])("$name", ({ channelAccounts, patterns }) => {
|
||||
const joined = formatChannelStatusJoined(channelAccounts);
|
||||
for (const pattern of patterns) {
|
||||
expect(joined).toMatch(pattern);
|
||||
}
|
||||
});
|
||||
|
||||
it("includes Telegram bot username from probe data", () => {
|
||||
const lines = formatGatewayChannelsStatusLines({
|
||||
channelAccounts: {
|
||||
telegram: [
|
||||
{
|
||||
accountId: "default",
|
||||
enabled: true,
|
||||
configured: true,
|
||||
probe: { ok: true, bot: { username: "openclaw_bot" } },
|
||||
},
|
||||
],
|
||||
},
|
||||
const joined = formatChannelStatusJoined({
|
||||
telegram: [
|
||||
{
|
||||
accountId: "default",
|
||||
enabled: true,
|
||||
configured: true,
|
||||
probe: { ok: true, bot: { username: "openclaw_bot" } },
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(lines.join("\n")).toMatch(/bot:@openclaw_bot/);
|
||||
expect(joined).toMatch(/bot:@openclaw_bot/);
|
||||
});
|
||||
|
||||
it("surfaces Telegram group membership audit issues in channels status output", () => {
|
||||
const lines = formatGatewayChannelsStatusLines({
|
||||
channelAccounts: {
|
||||
telegram: [
|
||||
{
|
||||
accountId: "default",
|
||||
enabled: true,
|
||||
configured: true,
|
||||
audit: {
|
||||
hasWildcardUnmentionedGroups: true,
|
||||
unresolvedGroups: 1,
|
||||
groups: [
|
||||
{
|
||||
chatId: "-1001",
|
||||
ok: false,
|
||||
status: "left",
|
||||
error: "not in group",
|
||||
},
|
||||
],
|
||||
},
|
||||
const joined = formatChannelStatusJoined({
|
||||
telegram: [
|
||||
{
|
||||
accountId: "default",
|
||||
enabled: true,
|
||||
configured: true,
|
||||
audit: {
|
||||
hasWildcardUnmentionedGroups: true,
|
||||
unresolvedGroups: 1,
|
||||
groups: [
|
||||
{
|
||||
chatId: "-1001",
|
||||
ok: false,
|
||||
status: "left",
|
||||
error: "not in group",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(lines.join("\n")).toMatch(/Warnings:/);
|
||||
expect(lines.join("\n")).toMatch(/membership probing is not possible/i);
|
||||
expect(lines.join("\n")).toMatch(/Group -1001/i);
|
||||
expect(joined).toMatch(/Warnings:/);
|
||||
expect(joined).toMatch(/membership probing is not possible/i);
|
||||
expect(joined).toMatch(/Group -1001/i);
|
||||
});
|
||||
|
||||
it("surfaces WhatsApp auth/runtime hints when unlinked or disconnected", () => {
|
||||
@@ -591,16 +595,8 @@ describe("channels command", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const prompt = { confirm: vi.fn().mockResolvedValue(true) };
|
||||
const promptSpy = vi
|
||||
.spyOn(clackPrompterModule, "createClackPrompter")
|
||||
.mockReturnValue(prompt as never);
|
||||
|
||||
await channelsRemoveCommand({ channel: "telegram", account: "default" }, runtime, {
|
||||
hasFlags: true,
|
||||
});
|
||||
await runRemoveWithConfirm({ channel: "telegram", account: "default" });
|
||||
|
||||
expect(offsetMocks.deleteTelegramUpdateOffset).not.toHaveBeenCalled();
|
||||
promptSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user