Files
modelstudioai__cli/packages/cli/tests/e2e/command-packs.e2e.test.ts
T

239 lines
8.5 KiB
TypeScript

import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { afterAll, beforeAll, describe, expect, test } from "vite-plus/test";
import { parseStdoutJson, runCli } from "./helpers.ts";
const fixtureRoot = join(fileURLToPath(import.meta.url), "..", "..", "fixtures", "command-pack");
let configDir: string;
function env(): NodeJS.ProcessEnv {
return {
BAILIAN_CONFIG_DIR: configDir,
DO_NOT_TRACK: "1",
DASHSCOPE_API_KEY: "",
DASHSCOPE_BASE_URL: "",
};
}
function parseStderrJsonDiagnostics(stderr: string): unknown[] {
return stderr
.trim()
.split(/\n\s*\n/)
.map((diagnostic) => JSON.parse(diagnostic) as unknown);
}
describe("e2e: Command Pack", () => {
beforeAll(async () => {
configDir = await mkdtemp(join(tmpdir(), "bl-command-pack-e2e-"));
});
afterAll(async () => {
await rm(configDir, { recursive: true, force: true });
});
test("plugin 分组和管理命令 help 正常", async () => {
const group = await runCli(["plugin"], env());
expect(group.exitCode, group.stderr).toBe(0);
expect(group.stderr).toContain("plugin install");
expect(group.stderr).toContain("plugin link");
expect(group.stderr).toContain("plugin list");
expect(group.stderr).toContain("plugin remove");
const install = await runCli(["plugin", "install", "--help"], env());
expect(install.exitCode, install.stderr).toBe(0);
expect(install.stderr).toContain("--package");
});
test("未 link 时插件命令不存在", async () => {
const result = await runCli(["agent", "ping", "--message", "before"], env());
expect(result.exitCode).not.toBe(0);
expect(result.stderr).toMatch(/Unknown command/i);
});
test("link 后命令进入原生 registry/help/执行链路", async () => {
const linked = await runCli(
["plugin", "link", "--path", fixtureRoot, "--output", "json"],
env(),
);
expect(linked.exitCode, linked.stderr).toBe(0);
const linkedJson = parseStdoutJson<{ linked: { name: string; commands: string[] } }>(
linked.stdout,
);
expect(linkedJson.linked.name).toBe("@ali/bailian-plugin-agent");
expect(linkedJson.linked.commands).toEqual([
"agent credential",
"agent credential-denied",
"agent dangerous",
"agent fail",
"agent output",
"agent ping",
]);
const rootHelp = await runCli(["--help"], env());
expect(rootHelp.exitCode, rootHelp.stderr).toBe(0);
expect(rootHelp.stderr).toContain("agent ping");
const commandHelp = await runCli(["agent", "ping", "--help"], env());
expect(commandHelp.exitCode, commandHelp.stderr).toBe(0);
expect(commandHelp.stderr).toContain("Ping the Command Pack fixture");
expect(commandHelp.stderr).toContain("--message");
const executed = await runCli(["agent", "ping", "--message", "hello"], env());
expect(executed.exitCode, executed.stderr).toBe(0);
expect(executed.stdout).toContain("command-pack:hello");
await writeFile(
join(configDir, "config.json"),
JSON.stringify(
{
base_url: "https://default.example.com",
active_config: "company-plan",
"company-plan": {
api_key: "sk-plan",
base_url: "https://plan.example.com",
api_key_capabilities: [],
},
},
null,
2,
) + "\n",
);
const fallbackFailure = await runCli(["agent", "credential", "--output", "json"], env());
expect(fallbackFailure.exitCode).toBe(3);
expect(fallbackFailure.stderr).toMatch(/^\{\n {2}"warning": \{/);
expect(fallbackFailure.stderr).toContain("\n\n{\n");
expect(parseStderrJsonDiagnostics(fallbackFailure.stderr)).toEqual([
{
warning: {
code: "PROFILE_API_KEY_FALLBACK",
message:
'Profile "company-plan" does not support command "agent credential"; API Key settings will be read from Profile "default" for this run.',
profile: "company-plan",
command_path: ["agent", "credential"],
fallback_profile: "default",
credential_fields: ["api_key", "base_url"],
},
},
{
error: expect.objectContaining({
code: 3,
message: "No API key found.",
}),
},
]);
await writeFile(
join(configDir, "config.json"),
JSON.stringify(
{
api_key: "sk-default",
base_url: "https://default.example.com",
active_config: "company-plan",
"company-plan": {
api_key: "sk-plan",
base_url: "https://plan.example.com",
api_key_capabilities: [],
},
},
null,
2,
) + "\n",
);
const fallbackCredential = await runCli(["agent", "credential"], env());
expect(fallbackCredential.exitCode, fallbackCredential.stderr).toBe(0);
expect(fallbackCredential.stderr).toContain('command "agent credential"');
expect(fallbackCredential.stdout).toContain("credential-base-url:https://default.example.com");
const credential = await runCli(["agent", "credential", "--api-key", "fixture-key"], env());
expect(credential.exitCode, credential.stderr).toBe(0);
expect(credential.stdout).toContain("credential-source:flag");
const denied = await runCli(["agent", "credential-denied"], {
...env(),
DASHSCOPE_API_KEY: "fixture-key",
});
expect(denied.exitCode).toBe(1);
expect(denied.stderr).toContain('must declare auth="apiKey"');
const outputText = await runCli(["agent", "output"], env());
expect(outputText.exitCode, outputText.stderr).toBe(0);
expect(outputText.stdout).toBe("command-pack-output\n");
const outputJson = await runCli(["agent", "output", "--output", "json"], env());
expect(outputJson.exitCode, outputJson.stderr).toBe(0);
expect(parseStdoutJson(outputJson.stdout)).toEqual({ source: "command-pack", ok: true });
const failed = await runCli(["agent", "fail", "--output", "text"], env());
expect(failed.exitCode).toBe(2);
expect(failed.stderr).toContain("Command Pack fixture usage error.");
expect(failed.stderr).toContain("Use agent fail only in tests.");
});
test("high-risk 命令由 runtime 统一确认并支持安全 dry-run", async () => {
const dangerousHelp = await runCli(["agent", "dangerous", "--help"], env());
expect(dangerousHelp.exitCode, dangerousHelp.stderr).toBe(0);
expect(dangerousHelp.stderr).toContain("--yes");
const unconfirmed = await runCli(["agent", "dangerous", "--output", "json"], env());
expect(unconfirmed.exitCode).toBe(7);
expect(JSON.parse(unconfirmed.stderr)).toMatchObject({
error: { code: 7, type: "requires_confirmation" },
});
const confirmed = await runCli(["agent", "dangerous", "--yes", "--output", "json"], env());
expect(confirmed.exitCode, confirmed.stderr).toBe(0);
expect(parseStdoutJson(confirmed.stdout)).toEqual({
executed: true,
dry_run: false,
command_flags: [],
});
const preview = await runCli(["agent", "dangerous", "--dry-run", "--output", "json"], env());
expect(preview.exitCode, preview.stderr).toBe(0);
expect(parseStdoutJson(preview.stdout)).toEqual({
executed: false,
dry_run: true,
command_flags: [],
});
});
test("plugin list 输出加载状态", async () => {
const result = await runCli(["plugin", "list", "--output", "json"], env());
expect(result.exitCode, result.stderr).toBe(0);
const json = parseStdoutJson<{
command_packs: Array<{ name: string; status: string; commands: string[] }>;
}>(result.stdout);
expect(json.command_packs).toEqual([
expect.objectContaining({
name: "@ali/bailian-plugin-agent",
status: "loaded",
commands: [
"agent credential",
"agent credential-denied",
"agent dangerous",
"agent fail",
"agent output",
"agent ping",
],
}),
]);
});
test("remove 后命令从下一进程消失", async () => {
const removed = await runCli(
["plugin", "remove", "--name", "@ali/bailian-plugin-agent", "--output", "json"],
env(),
);
expect(removed.exitCode, removed.stderr).toBe(0);
expect(parseStdoutJson<{ removed: string }>(removed.stdout).removed).toBe(
"@ali/bailian-plugin-agent",
);
const result = await runCli(["agent", "ping", "--message", "after"], env());
expect(result.exitCode).not.toBe(0);
expect(result.stderr).toMatch(/Unknown command/i);
});
});