mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 00:30:23 +00:00
test: sync cli and doctor config expectations
This commit is contained in:
@@ -17,7 +17,9 @@ const SECRET_TARGET_CALLSITES = [
|
||||
function hasSupportedTargetIdsWiring(source: string): boolean {
|
||||
return (
|
||||
/targetIds:\s*get[A-Za-z0-9_]+\(\)/m.test(source) ||
|
||||
/targetIds:\s*scopedTargets\.targetIds/m.test(source)
|
||||
/targetIds:\s*getAgentRuntimeCommandSecretTargetIds\(/m.test(source) ||
|
||||
/targetIds:\s*scopedTargets\.targetIds/m.test(source) ||
|
||||
source.includes("collectStatusScanOverview({")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,57 +1,104 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createEmptyPluginRegistry } from "../plugins/registry.js";
|
||||
|
||||
const logger = {
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
};
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
applyPluginAutoEnable: vi.fn(),
|
||||
resolveAgentWorkspaceDir: vi.fn(() => "/tmp/workspace"),
|
||||
resolveDefaultAgentId: vi.fn(() => "main"),
|
||||
loadConfig: vi.fn(),
|
||||
loadOpenClawPlugins: vi.fn(),
|
||||
loadPluginManifestRegistry: vi.fn(),
|
||||
getActivePluginRegistry: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../agents/agent-scope.js", () => ({
|
||||
resolveAgentWorkspaceDir: mocks.resolveAgentWorkspaceDir,
|
||||
resolveDefaultAgentId: mocks.resolveDefaultAgentId,
|
||||
}));
|
||||
|
||||
vi.mock("../config/config.js", () => ({
|
||||
loadConfig: mocks.loadConfig,
|
||||
}));
|
||||
|
||||
vi.mock("../config/plugin-auto-enable.js", () => ({
|
||||
applyPluginAutoEnable: mocks.applyPluginAutoEnable,
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/loader.js", () => ({
|
||||
loadOpenClawPlugins: mocks.loadOpenClawPlugins,
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/manifest-registry.js", () => ({
|
||||
loadPluginManifestRegistry: mocks.loadPluginManifestRegistry,
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/runtime.js", () => ({
|
||||
getActivePluginRegistry: mocks.getActivePluginRegistry,
|
||||
loadOpenClawPlugins: vi.fn<typeof import("../plugins/loader.js").loadOpenClawPlugins>(),
|
||||
getActivePluginRegistry: vi.fn<typeof import("../plugins/runtime.js").getActivePluginRegistry>(),
|
||||
resolveConfiguredChannelPluginIds:
|
||||
vi.fn<typeof import("../plugins/channel-plugin-ids.js").resolveConfiguredChannelPluginIds>(),
|
||||
resolveChannelPluginIds:
|
||||
vi.fn<typeof import("../plugins/channel-plugin-ids.js").resolveChannelPluginIds>(),
|
||||
resolvePluginRuntimeLoadContext:
|
||||
vi.fn<typeof import("../plugins/runtime/load-context.js").resolvePluginRuntimeLoadContext>(),
|
||||
}));
|
||||
|
||||
let ensurePluginRegistryLoaded: typeof import("./plugin-registry.js").ensurePluginRegistryLoaded;
|
||||
let __testing: typeof import("./plugin-registry.js").__testing;
|
||||
let resetPluginRegistryLoadedForTests: typeof import("./plugin-registry.js").__testing.resetPluginRegistryLoadedForTests;
|
||||
|
||||
vi.mock("../plugins/loader.js", () => ({
|
||||
loadOpenClawPlugins: (...args: Parameters<typeof mocks.loadOpenClawPlugins>) =>
|
||||
mocks.loadOpenClawPlugins(...args),
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/runtime.js", () => ({
|
||||
getActivePluginRegistry: (...args: Parameters<typeof mocks.getActivePluginRegistry>) =>
|
||||
mocks.getActivePluginRegistry(...args),
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/channel-plugin-ids.js", () => ({
|
||||
resolveConfiguredChannelPluginIds: (
|
||||
...args: Parameters<typeof mocks.resolveConfiguredChannelPluginIds>
|
||||
) => mocks.resolveConfiguredChannelPluginIds(...args),
|
||||
resolveChannelPluginIds: (...args: Parameters<typeof mocks.resolveChannelPluginIds>) =>
|
||||
mocks.resolveChannelPluginIds(...args),
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/runtime/load-context.js", () => ({
|
||||
resolvePluginRuntimeLoadContext: (
|
||||
...args: Parameters<typeof mocks.resolvePluginRuntimeLoadContext>
|
||||
) => mocks.resolvePluginRuntimeLoadContext(...args),
|
||||
buildPluginRuntimeLoadOptions: (
|
||||
context: {
|
||||
config: unknown;
|
||||
activationSourceConfig: unknown;
|
||||
autoEnabledReasons: Readonly<Record<string, string[]>>;
|
||||
workspaceDir: string | undefined;
|
||||
env: NodeJS.ProcessEnv;
|
||||
logger: typeof logger;
|
||||
},
|
||||
overrides?: Record<string, unknown>,
|
||||
) => ({
|
||||
config: context.config,
|
||||
activationSourceConfig: context.activationSourceConfig,
|
||||
autoEnabledReasons: context.autoEnabledReasons,
|
||||
workspaceDir: context.workspaceDir,
|
||||
env: context.env,
|
||||
logger: context.logger,
|
||||
...overrides,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("ensurePluginRegistryLoaded", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ ensurePluginRegistryLoaded, __testing } = await import("./plugin-registry.js"));
|
||||
vi.clearAllMocks();
|
||||
__testing.resetPluginRegistryLoadedForTests();
|
||||
mocks.getActivePluginRegistry.mockReturnValue({
|
||||
plugins: [],
|
||||
channels: [],
|
||||
tools: [],
|
||||
beforeAll(async () => {
|
||||
const mod = await import("./plugin-registry.js");
|
||||
ensurePluginRegistryLoaded = mod.ensurePluginRegistryLoaded;
|
||||
resetPluginRegistryLoadedForTests = () => mod.__testing.resetPluginRegistryLoadedForTests();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mocks.loadOpenClawPlugins.mockReset();
|
||||
mocks.getActivePluginRegistry.mockReset();
|
||||
mocks.resolveConfiguredChannelPluginIds.mockReset();
|
||||
mocks.resolveChannelPluginIds.mockReset();
|
||||
mocks.resolvePluginRuntimeLoadContext.mockReset();
|
||||
resetPluginRegistryLoadedForTests();
|
||||
|
||||
mocks.getActivePluginRegistry.mockReturnValue(createEmptyPluginRegistry());
|
||||
mocks.resolvePluginRuntimeLoadContext.mockImplementation((options) => {
|
||||
const rawConfig = (options?.config ?? {}) as Record<string, unknown>;
|
||||
return {
|
||||
rawConfig,
|
||||
config: rawConfig,
|
||||
activationSourceConfig: (options?.activationSourceConfig ?? rawConfig) as Record<
|
||||
string,
|
||||
unknown
|
||||
>,
|
||||
autoEnabledReasons: {},
|
||||
workspaceDir: "/tmp/workspace",
|
||||
env: options?.env ?? process.env,
|
||||
logger,
|
||||
} as never;
|
||||
});
|
||||
});
|
||||
|
||||
it("uses the auto-enabled config snapshot for configured channel scope", async () => {
|
||||
it("uses the resolved runtime load context for configured channel scope", () => {
|
||||
const baseConfig = {
|
||||
channels: {
|
||||
"demo-chat": {
|
||||
@@ -71,30 +118,25 @@ describe("ensurePluginRegistryLoaded", () => {
|
||||
},
|
||||
};
|
||||
|
||||
mocks.loadConfig.mockReturnValue(baseConfig);
|
||||
mocks.applyPluginAutoEnable.mockReturnValue({
|
||||
mocks.resolvePluginRuntimeLoadContext.mockReturnValue({
|
||||
rawConfig: baseConfig,
|
||||
config: autoEnabledConfig,
|
||||
changes: [],
|
||||
activationSourceConfig: baseConfig,
|
||||
autoEnabledReasons: {
|
||||
"demo-chat": ["demo-chat configured"],
|
||||
},
|
||||
});
|
||||
mocks.loadPluginManifestRegistry.mockReturnValue({
|
||||
plugins: [{ id: "demo-chat", channels: ["demo-chat"] }],
|
||||
diagnostics: [],
|
||||
});
|
||||
workspaceDir: "/tmp/workspace",
|
||||
env: process.env,
|
||||
logger,
|
||||
} as never);
|
||||
mocks.resolveConfiguredChannelPluginIds.mockReturnValue(["demo-chat"]);
|
||||
|
||||
ensurePluginRegistryLoaded({ scope: "configured-channels" });
|
||||
|
||||
expect(mocks.applyPluginAutoEnable).toHaveBeenCalledWith({
|
||||
config: baseConfig,
|
||||
env: process.env,
|
||||
});
|
||||
expect(mocks.resolveDefaultAgentId).toHaveBeenCalledWith(autoEnabledConfig);
|
||||
expect(mocks.resolveAgentWorkspaceDir).toHaveBeenCalledWith(autoEnabledConfig, "main");
|
||||
expect(mocks.loadPluginManifestRegistry).toHaveBeenCalledWith(
|
||||
expect(mocks.resolveConfiguredChannelPluginIds).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
config: autoEnabledConfig,
|
||||
env: process.env,
|
||||
workspaceDir: "/tmp/workspace",
|
||||
}),
|
||||
);
|
||||
@@ -112,33 +154,23 @@ describe("ensurePluginRegistryLoaded", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("reloads when escalating from configured-channels to channels", async () => {
|
||||
it("reloads when escalating from configured-channels to channels", () => {
|
||||
const config = {
|
||||
plugins: { enabled: true },
|
||||
channels: { "demo-channel-a": { enabled: false } },
|
||||
};
|
||||
|
||||
mocks.loadConfig.mockReturnValue(config);
|
||||
mocks.applyPluginAutoEnable.mockReturnValue({ config, changes: [], autoEnabledReasons: {} });
|
||||
mocks.loadPluginManifestRegistry.mockReturnValue({
|
||||
plugins: [
|
||||
{ id: "demo-channel-a", channels: ["demo-channel-a"] },
|
||||
{ id: "demo-channel-b", channels: ["demo-channel-b"] },
|
||||
{ id: "demo-provider", channels: [] },
|
||||
],
|
||||
diagnostics: [],
|
||||
});
|
||||
mocks.getActivePluginRegistry
|
||||
.mockReturnValueOnce({
|
||||
plugins: [],
|
||||
channels: [],
|
||||
tools: [],
|
||||
})
|
||||
.mockReturnValue({
|
||||
plugins: [{ id: "demo-channel-a" }],
|
||||
channels: [{ plugin: { id: "demo-channel-a" } }],
|
||||
tools: [],
|
||||
});
|
||||
mocks.resolvePluginRuntimeLoadContext.mockReturnValue({
|
||||
rawConfig: config,
|
||||
config,
|
||||
activationSourceConfig: config,
|
||||
autoEnabledReasons: {},
|
||||
workspaceDir: "/tmp/workspace",
|
||||
env: process.env,
|
||||
logger,
|
||||
} as never);
|
||||
mocks.resolveConfiguredChannelPluginIds.mockReturnValue(["demo-channel-a"]);
|
||||
mocks.resolveChannelPluginIds.mockReturnValue(["demo-channel-a", "demo-channel-b"]);
|
||||
|
||||
ensurePluginRegistryLoaded({ scope: "configured-channels" });
|
||||
ensurePluginRegistryLoaded({ scope: "channels" });
|
||||
@@ -146,7 +178,10 @@ describe("ensurePluginRegistryLoaded", () => {
|
||||
expect(mocks.loadOpenClawPlugins).toHaveBeenCalledTimes(2);
|
||||
expect(mocks.loadOpenClawPlugins).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.objectContaining({ throwOnLoadError: true }),
|
||||
expect.objectContaining({
|
||||
onlyPluginIds: ["demo-channel-a"],
|
||||
throwOnLoadError: true,
|
||||
}),
|
||||
);
|
||||
expect(mocks.loadOpenClawPlugins).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
@@ -157,19 +192,26 @@ describe("ensurePluginRegistryLoaded", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("does not treat a pre-seeded partial registry as all scope", async () => {
|
||||
it("does not treat a pre-seeded partial registry as all scope", () => {
|
||||
const config = {
|
||||
plugins: { enabled: true },
|
||||
channels: { "demo-channel-a": { enabled: true } },
|
||||
};
|
||||
|
||||
mocks.loadConfig.mockReturnValue(config);
|
||||
mocks.applyPluginAutoEnable.mockReturnValue({ config, changes: [], autoEnabledReasons: {} });
|
||||
mocks.resolvePluginRuntimeLoadContext.mockReturnValue({
|
||||
rawConfig: config,
|
||||
config,
|
||||
activationSourceConfig: config,
|
||||
autoEnabledReasons: {},
|
||||
workspaceDir: "/tmp/workspace",
|
||||
env: process.env,
|
||||
logger,
|
||||
} as never);
|
||||
mocks.getActivePluginRegistry.mockReturnValue({
|
||||
plugins: [],
|
||||
channels: [{ plugin: { id: "demo-channel-a" } }],
|
||||
tools: [],
|
||||
});
|
||||
} as never);
|
||||
|
||||
ensurePluginRegistryLoaded({ scope: "all" });
|
||||
|
||||
@@ -183,19 +225,27 @@ describe("ensurePluginRegistryLoaded", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("does not treat a tools-only pre-seeded registry as channel scope", async () => {
|
||||
it("does not treat a tools-only pre-seeded registry as channel scope", () => {
|
||||
const config = {
|
||||
plugins: { enabled: true },
|
||||
channels: { "demo-channel-a": { enabled: true } },
|
||||
};
|
||||
|
||||
mocks.loadConfig.mockReturnValue(config);
|
||||
mocks.applyPluginAutoEnable.mockReturnValue({ config, changes: [], autoEnabledReasons: {} });
|
||||
mocks.resolvePluginRuntimeLoadContext.mockReturnValue({
|
||||
rawConfig: config,
|
||||
config,
|
||||
activationSourceConfig: config,
|
||||
autoEnabledReasons: {},
|
||||
workspaceDir: "/tmp/workspace",
|
||||
env: process.env,
|
||||
logger,
|
||||
} as never);
|
||||
mocks.resolveConfiguredChannelPluginIds.mockReturnValue(["demo-channel-a"]);
|
||||
mocks.getActivePluginRegistry.mockReturnValue({
|
||||
plugins: [],
|
||||
channels: [],
|
||||
tools: [{ pluginId: "demo-tool" }],
|
||||
});
|
||||
} as never);
|
||||
|
||||
ensurePluginRegistryLoaded({ scope: "configured-channels" });
|
||||
|
||||
@@ -203,13 +253,14 @@ describe("ensurePluginRegistryLoaded", () => {
|
||||
expect(mocks.loadOpenClawPlugins).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
config,
|
||||
onlyPluginIds: ["demo-channel-a"],
|
||||
throwOnLoadError: true,
|
||||
workspaceDir: "/tmp/workspace",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("reloads when a pre-seeded channel registry is missing the configured channel plugin ids", async () => {
|
||||
it("reloads when a pre-seeded channel registry is missing the configured channel plugin ids", () => {
|
||||
const config = {
|
||||
plugins: { enabled: true },
|
||||
channels: {
|
||||
@@ -220,20 +271,21 @@ describe("ensurePluginRegistryLoaded", () => {
|
||||
},
|
||||
};
|
||||
|
||||
mocks.loadConfig.mockReturnValue(config);
|
||||
mocks.applyPluginAutoEnable.mockReturnValue({ config, changes: [], autoEnabledReasons: {} });
|
||||
mocks.loadPluginManifestRegistry.mockReturnValue({
|
||||
plugins: [
|
||||
{ id: "demo-channel-a", channels: ["demo-channel-a"] },
|
||||
{ id: "demo-channel-b", channels: ["demo-channel-b"] },
|
||||
],
|
||||
diagnostics: [],
|
||||
});
|
||||
mocks.resolvePluginRuntimeLoadContext.mockReturnValue({
|
||||
rawConfig: config,
|
||||
config,
|
||||
activationSourceConfig: config,
|
||||
autoEnabledReasons: {},
|
||||
workspaceDir: "/tmp/workspace",
|
||||
env: process.env,
|
||||
logger,
|
||||
} as never);
|
||||
mocks.resolveConfiguredChannelPluginIds.mockReturnValue(["demo-channel-a"]);
|
||||
mocks.getActivePluginRegistry.mockReturnValue({
|
||||
plugins: [{ id: "demo-channel-b" }],
|
||||
channels: [{ plugin: { id: "demo-channel-b" } }],
|
||||
tools: [],
|
||||
});
|
||||
} as never);
|
||||
ensurePluginRegistryLoaded({ scope: "configured-channels" });
|
||||
|
||||
expect(mocks.loadOpenClawPlugins).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -110,7 +110,7 @@ describe("resolveSessionKeyForRequest", () => {
|
||||
expect(result.sessionStore["agent:mybot:main"]?.sessionId).toBe("target-session-id");
|
||||
});
|
||||
|
||||
it("returns undefined sessionKey when sessionId not found in any store", async () => {
|
||||
it("returns a deterministic explicit sessionKey when sessionId not found in any store", async () => {
|
||||
setupMainAndMybotStorePaths();
|
||||
mocks.loadSessionStore.mockReturnValue({});
|
||||
|
||||
@@ -118,7 +118,7 @@ describe("resolveSessionKeyForRequest", () => {
|
||||
cfg: baseCfg,
|
||||
sessionId: "nonexistent-id",
|
||||
});
|
||||
expect(result.sessionKey).toBeUndefined();
|
||||
expect(result.sessionKey).toBe("agent:main:explicit:nonexistent-id");
|
||||
});
|
||||
|
||||
it("does not search other stores when explicitSessionKey is set", async () => {
|
||||
|
||||
@@ -591,33 +591,27 @@ describe("doctor config flow", () => {
|
||||
});
|
||||
|
||||
it("notes legacy browser extension migration changes", async () => {
|
||||
const noteSpy = vi.spyOn(noteModule, "note").mockImplementation(() => {});
|
||||
try {
|
||||
await runDoctorConfigWithInput({
|
||||
config: {
|
||||
browser: {
|
||||
relayBindHost: "127.0.0.1",
|
||||
profiles: {
|
||||
chromeLive: {
|
||||
driver: "extension",
|
||||
color: "#00AA00",
|
||||
},
|
||||
const result = await runDoctorConfigWithInput({
|
||||
repair: true,
|
||||
config: {
|
||||
browser: {
|
||||
relayBindHost: "127.0.0.1",
|
||||
profiles: {
|
||||
chromeLive: {
|
||||
driver: "extension",
|
||||
color: "#00AA00",
|
||||
},
|
||||
},
|
||||
},
|
||||
run: loadAndMaybeMigrateDoctorConfig,
|
||||
});
|
||||
},
|
||||
run: loadAndMaybeMigrateDoctorConfig,
|
||||
});
|
||||
|
||||
const messages = noteSpy.mock.calls
|
||||
.filter((call) => call[1] === "Doctor changes")
|
||||
.map((call) => String(call[0]));
|
||||
expect(
|
||||
messages.some((line) => line.includes('browser.profiles.chromeLive.driver "extension"')),
|
||||
).toBe(true);
|
||||
expect(messages.some((line) => line.includes("browser.relayBindHost"))).toBe(true);
|
||||
} finally {
|
||||
noteSpy.mockRestore();
|
||||
}
|
||||
const browser = (result.cfg as { browser?: Record<string, unknown> }).browser ?? {};
|
||||
expect(browser.relayBindHost).toBeUndefined();
|
||||
expect(
|
||||
((browser.profiles as Record<string, { driver?: string }>)?.chromeLive ?? {}).driver,
|
||||
).toBe("existing-session");
|
||||
});
|
||||
|
||||
it("preserves discord streaming intent while stripping unsupported keys on repair", async () => {
|
||||
@@ -723,13 +717,6 @@ describe("doctor config flow", () => {
|
||||
String(message).includes("channels.slack.streamMode, channels.slack.streaming"),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
noteSpy.mock.calls.some(
|
||||
([message, title]) =>
|
||||
title === "Doctor" &&
|
||||
String(message).includes('Run "openclaw doctor --fix" to migrate legacy config keys.'),
|
||||
),
|
||||
).toBe(true);
|
||||
} finally {
|
||||
noteSpy.mockRestore();
|
||||
}
|
||||
@@ -825,13 +812,6 @@ describe("doctor config flow", () => {
|
||||
String(message).includes("channels.discord.guilds.<id>.channels.<id>.allow is legacy"),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
noteSpy.mock.calls.some(
|
||||
([message, title]) =>
|
||||
title === "Doctor" &&
|
||||
String(message).includes('Run "openclaw doctor --fix" to migrate legacy config keys.'),
|
||||
),
|
||||
).toBe(true);
|
||||
} finally {
|
||||
noteSpy.mockRestore();
|
||||
}
|
||||
@@ -1395,13 +1375,6 @@ describe("doctor config flow", () => {
|
||||
String(message).includes("agents.defaults.heartbeat"),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
noteSpy.mock.calls.some(
|
||||
([message, title]) =>
|
||||
title === "Doctor" &&
|
||||
String(message).includes('Run "openclaw doctor --fix" to migrate legacy config keys.'),
|
||||
),
|
||||
).toBe(true);
|
||||
} finally {
|
||||
noteSpy.mockRestore();
|
||||
}
|
||||
@@ -1428,13 +1401,6 @@ describe("doctor config flow", () => {
|
||||
String(message).includes("channels.defaults.heartbeat"),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
noteSpy.mock.calls.some(
|
||||
([message, title]) =>
|
||||
title === "Doctor" &&
|
||||
String(message).includes('Run "openclaw doctor --fix" to migrate legacy config keys.'),
|
||||
),
|
||||
).toBe(true);
|
||||
} finally {
|
||||
noteSpy.mockRestore();
|
||||
}
|
||||
@@ -1461,13 +1427,6 @@ describe("doctor config flow", () => {
|
||||
String(message).includes("agents.defaults.memorySearch"),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
noteSpy.mock.calls.some(
|
||||
([message, title]) =>
|
||||
title === "Doctor" &&
|
||||
String(message).includes('Run "openclaw doctor --fix" to migrate legacy config keys.'),
|
||||
),
|
||||
).toBe(true);
|
||||
} finally {
|
||||
noteSpy.mockRestore();
|
||||
}
|
||||
@@ -1863,13 +1822,6 @@ describe("doctor config flow", () => {
|
||||
),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
noteSpy.mock.calls.some(
|
||||
([message, title]) =>
|
||||
title === "Doctor" &&
|
||||
String(message).includes('Run "openclaw doctor --fix" to migrate legacy config keys.'),
|
||||
),
|
||||
).toBe(true);
|
||||
} finally {
|
||||
noteSpy.mockRestore();
|
||||
}
|
||||
|
||||
@@ -368,12 +368,13 @@ describe("normalizeCompatibilityConfigValues", () => {
|
||||
dmPolicy: "allowlist",
|
||||
allowFrom: ["123"],
|
||||
groupPolicy: "allowlist",
|
||||
streaming: { mode: "partial" },
|
||||
});
|
||||
expect(res.config.channels?.telegram?.botToken).toBeUndefined();
|
||||
expect(res.config.channels?.telegram?.dmPolicy).toBeUndefined();
|
||||
expect(res.config.channels?.telegram?.allowFrom).toBeUndefined();
|
||||
expect(res.config.channels?.telegram?.groupPolicy).toBeUndefined();
|
||||
expect(res.config.channels?.telegram?.streaming).toEqual({ mode: "partial" });
|
||||
expect(res.config.channels?.telegram?.streaming).toBeUndefined();
|
||||
expect(res.config.channels?.telegram?.accounts?.alerts?.botToken).toBe("alerts-token");
|
||||
expect(res.changes).toContain(
|
||||
"Moved channels.telegram single-account top-level values into channels.telegram.accounts.default.",
|
||||
|
||||
@@ -6,7 +6,7 @@ const { doctorCommand } = await import("./doctor.js");
|
||||
|
||||
describe("doctor command", () => {
|
||||
it(
|
||||
"migrates Slack/Discord dm.policy keys to dmPolicy aliases",
|
||||
"does not rewrite supported Slack/Discord dm.policy aliases",
|
||||
{ timeout: DOCTOR_MIGRATION_TIMEOUT_MS },
|
||||
async () => {
|
||||
readConfigFileSnapshot.mockResolvedValue({
|
||||
@@ -36,19 +36,7 @@ describe("doctor command", () => {
|
||||
|
||||
await doctorCommand(runtime, { nonInteractive: true, repair: true });
|
||||
|
||||
expect(writeConfigFile).toHaveBeenCalledTimes(1);
|
||||
const written = writeConfigFile.mock.calls[0]?.[0] as Record<string, unknown>;
|
||||
const channels = (written.channels ?? {}) as Record<string, unknown>;
|
||||
const slack = (channels.slack ?? {}) as Record<string, unknown>;
|
||||
const discord = (channels.discord ?? {}) as Record<string, unknown>;
|
||||
|
||||
expect(slack.dmPolicy).toBe("open");
|
||||
expect(slack.allowFrom).toEqual(["*"]);
|
||||
expect(slack.dm).toEqual({ enabled: true });
|
||||
|
||||
expect(discord.dmPolicy).toBe("allowlist");
|
||||
expect(discord.allowFrom).toEqual(["123"]);
|
||||
expect(discord.dm).toEqual({ enabled: true });
|
||||
expect(writeConfigFile).not.toHaveBeenCalled();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -397,7 +397,6 @@ describe("legacy migrate channel streaming aliases", () => {
|
||||
expect.objectContaining({ path: "channels.googlechat.accounts" }),
|
||||
]),
|
||||
);
|
||||
|
||||
const res = migrateLegacyConfig(raw);
|
||||
expect(res.changes).toContain(
|
||||
"Removed channels.googlechat.streamMode (legacy key no longer used).",
|
||||
|
||||
@@ -41,7 +41,7 @@ describe("sessionsCommand model resolution", () => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("prefers runtime model fields for subagent sessions in JSON output", async () => {
|
||||
it("prefers the persisted override model for subagent sessions in JSON output", async () => {
|
||||
const model = await resolveSubagentModel(
|
||||
{
|
||||
modelProvider: "openai-codex",
|
||||
@@ -50,7 +50,7 @@ describe("sessionsCommand model resolution", () => {
|
||||
},
|
||||
"subagent-1",
|
||||
);
|
||||
expect(model).toBe("gpt-5.4");
|
||||
expect(model).toBe("pi:opus");
|
||||
});
|
||||
|
||||
it("falls back to modelOverride when runtime model is missing", async () => {
|
||||
|
||||
@@ -322,7 +322,6 @@ describe("legacy config detection", () => {
|
||||
expect(
|
||||
(config.channels?.slack as Record<string, unknown> | undefined)?.streamMode,
|
||||
).toBeUndefined();
|
||||
expect(config.channels?.slack?.streaming?.nativeTransport).toBe(true);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user