mirror of
https://github.com/modelstudioai/cli.git
synced 2026-09-14 19:49:23 +08:00
perf(test): run registry smoke help checks in process
This commit is contained in:
@@ -2,16 +2,16 @@
|
||||
|
||||
## 架构分层
|
||||
|
||||
| 层级 | 路径 | 测什么 |
|
||||
| --------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
||||
| **共享基建** | `packages/e2e` | gating、子进程 runner、output、globalSetup(`private`,不发布) |
|
||||
| **命令 E2E** | `packages/commands/tests/e2e` | help、缺参、dry-run、live(gated);每用例最小路由 |
|
||||
| **Journey E2E** | `packages/commands/tests/e2e/knowledge/journeys` | 用户旅程全链路(跨命令回路 + 标记词召回闭环),全部 live gated;见 `journeys/README.md` |
|
||||
| **bl smoke** | `packages/cli/tests/e2e/registry.smoke.e2e.test.ts` | 产品 map 全部 path `--help`、分组 help、根 help |
|
||||
| **kscli smoke** | `packages/kscli/tests/e2e/registry.smoke.e2e.test.ts` | 从 `kscli/src/commands.ts` 推导 path/分组;identity(`--version`、`search --help` path) |
|
||||
| **runtime** | `packages/runtime/tests` | `proxy.e2e`、console 跨域 flag 拒绝 |
|
||||
| 层级 | 路径 | 测什么 |
|
||||
| --------------- | ----------------------------------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| **共享基建** | `packages/e2e` | gating、子进程 runner、registry help 捕获、output、globalSetup(`private`,不发布) |
|
||||
| **命令 E2E** | `packages/commands/tests/e2e` | help、缺参、dry-run、live(gated);每用例最小路由 |
|
||||
| **Journey E2E** | `packages/commands/tests/e2e/knowledge/journeys` | 用户旅程全链路(跨命令回路 + 标记词召回闭环),全部 live gated;见 `journeys/README.md` |
|
||||
| **bl smoke** | `packages/cli/tests/e2e/registry.smoke.e2e.test.ts` | 产品 map 全部 path/分组的进程内 help;根 help、鉴权域等代表性子进程冒烟 |
|
||||
| **kscli smoke** | `packages/kscli/tests/e2e/registry.smoke.e2e.test.ts` | map 全部 path/分组的进程内 help;`--version`、`search --help` 等代表性子进程冒烟 |
|
||||
| **runtime** | `packages/runtime/tests` | `proxy.e2e`、console 跨域 flag 拒绝 |
|
||||
|
||||
**依赖边界**:`e2e` → `core`;`commands/tests` → `e2e` + `commands/src`;产品 tests → `e2e` + 各自 `src`。**禁止**产品 import `commands/tests/**`(子进程 spawn harness 路径除外)。
|
||||
**依赖边界**:`e2e` → `core`;`commands/tests` → `e2e` + `commands/src`;产品 tests → `e2e` + 各自 `src` + `runtime` 公共 API。**禁止**产品 import `commands/tests/**`(子进程 spawn harness 路径除外)。
|
||||
|
||||
## 触发条件
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
|
||||
- bl:`runCli` from `packages/cli/tests/e2e/helpers.ts`
|
||||
- kscli:`runKscli` from `packages/kscli/tests/e2e/helpers.ts`
|
||||
- 全量 leaf/group help 使用产品 `commands` 创建 `CommandRegistry`,先通过 `resolve([...path, "--help"])` 检查 help 路由,再用 `captureRegistryHelp` 检查完整 Usage;禁止在 `test.each(commandPaths/groupPaths)` 中逐条启动 `tsx` 子进程
|
||||
- 真实子进程只保留根 help/version、产品身份、代表性叶子 help/鉴权域和缺参退出码等 shell/stdio/env 契约
|
||||
|
||||
### 共享
|
||||
|
||||
|
||||
@@ -1,14 +1,26 @@
|
||||
import { describe, expect, test } from "vite-plus/test";
|
||||
import { deriveGroupPaths } from "e2e/registry-smoke";
|
||||
import { mkdtempSync, rmSync } from "fs";
|
||||
import { tmpdir } from "os";
|
||||
import { join } from "path";
|
||||
import { afterAll, describe, expect, test } from "vite-plus/test";
|
||||
import { captureRegistryHelp, deriveGroupPaths } from "e2e/registry-smoke";
|
||||
import { CommandRegistry, resolve } from "bailian-cli-runtime";
|
||||
import { commands } from "../../src/commands.ts";
|
||||
import { runCli } from "./helpers.ts";
|
||||
|
||||
const commandPaths = Object.keys(commands).sort();
|
||||
const groupPaths = deriveGroupPaths(commandPaths);
|
||||
const registry = new CommandRegistry(commands, "bl");
|
||||
const isolatedConfigDir = mkdtempSync(join(tmpdir(), "bl-registry-smoke-"));
|
||||
|
||||
afterAll(() => rmSync(isolatedConfigDir, { recursive: true, force: true }));
|
||||
|
||||
function runCliSmoke(args: string[]) {
|
||||
return runCli(args, { BAILIAN_CONFIG_DIR: isolatedConfigDir });
|
||||
}
|
||||
|
||||
describe("e2e: bl registry smoke", () => {
|
||||
test("根帮助展示 bl、逐命令鉴权域与全局 flag", async () => {
|
||||
const { stderr, exitCode } = await runCli(["--help"]);
|
||||
const { stderr, exitCode } = await runCliSmoke(["--help"]);
|
||||
expect(exitCode, stderr).toBe(0);
|
||||
expect(stderr).toMatch(/\bbl\b/i);
|
||||
expect(stderr).not.toMatch(/COMMAND\s+AUTH\s+DESCRIPTION/);
|
||||
@@ -24,7 +36,7 @@ describe("e2e: bl registry smoke", () => {
|
||||
});
|
||||
|
||||
test("分组帮助按叶子命令展示不同鉴权域", async () => {
|
||||
const { stderr, exitCode } = await runCli(["app", "--help"]);
|
||||
const { stderr, exitCode } = await runCliSmoke(["app", "--help"]);
|
||||
expect(exitCode, stderr).toBe(0);
|
||||
expect(stderr).toMatch(/app call\s+\[API Key\]\s+Call a Bailian application/);
|
||||
expect(stderr).toMatch(/app list\s+\[Console\]\s+List Bailian applications/);
|
||||
@@ -36,13 +48,13 @@ describe("e2e: bl registry smoke", () => {
|
||||
[["token-plan", "list-seats"], "AK/SK"],
|
||||
[["config", "show"], "No Auth"],
|
||||
] as const)("%s --help 明确展示鉴权域 %s", async (commandPath, authLabel) => {
|
||||
const { stderr, exitCode } = await runCli([...commandPath, "--help"]);
|
||||
const { stderr, exitCode } = await runCliSmoke([...commandPath, "--help"]);
|
||||
expect(exitCode, stderr).toBe(0);
|
||||
expect(stderr).toContain(`Authentication: ${authLabel}`);
|
||||
});
|
||||
|
||||
test("quota check --help:Flags 含 console 域鉴权 flag,Global Flags 全量列出", async () => {
|
||||
const { stderr, exitCode } = await runCli(["quota", "check", "--help"]);
|
||||
const { stderr, exitCode } = await runCliSmoke(["quota", "check", "--help"]);
|
||||
expect(exitCode, stderr).toBe(0);
|
||||
expect(stderr).toMatch(/Global Flags:/);
|
||||
expect(stderr).toMatch(/--console-region <region>/);
|
||||
@@ -52,13 +64,25 @@ describe("e2e: bl registry smoke", () => {
|
||||
expect(stderr).not.toMatch(/API region \(default: cn-beijing\)/);
|
||||
});
|
||||
|
||||
test.each(commandPaths)("已注册命令 %s --help 成功", async (path) => {
|
||||
const { stderr, exitCode } = await runCli([...path.split(" "), "--help"]);
|
||||
expect(exitCode, stderr).toBe(0);
|
||||
test.each(commandPaths)("已注册命令 %s --help 成功", (path) => {
|
||||
const commandPath = path.split(" ");
|
||||
|
||||
expect(resolve([...commandPath, "--help"], registry)).toEqual({
|
||||
kind: "help",
|
||||
path: commandPath,
|
||||
});
|
||||
expect(captureRegistryHelp(registry, commandPath)).toContain(`Usage: bl ${path}`);
|
||||
});
|
||||
|
||||
test.each(groupPaths)("命令分组 %s --help 成功", async (path) => {
|
||||
const { stderr, exitCode } = await runCli([...path.split(" "), "--help"]);
|
||||
expect(exitCode, stderr).toBe(0);
|
||||
test.each(groupPaths)("命令分组 %s --help 成功", (path) => {
|
||||
const commandPath = path.split(" ");
|
||||
|
||||
expect(resolve([...commandPath, "--help"], registry)).toEqual({
|
||||
kind: "help",
|
||||
path: commandPath,
|
||||
});
|
||||
expect(captureRegistryHelp(registry, commandPath)).toContain(
|
||||
`Usage: bl ${path} <command> [flags]`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,8 +6,8 @@ export function deriveGroupPaths(commandPaths: string[]): string[] {
|
||||
const groups = new Set<string>();
|
||||
for (const path of commandPaths) {
|
||||
const parts = path.split(" ");
|
||||
for (let i = 1; i < parts.length; i++) {
|
||||
const prefix = parts.slice(0, i).join(" ");
|
||||
for (let partIndex = 1; partIndex < parts.length; partIndex++) {
|
||||
const prefix = parts.slice(0, partIndex).join(" ");
|
||||
const hasChildren = commandPaths.some(
|
||||
(candidate) => candidate.startsWith(`${prefix} `) && candidate !== prefix,
|
||||
);
|
||||
@@ -16,3 +16,21 @@ export function deriveGroupPaths(commandPaths: string[]): string[] {
|
||||
}
|
||||
return [...groups].sort();
|
||||
}
|
||||
|
||||
interface RegistryHelpPrinter {
|
||||
printHelp(commandPath: string[], output: NodeJS.WriteStream): void;
|
||||
}
|
||||
|
||||
export function captureRegistryHelp(printer: RegistryHelpPrinter, commandPath: string[]): string {
|
||||
let helpOutput = "";
|
||||
const output = {
|
||||
isTTY: false,
|
||||
write(chunk: string): boolean {
|
||||
helpOutput += chunk;
|
||||
return true;
|
||||
},
|
||||
} as NodeJS.WriteStream;
|
||||
|
||||
printer.printHelp(commandPath, output);
|
||||
return helpOutput;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { expect, test } from "vite-plus/test";
|
||||
import { captureRegistryHelp } from "../src/registry-smoke.ts";
|
||||
|
||||
test("captureRegistryHelp 捕获指定命令路径的非 TTY 输出", () => {
|
||||
const requestedPaths: string[][] = [];
|
||||
const renderer = {
|
||||
printHelp(commandPath: string[], output: NodeJS.WriteStream): void {
|
||||
requestedPaths.push(commandPath);
|
||||
output.write(`Usage: bl ${commandPath.join(" ")}`);
|
||||
},
|
||||
};
|
||||
|
||||
const output = captureRegistryHelp(renderer, ["text", "chat"]);
|
||||
|
||||
expect(output).toBe("Usage: bl text chat");
|
||||
expect(requestedPaths).toEqual([["text", "chat"]]);
|
||||
});
|
||||
@@ -1,11 +1,13 @@
|
||||
import { describe, expect, test } from "vite-plus/test";
|
||||
import { deriveGroupPaths } from "e2e/registry-smoke";
|
||||
import { captureRegistryHelp, deriveGroupPaths } from "e2e/registry-smoke";
|
||||
import { CommandRegistry, resolve } from "bailian-cli-runtime";
|
||||
import pkg from "../../package.json" with { type: "json" };
|
||||
import { commands } from "../../src/commands.ts";
|
||||
import { runKscli } from "./helpers.ts";
|
||||
|
||||
const commandPaths = Object.keys(commands).sort();
|
||||
const groupPaths = deriveGroupPaths(commandPaths);
|
||||
const registry = new CommandRegistry(commands, "kscli");
|
||||
|
||||
describe("e2e: kscli registry smoke", () => {
|
||||
test("根帮助展示 kscli 与全局 flag", async () => {
|
||||
@@ -38,13 +40,25 @@ describe("e2e: kscli registry smoke", () => {
|
||||
expect(stderr).toMatch(/--query|Missing required/i);
|
||||
});
|
||||
|
||||
test.each(commandPaths)("已注册命令 %s --help 成功", async (path) => {
|
||||
const { stderr, exitCode } = await runKscli([...path.split(" "), "--help"]);
|
||||
expect(exitCode, stderr).toBe(0);
|
||||
test.each(commandPaths)("已注册命令 %s --help 成功", (path) => {
|
||||
const commandPath = path.split(" ");
|
||||
|
||||
expect(resolve([...commandPath, "--help"], registry)).toEqual({
|
||||
kind: "help",
|
||||
path: commandPath,
|
||||
});
|
||||
expect(captureRegistryHelp(registry, commandPath)).toContain(`Usage: kscli ${path}`);
|
||||
});
|
||||
|
||||
test.each(groupPaths)("命令分组 %s --help 成功", async (path) => {
|
||||
const { stderr, exitCode } = await runKscli([...path.split(" "), "--help"]);
|
||||
expect(exitCode, stderr).toBe(0);
|
||||
test.each(groupPaths)("命令分组 %s --help 成功", (path) => {
|
||||
const commandPath = path.split(" ");
|
||||
|
||||
expect(resolve([...commandPath, "--help"], registry)).toEqual({
|
||||
kind: "help",
|
||||
path: commandPath,
|
||||
});
|
||||
expect(captureRegistryHelp(registry, commandPath)).toContain(
|
||||
`Usage: kscli ${path} <command> [flags]`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user