fix: add dmScope guidance regression coverage (#13129) (thanks @VintLin)

This commit is contained in:
Peter Steinberger
2026-02-13 16:42:49 +01:00
parent ca3c83acdf
commit f612e35907
4 changed files with 74 additions and 1 deletions

View File

@@ -2,13 +2,14 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
const note = vi.hoisted(() => vi.fn());
const pluginRegistry = vi.hoisted(() => ({ list: [] as any[] }));
vi.mock("../terminal/note.js", () => ({
note,
}));
vi.mock("../channels/plugins/index.js", () => ({
listChannelPlugins: () => [],
listChannelPlugins: () => pluginRegistry.list,
}));
import { noteSecurityWarnings } from "./doctor-security.js";
@@ -19,6 +20,7 @@ describe("noteSecurityWarnings gateway exposure", () => {
beforeEach(() => {
note.mockClear();
pluginRegistry.list = [];
prevToken = process.env.OPENCLAW_GATEWAY_TOKEN;
prevPassword = process.env.OPENCLAW_GATEWAY_PASSWORD;
delete process.env.OPENCLAW_GATEWAY_TOKEN;
@@ -73,4 +75,31 @@ describe("noteSecurityWarnings gateway exposure", () => {
expect(message).toContain("No channel security warnings detected");
expect(message).not.toContain("Gateway bound");
});
it("shows explicit dmScope config command for multi-user DMs", async () => {
pluginRegistry.list = [
{
id: "whatsapp",
meta: { label: "WhatsApp" },
config: {
listAccountIds: () => ["default"],
resolveAccount: () => ({}),
isEnabled: () => true,
isConfigured: () => true,
},
security: {
resolveDmPolicy: () => ({
policy: "allowlist",
allowFrom: ["alice", "bob"],
allowFromPath: "channels.whatsapp.",
approveHint: "approve",
}),
},
},
];
const cfg = { session: { dmScope: "main" } } as OpenClawConfig;
await noteSecurityWarnings(cfg);
const message = lastMessage();
expect(message).toContain('config set session.dmScope "per-channel-peer"');
});
});

View File

@@ -87,6 +87,48 @@ describe("setupChannels", () => {
expect(multiselect).not.toHaveBeenCalled();
});
it("shows explicit dmScope config command in channel primer", async () => {
const note = vi.fn(async () => {});
const select = vi.fn(async () => "__done__");
const multiselect = vi.fn(async () => {
throw new Error("unexpected multiselect");
});
const text = vi.fn(async ({ message }: { message: string }) => {
throw new Error(`unexpected text prompt: ${message}`);
});
const prompter: WizardPrompter = {
intro: vi.fn(async () => {}),
outro: vi.fn(async () => {}),
note,
select,
multiselect,
text: text as unknown as WizardPrompter["text"],
confirm: vi.fn(async () => false),
progress: vi.fn(() => ({ update: vi.fn(), stop: vi.fn() })),
};
const runtime: RuntimeEnv = {
log: vi.fn(),
error: vi.fn(),
exit: vi.fn((code: number) => {
throw new Error(`exit:${code}`);
}),
};
await setupChannels({} as OpenClawConfig, runtime, prompter, {
skipConfirm: true,
});
const sawPrimer = note.mock.calls.some(
([message, title]) =>
title === "How channels work" &&
String(message).includes('config set session.dmScope "per-channel-peer"'),
);
expect(sawPrimer).toBe(true);
expect(multiselect).not.toHaveBeenCalled();
});
it("prompts for configured channel action and skips configuration when told to skip", async () => {
const select = vi.fn(async ({ message }: { message: string }) => {
if (message === "Select channel (QuickStart)") {

View File

@@ -589,6 +589,7 @@ describe("security audit", () => {
expect.objectContaining({
checkId: "channels.whatsapp.dm.scope_main_multiuser",
severity: "warn",
remediation: expect.stringContaining('config set session.dmScope "per-channel-peer"'),
}),
]),
);