mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-18 13:30:48 +00:00
* CLI: lazy-load channel subcommand handlers * Channels: defer add command dependencies * CLI: skip status JSON plugin preload * CLI: cover status JSON route preload * Status: trim JSON security audit path * Status: update JSON fast-path tests * CLI: cover root help fast path * CLI: fast-path root help * Status: keep JSON security parity * Status: restore JSON security tests * CLI: document status plugin preload * Channels: reuse Telegram account import
210 lines
6.9 KiB
TypeScript
210 lines
6.9 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { findRoutedCommand } from "./routes.js";
|
|
|
|
const runConfigGetMock = vi.hoisted(() => vi.fn(async () => {}));
|
|
const runConfigUnsetMock = vi.hoisted(() => vi.fn(async () => {}));
|
|
const modelsListCommandMock = vi.hoisted(() => vi.fn(async () => {}));
|
|
const modelsStatusCommandMock = vi.hoisted(() => vi.fn(async () => {}));
|
|
|
|
vi.mock("../config-cli.js", () => ({
|
|
runConfigGet: runConfigGetMock,
|
|
runConfigUnset: runConfigUnsetMock,
|
|
}));
|
|
|
|
vi.mock("../../commands/models.js", () => ({
|
|
modelsListCommand: modelsListCommandMock,
|
|
modelsStatusCommand: modelsStatusCommandMock,
|
|
}));
|
|
|
|
describe("program routes", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
function expectRoute(path: string[]) {
|
|
const route = findRoutedCommand(path);
|
|
expect(route).not.toBeNull();
|
|
return route;
|
|
}
|
|
|
|
async function expectRunFalse(path: string[], argv: string[]) {
|
|
const route = expectRoute(path);
|
|
await expect(route?.run(argv)).resolves.toBe(false);
|
|
}
|
|
|
|
it("matches status route and always preloads plugins", () => {
|
|
const route = expectRoute(["status"]);
|
|
expect(route?.loadPlugins).toBe(true);
|
|
});
|
|
|
|
it("matches health route and preloads plugins only for text output", () => {
|
|
const route = expectRoute(["health"]);
|
|
expect(typeof route?.loadPlugins).toBe("function");
|
|
const shouldLoad = route?.loadPlugins as (argv: string[]) => boolean;
|
|
expect(shouldLoad(["node", "openclaw", "health"])).toBe(true);
|
|
expect(shouldLoad(["node", "openclaw", "health", "--json"])).toBe(false);
|
|
});
|
|
|
|
it("returns false when status timeout flag value is missing", async () => {
|
|
await expectRunFalse(["status"], ["node", "openclaw", "status", "--timeout"]);
|
|
});
|
|
|
|
it("returns false for sessions route when --store value is missing", async () => {
|
|
await expectRunFalse(["sessions"], ["node", "openclaw", "sessions", "--store"]);
|
|
});
|
|
|
|
it("returns false for sessions route when --active value is missing", async () => {
|
|
await expectRunFalse(["sessions"], ["node", "openclaw", "sessions", "--active"]);
|
|
});
|
|
|
|
it("returns false for sessions route when --agent value is missing", async () => {
|
|
await expectRunFalse(["sessions"], ["node", "openclaw", "sessions", "--agent"]);
|
|
});
|
|
|
|
it("does not fast-route sessions subcommands", () => {
|
|
expect(findRoutedCommand(["sessions", "cleanup"])).toBeNull();
|
|
});
|
|
|
|
it("does not match unknown routes", () => {
|
|
expect(findRoutedCommand(["definitely-not-real"])).toBeNull();
|
|
});
|
|
|
|
it("returns false for config get route when path argument is missing", async () => {
|
|
await expectRunFalse(["config", "get"], ["node", "openclaw", "config", "get", "--json"]);
|
|
});
|
|
|
|
it("returns false for config unset route when path argument is missing", async () => {
|
|
await expectRunFalse(["config", "unset"], ["node", "openclaw", "config", "unset"]);
|
|
});
|
|
|
|
it("passes config get path correctly when root option values precede command", async () => {
|
|
const route = expectRoute(["config", "get"]);
|
|
await expect(
|
|
route?.run([
|
|
"node",
|
|
"openclaw",
|
|
"--log-level",
|
|
"debug",
|
|
"config",
|
|
"get",
|
|
"update.channel",
|
|
"--json",
|
|
]),
|
|
).resolves.toBe(true);
|
|
expect(runConfigGetMock).toHaveBeenCalledWith({ path: "update.channel", json: true });
|
|
});
|
|
|
|
it("passes config unset path correctly when root option values precede command", async () => {
|
|
const route = expectRoute(["config", "unset"]);
|
|
await expect(
|
|
route?.run(["node", "openclaw", "--profile", "work", "config", "unset", "update.channel"]),
|
|
).resolves.toBe(true);
|
|
expect(runConfigUnsetMock).toHaveBeenCalledWith({ path: "update.channel" });
|
|
});
|
|
|
|
it("passes config get path when root value options appear after subcommand", async () => {
|
|
const route = expectRoute(["config", "get"]);
|
|
await expect(
|
|
route?.run([
|
|
"node",
|
|
"openclaw",
|
|
"config",
|
|
"get",
|
|
"--log-level",
|
|
"debug",
|
|
"update.channel",
|
|
"--json",
|
|
]),
|
|
).resolves.toBe(true);
|
|
expect(runConfigGetMock).toHaveBeenCalledWith({ path: "update.channel", json: true });
|
|
});
|
|
|
|
it("passes config unset path when root value options appear after subcommand", async () => {
|
|
const route = expectRoute(["config", "unset"]);
|
|
await expect(
|
|
route?.run(["node", "openclaw", "config", "unset", "--profile", "work", "update.channel"]),
|
|
).resolves.toBe(true);
|
|
expect(runConfigUnsetMock).toHaveBeenCalledWith({ path: "update.channel" });
|
|
});
|
|
|
|
it("returns false for config get route when unknown option appears", async () => {
|
|
await expectRunFalse(
|
|
["config", "get"],
|
|
["node", "openclaw", "config", "get", "--mystery", "value", "update.channel"],
|
|
);
|
|
});
|
|
|
|
it("returns false for memory status route when --agent value is missing", async () => {
|
|
await expectRunFalse(["memory", "status"], ["node", "openclaw", "memory", "status", "--agent"]);
|
|
});
|
|
|
|
it("returns false for models list route when --provider value is missing", async () => {
|
|
await expectRunFalse(["models", "list"], ["node", "openclaw", "models", "list", "--provider"]);
|
|
});
|
|
|
|
it("returns false for models status route when probe flags are missing values", async () => {
|
|
await expectRunFalse(
|
|
["models", "status"],
|
|
["node", "openclaw", "models", "status", "--probe-provider"],
|
|
);
|
|
await expectRunFalse(
|
|
["models", "status"],
|
|
["node", "openclaw", "models", "status", "--probe-timeout"],
|
|
);
|
|
await expectRunFalse(
|
|
["models", "status"],
|
|
["node", "openclaw", "models", "status", "--probe-concurrency"],
|
|
);
|
|
await expectRunFalse(
|
|
["models", "status"],
|
|
["node", "openclaw", "models", "status", "--probe-max-tokens"],
|
|
);
|
|
await expectRunFalse(
|
|
["models", "status"],
|
|
["node", "openclaw", "models", "status", "--probe-provider", "openai", "--agent"],
|
|
);
|
|
});
|
|
|
|
it("returns false for models status route when --probe-profile has no value", async () => {
|
|
await expectRunFalse(
|
|
["models", "status"],
|
|
["node", "openclaw", "models", "status", "--probe-profile"],
|
|
);
|
|
});
|
|
|
|
it("accepts negative-number probe profile values", async () => {
|
|
const route = expectRoute(["models", "status"]);
|
|
await expect(
|
|
route?.run([
|
|
"node",
|
|
"openclaw",
|
|
"models",
|
|
"status",
|
|
"--probe-provider",
|
|
"openai",
|
|
"--probe-timeout",
|
|
"5000",
|
|
"--probe-concurrency",
|
|
"2",
|
|
"--probe-max-tokens",
|
|
"64",
|
|
"--probe-profile",
|
|
"-1",
|
|
"--agent",
|
|
"default",
|
|
]),
|
|
).resolves.toBe(true);
|
|
expect(modelsStatusCommandMock).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
probeProvider: "openai",
|
|
probeTimeout: "5000",
|
|
probeConcurrency: "2",
|
|
probeMaxTokens: "64",
|
|
probeProfile: "-1",
|
|
agent: "default",
|
|
}),
|
|
expect.any(Object),
|
|
);
|
|
});
|
|
});
|