mirror of
https://github.com/modelstudioai/cli.git
synced 2026-09-14 19:49:23 +08:00
91e6c6f553
把 main 从一堆 if + process.exit 重构为「argv 解析成数据 → 交给统一管线执行」。 内核 - resolve(argv) → Resolution(version/help/run/usageError):路由即数据,dispatch 只 switch - compose 洋葱中间件 (versionCheck/telemetry/auth/runCommand),命令仍收 (config, flags) - registry.locate() 统一 leaf/group/unknown,取代 isGroupPath + 抛异常的 resolve - 删除 command-help.ts 全局可变单例:help 渲染收口到错误边界 错误模型 - 新增 UsageError(写错了 → exit 2) 与 IncompleteCommandError(没写完 → 打 help、exit 0) - version / help / onboarding / 组帮助统一由 resolve 产出、dispatch 分派 参数与校验 - parseFlags 重写:无 positional、新增 switch 类型、值/类型/重复校验 - 无条件必填 → 解析器声明式强制 (OptionDef.required) - 跨 flag / 条件约束 → 新增 command.validate(flags) 钩子 (text-chat / search-web / speech / vision / video-ref) - 移除全部交互式输入 (promptText/Select/Confirm),缺输入直接打 help 输出 - detectOutputFormat 默认 text,不再按 TTY 切 json 测试 - 删除 3 个 stale cli 测试,runtime 单测重写 (29 passed),e2e 适配新行为
192 lines
5.6 KiB
TypeScript
192 lines
5.6 KiB
TypeScript
import { describe, expect, test } from "vite-plus/test";
|
||
import { parseStdoutJson, runCli } from "./helpers.ts";
|
||
|
||
/**
|
||
* Config 相关 E2E
|
||
*/
|
||
|
||
describe("e2e: config", () => {
|
||
test("config 分组展示子命令帮助且成功退出", async () => {
|
||
const { stdout, stderr, exitCode } = await runCli(["config"]);
|
||
expect(exitCode, stderr).toBe(0);
|
||
const out = `${stdout}\n${stderr}`;
|
||
expect(out).toMatch(/config|show|set|export-schema/i);
|
||
});
|
||
|
||
test("config show --help 正常退出", async () => {
|
||
const { stderr, exitCode } = await runCli(["config", "show", "--help"]);
|
||
expect(exitCode, stderr).toBe(0);
|
||
expect(stderr).toMatch(/show|config/i);
|
||
});
|
||
|
||
test("config set --help 正常退出", async () => {
|
||
const { stderr, exitCode } = await runCli(["config", "set", "--help"]);
|
||
expect(exitCode, stderr).toBe(0);
|
||
expect(stderr).toMatch(/set|--key|--value/i);
|
||
});
|
||
|
||
test("config export-schema --help 正常退出", async () => {
|
||
const { stderr, exitCode } = await runCli(["config", "export-schema", "--help"]);
|
||
expect(exitCode, stderr).toBe(0);
|
||
expect(stderr).toMatch(/export-schema|--command/i);
|
||
});
|
||
|
||
test("config show --output json", async () => {
|
||
const { stdout, stderr, exitCode } = await runCli([
|
||
"config",
|
||
"show",
|
||
"--non-interactive",
|
||
"--output",
|
||
"json",
|
||
]);
|
||
expect(exitCode, stderr).toBe(0);
|
||
const data = parseStdoutJson<{
|
||
config_file?: string;
|
||
base_url?: string;
|
||
timeout?: number;
|
||
}>(stdout);
|
||
expect(data.config_file).toBeDefined();
|
||
expect(data.base_url).toBeDefined();
|
||
expect(data.timeout).toBeDefined();
|
||
});
|
||
|
||
test("config show --output text --no-color", async () => {
|
||
const { stdout, stderr, exitCode } = await runCli([
|
||
"config",
|
||
"show",
|
||
"--non-interactive",
|
||
"--output",
|
||
"text",
|
||
"--no-color",
|
||
]);
|
||
expect(exitCode, stderr).toBe(0);
|
||
expect(stdout).toMatch(/config_file|timeout|base_url/i);
|
||
});
|
||
|
||
test("config set 缺少 --key / --value 时打印子命令帮助并退出 (0)", async () => {
|
||
const { stderr, exitCode } = await runCli(["config", "set", "--non-interactive"]);
|
||
expect(exitCode, stderr).toBe(0);
|
||
expect(stderr).toMatch(/--key|--value|Usage:/i);
|
||
});
|
||
|
||
test("config set 非法 key 时退出为用法错误", async () => {
|
||
const { stderr, exitCode } = await runCli([
|
||
"config",
|
||
"set",
|
||
"--non-interactive",
|
||
"--key",
|
||
"not-a-real-key",
|
||
"--value",
|
||
"x",
|
||
]);
|
||
expect(exitCode).toBe(2);
|
||
expect(stderr).toMatch(/Invalid config key|not-a-real-key/i);
|
||
});
|
||
|
||
test("config set 非法 output", async () => {
|
||
const { stderr, exitCode } = await runCli([
|
||
"config",
|
||
"set",
|
||
"--non-interactive",
|
||
"--key",
|
||
"output",
|
||
"--value",
|
||
"yaml",
|
||
]);
|
||
expect(exitCode).toBe(2);
|
||
expect(stderr).toMatch(/Invalid output|text, json/i);
|
||
});
|
||
|
||
test("config set 非法 timeout", async () => {
|
||
const { stderr, exitCode } = await runCli([
|
||
"config",
|
||
"set",
|
||
"--non-interactive",
|
||
"--key",
|
||
"timeout",
|
||
"--value",
|
||
"0",
|
||
]);
|
||
expect(exitCode).toBe(2);
|
||
expect(stderr).toMatch(/Invalid timeout|positive/i);
|
||
});
|
||
|
||
test("config set --dry-run 不落盘(仅输出 would_set)", async () => {
|
||
const { stdout, stderr, exitCode } = await runCli([
|
||
"config",
|
||
"set",
|
||
"--dry-run",
|
||
"--non-interactive",
|
||
"--key",
|
||
"output",
|
||
"--value",
|
||
"json",
|
||
"--output",
|
||
"json",
|
||
]);
|
||
expect(exitCode, stderr).toBe(0);
|
||
const data = parseStdoutJson<{ would_set?: { output?: string } }>(stdout);
|
||
expect(data.would_set?.output).toBe("json");
|
||
});
|
||
|
||
test("config set --dry-run 支持连字符别名 key", async () => {
|
||
const { stdout, stderr, exitCode } = await runCli([
|
||
"config",
|
||
"set",
|
||
"--dry-run",
|
||
"--non-interactive",
|
||
"--key",
|
||
"default-text-model",
|
||
"--value",
|
||
"qwen3.7-max",
|
||
"--output",
|
||
"json",
|
||
]);
|
||
expect(exitCode, stderr).toBe(0);
|
||
const data = parseStdoutJson<{ would_set?: { default_text_model?: string } }>(stdout);
|
||
expect(data.would_set?.default_text_model).toBe("qwen3.7-max");
|
||
});
|
||
|
||
test("config export-schema --command 导出单条工具 JSON", async () => {
|
||
const { stdout, stderr, exitCode } = await runCli([
|
||
"config",
|
||
"export-schema",
|
||
"--command",
|
||
"text chat",
|
||
"--non-interactive",
|
||
]);
|
||
expect(exitCode, stderr).toBe(0);
|
||
const schema = parseStdoutJson<{ name?: string; input_schema?: { type?: string } }>(stdout);
|
||
expect(schema.name).toMatch(/bailian_text_chat/);
|
||
expect(schema.input_schema?.type).toBe("object");
|
||
});
|
||
|
||
test("config export-schema 不存在的子命令时报错", async () => {
|
||
const { stderr, exitCode } = await runCli([
|
||
"config",
|
||
"export-schema",
|
||
"--command",
|
||
"this-command-does-not-exist-xyz",
|
||
"--non-interactive",
|
||
"--output",
|
||
"json",
|
||
]);
|
||
expect(exitCode).toBe(2);
|
||
const err = JSON.parse(stderr.trim()) as { error?: { message?: string } };
|
||
expect(err.error?.message).toMatch(/not found/i);
|
||
});
|
||
|
||
test("config export-schema 导出全部为 JSON 数组", async () => {
|
||
const { stdout, stderr, exitCode } = await runCli([
|
||
"config",
|
||
"export-schema",
|
||
"--non-interactive",
|
||
]);
|
||
expect(exitCode, stderr).toBe(0);
|
||
const arr = parseStdoutJson<Array<{ name?: string }>>(stdout);
|
||
expect(Array.isArray(arr)).toBe(true);
|
||
expect(arr.length).toBeGreaterThan(0);
|
||
expect(arr[0]?.name).toMatch(/^bailian_/);
|
||
});
|
||
});
|