mirror of
https://github.com/modelstudioai/cli.git
synced 2026-09-14 19:49:23 +08:00
90 lines
3.1 KiB
TypeScript
90 lines
3.1 KiB
TypeScript
import { describe, expect, test, vi } from "vite-plus/test";
|
||
import { defineCommand, ExitCode, type CommandRisk, type LocalizedText } from "bailian-cli-core";
|
||
import { ConfirmationRequiredError, confirmationFlagDefs } from "../src/confirm.ts";
|
||
import { confirmationStage, type RunContext } from "../src/middleware.ts";
|
||
|
||
const HIGH_RISK_MESSAGE = {
|
||
"en-US": "This permanently deletes the document and its chunks.",
|
||
"zh-CN": "该操作会永久删除文档及其 Chunk,且无法撤销。",
|
||
} satisfies LocalizedText;
|
||
|
||
function makeContext(options: {
|
||
risk?: CommandRisk;
|
||
confirmed?: boolean;
|
||
dryRun?: boolean;
|
||
}): RunContext {
|
||
const command = defineCommand({
|
||
description: "Delete a document",
|
||
auth: "none",
|
||
risk: options.risk,
|
||
async run() {},
|
||
});
|
||
return {
|
||
identity: {
|
||
binName: "bl",
|
||
version: "0.0.0-test",
|
||
clientName: "bailian-cli-test",
|
||
npmPackage: "bailian-cli",
|
||
},
|
||
path: ["knowledge", "doc", "delete"],
|
||
command,
|
||
flags: {},
|
||
confirmed: options.confirmed ?? false,
|
||
localize: (text: LocalizedText) => (typeof text === "string" ? text : text["zh-CN"]),
|
||
settings: { dryRun: options.dryRun ?? false } as RunContext["settings"],
|
||
} as unknown as RunContext;
|
||
}
|
||
|
||
describe("confirmation metadata", () => {
|
||
test("injects --yes only for high-risk commands", () => {
|
||
expect(
|
||
confirmationFlagDefs({ risk: { level: "high", message: HIGH_RISK_MESSAGE } }),
|
||
).toHaveProperty("yes");
|
||
expect(confirmationFlagDefs({})).toEqual({});
|
||
});
|
||
|
||
test("serializes the stable Agent-readable confirmation contract", () => {
|
||
const error = new ConfirmationRequiredError({
|
||
message: HIGH_RISK_MESSAGE["zh-CN"],
|
||
hint: "此命令将执行高风险操作。如确认继续,请在原命令中添加 --yes 后重新执行。",
|
||
});
|
||
|
||
expect(error.exitCode).toBe(ExitCode.CONFIRMATION_REQUIRED);
|
||
expect(error.toJSON()).toEqual({
|
||
error: {
|
||
code: 7,
|
||
type: "requires_confirmation",
|
||
message: HIGH_RISK_MESSAGE["zh-CN"],
|
||
hint: "此命令将执行高风险操作。如确认继续,请在原命令中添加 --yes 后重新执行。",
|
||
},
|
||
});
|
||
});
|
||
});
|
||
|
||
describe("confirmationStage", () => {
|
||
test("blocks high-risk execution without echoing the original command", async () => {
|
||
const next = vi.fn(async () => {});
|
||
|
||
const promise = confirmationStage(
|
||
makeContext({ risk: { level: "high", message: HIGH_RISK_MESSAGE } }),
|
||
next,
|
||
);
|
||
await expect(promise).rejects.toMatchObject({
|
||
exitCode: 7,
|
||
message: HIGH_RISK_MESSAGE["zh-CN"],
|
||
hint: "此命令将执行高风险操作。如确认继续,请在原命令中添加 --yes 后重新执行。",
|
||
});
|
||
expect(next).not.toHaveBeenCalled();
|
||
});
|
||
|
||
test.each([
|
||
["explicit --yes", { risk: { level: "high", message: HIGH_RISK_MESSAGE }, confirmed: true }],
|
||
["dry-run", { risk: { level: "high", message: HIGH_RISK_MESSAGE }, dryRun: true }],
|
||
["normal command", {}],
|
||
] as const)("passes %s through", async (_label, options) => {
|
||
const next = vi.fn(async () => {});
|
||
await confirmationStage(makeContext(options), next);
|
||
expect(next).toHaveBeenCalledOnce();
|
||
});
|
||
});
|