feat: CLI 错误类层次

This commit is contained in:
2026-06-08 22:30:03 +08:00
parent bafbbaa01e
commit dab63975f5
2 changed files with 81 additions and 0 deletions

26
src/cli/errors.ts Normal file
View File

@@ -0,0 +1,26 @@
export class CliError extends Error {
readonly hint?: string;
readonly usage?: string;
constructor(
message: string,
opts?: { hint?: string; usage?: string },
) {
super(message);
this.name = this.constructor.name;
this.hint = opts?.hint;
this.usage = opts?.usage;
}
}
export class UsageError extends CliError {}
export class ConfigError extends CliError {}
export class CommandError extends CliError {}
export class InternalError extends CliError {
constructor() {
super("发生了未预期的错误");
}
}

55
tests/cli/errors.test.ts Normal file
View File

@@ -0,0 +1,55 @@
import { describe, it, expect } from "bun:test";
import {
CliError,
UsageError,
ConfigError,
CommandError,
InternalError,
} from "../../src/cli/errors.ts";
describe("CliError 类层次", () => {
it("UsageError 携带 message 和可选 hint", () => {
const err = new UsageError("缺少参数", { hint: "请提供参数" });
expect(err).toBeInstanceOf(CliError);
expect(err).toBeInstanceOf(UsageError);
expect(err.message).toBe("缺少参数");
expect(err.hint).toBe("请提供参数");
expect(err.usage).toBeUndefined();
});
it("UsageError 携带 message、hint、usage", () => {
const err = new UsageError("缺少参数", {
hint: "请提供参数",
usage: "rune plan <change-name>",
});
expect(err.usage).toBe("rune plan <change-name>");
});
it("ConfigError 只带 message", () => {
const err = new ConfigError("未初始化");
expect(err).toBeInstanceOf(CliError);
expect(err).toBeInstanceOf(ConfigError);
expect(err.message).toBe("未初始化");
expect(err.hint).toBeUndefined();
});
it("CommandError 带 message 和 hint", () => {
const err = new CommandError("变更不存在", { hint: "请先运行 rune plan" });
expect(err).toBeInstanceOf(CliError);
expect(err.message).toBe("变更不存在");
expect(err.hint).toBe("请先运行 rune plan");
});
it("InternalError 用默认 message", () => {
const err = new InternalError();
expect(err).toBeInstanceOf(CliError);
expect(err.message).toBe("发生了未预期的错误");
});
it("可以用 name 区分错误类型", () => {
expect(new UsageError("a").name).toBe("UsageError");
expect(new ConfigError("b").name).toBe("ConfigError");
expect(new CommandError("c").name).toBe("CommandError");
expect(new InternalError().name).toBe("InternalError");
});
});