File size: 5,472 Bytes
fc93158 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | import { Command } from "commander";
import { describe, expect, it, vi } from "vitest";
import type { ProgramContext } from "./context.js";
// Perf: `registerCoreCliByName(...)` dynamically imports registrar modules.
// Mock the heavy registrars so this suite stays focused on command-registry wiring.
vi.mock("./register.agent.js", () => ({
registerAgentCommands: (program: Command) => {
program.command("agent");
program.command("agents");
},
}));
vi.mock("./register.backup.js", () => ({
registerBackupCommand: (program: Command) => {
const backup = program.command("backup");
backup.command("create");
},
}));
vi.mock("./register.maintenance.js", () => ({
registerMaintenanceCommands: (program: Command) => {
program.command("doctor");
program.command("dashboard");
program.command("reset");
program.command("uninstall");
},
}));
const {
getCoreCliCommandNames,
getCoreCliCommandsWithSubcommands,
registerCoreCliByName,
registerCoreCliCommands,
} = await import("./command-registry.js");
vi.mock("./register.status-health-sessions.js", () => ({
registerStatusHealthSessionsCommands: (program: Command) => {
program.command("status");
program.command("health");
program.command("sessions");
},
}));
const testProgramContext: ProgramContext = {
programVersion: "0.0.0-test",
channelOptions: [],
messageChannelOptions: "",
agentChannelOptions: "web",
};
describe("command-registry", () => {
const createProgram = () => new Command();
const namesOf = (program: Command) => program.commands.map((command) => command.name());
const withProcessArgv = async (argv: string[], run: () => Promise<void>) => {
const prevArgv = process.argv;
process.argv = argv;
try {
await run();
} finally {
process.argv = prevArgv;
}
};
it("includes both agent and agents in core CLI command names", () => {
const names = getCoreCliCommandNames();
expect(names).toContain("agent");
expect(names).toContain("agents");
});
it("returns only commands that support subcommands", () => {
const names = getCoreCliCommandsWithSubcommands();
expect(names).toContain("config");
expect(names).toContain("memory");
expect(names).toContain("agents");
expect(names).toContain("backup");
expect(names).toContain("browser");
expect(names).toContain("sessions");
expect(names).not.toContain("agent");
expect(names).not.toContain("status");
expect(names).not.toContain("doctor");
});
it("registerCoreCliByName resolves agents to the agent entry", async () => {
const program = createProgram();
const found = await registerCoreCliByName(program, testProgramContext, "agents");
expect(found).toBe(true);
const agentsCmd = program.commands.find((c) => c.name() === "agents");
expect(agentsCmd).toBeDefined();
// The registrar also installs the singular "agent" command from the same entry.
const agentCmd = program.commands.find((c) => c.name() === "agent");
expect(agentCmd).toBeDefined();
});
it("registerCoreCliByName returns false for unknown commands", async () => {
const program = createProgram();
const found = await registerCoreCliByName(program, testProgramContext, "nonexistent");
expect(found).toBe(false);
});
it("registers doctor placeholder for doctor primary command", () => {
const program = createProgram();
registerCoreCliCommands(program, testProgramContext, ["node", "openclaw", "doctor"]);
expect(namesOf(program)).toEqual(["doctor"]);
});
it("does not narrow to the primary command when help is requested", () => {
const program = createProgram();
registerCoreCliCommands(program, testProgramContext, ["node", "openclaw", "doctor", "--help"]);
const names = namesOf(program);
expect(names).toContain("doctor");
expect(names).toContain("status");
expect(names.length).toBeGreaterThan(1);
});
it("treats maintenance commands as top-level builtins", async () => {
const program = createProgram();
expect(await registerCoreCliByName(program, testProgramContext, "doctor")).toBe(true);
const names = getCoreCliCommandNames();
expect(names).toContain("doctor");
expect(names).toContain("dashboard");
expect(names).toContain("reset");
expect(names).toContain("uninstall");
expect(names).not.toContain("maintenance");
});
it("registers grouped core entry placeholders without duplicate command errors", async () => {
const program = createProgram();
registerCoreCliCommands(program, testProgramContext, ["node", "openclaw", "vitest"]);
program.exitOverride();
await withProcessArgv(["node", "openclaw", "status"], async () => {
await program.parseAsync(["node", "openclaw", "status"]);
});
const names = namesOf(program);
expect(names).toContain("status");
expect(names).toContain("health");
expect(names).toContain("sessions");
});
it("replaces placeholders when loading a grouped entry by secondary command name", async () => {
const program = createProgram();
registerCoreCliCommands(program, testProgramContext, ["node", "openclaw", "doctor"]);
expect(namesOf(program)).toEqual(["doctor"]);
const found = await registerCoreCliByName(program, testProgramContext, "dashboard");
expect(found).toBe(true);
expect(namesOf(program)).toEqual(["doctor", "dashboard", "reset", "uninstall"]);
});
});
|