Skip to content

Commit 59250c4

Browse files
committed
perf(test): streamline command e2e execution
1 parent 98ebeb3 commit 59250c4

56 files changed

Lines changed: 331 additions & 139 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/agents/cli-e2e-tests.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
| 层级 | 路径 | 测什么 |
66
| --------------- | ----------------------------------------------------- | --------------------------------------------------------------------------------------- |
77
| **共享基建** | `packages/e2e` | gating、子进程 runner、registry help 捕获、output、globalSetup(`private`,不发布) |
8-
| **命令 E2E** | `packages/commands/tests/e2e` | help、缺参、dry-runlive(gated);每用例最小路由 |
8+
| **命令 E2E** | `packages/commands/tests/e2e` | 进程内 help、子进程缺参/dry-run/live(gated);每用例最小路由 |
99
| **Journey E2E** | `packages/commands/tests/e2e/knowledge/journeys` | 用户旅程全链路(跨命令回路 + 标记词召回闭环),全部 live gated;见 `journeys/README.md` |
1010
| **bl smoke** | `packages/cli/tests/e2e/registry.smoke.e2e.test.ts` | 产品 map 全部 path/分组的进程内 help;根 help、鉴权域等代表性子进程冒烟 |
1111
| **kscli smoke** | `packages/kscli/tests/e2e/registry.smoke.e2e.test.ts` | map 全部 path/分组的进程内 help;`--version``search --help` 等代表性子进程冒烟 |
@@ -29,7 +29,8 @@
2929
### commands E2E
3030

3131
- 路径:`packages/commands/tests/e2e/<kebab-topic>.e2e.test.ts`;knowledge 领域集中在 `packages/commands/tests/e2e/knowledge/` 子目录(新增 knowledge 命令测试放这里)
32-
- 子进程:`runCommandE2e(routes, args)` from `./helpers.ts`(spawn `harness/main.ts``routes` 为本 topic 最小 path → export 映射)
32+
- help:`runCommandHelp(routes, [...path, "--help"])` from `./helpers.ts`(当前 Vitest worker 内用真实 command + `CommandRegistry` 渲染,不启动子进程)
33+
- 子进程:缺参、dry-run、live 使用 `runCommandE2e(routes, args)`(spawn `harness/main.ts``routes` 为本 topic 最小 path → export 映射)
3334
- fixtures:`packages/commands/tests/e2e/fixtures/`
3435
- 路由常量:`topic-routes.ts`(按 topic 维护,****全量产品 map)
3536

@@ -42,17 +43,20 @@
4243

4344
### 共享
4445

45-
- gating / output / runner:`e2e/gating``e2e/output``e2e/runner`
46+
- gating / output / runner:`e2e/gating``e2e/output``e2e/runner`;runner 使用 `node --import tsx` 执行 TypeScript 入口,不启动 tsx CLI IPC server
4647
- globalSetup:根 `vite.config.ts``packages/e2e/src/global-setup.ts`
4748
- 解析 JSON stdout:`parseStdoutJson`;输出目录:`makeE2eOutputDir(e2eLabelFromMetaUrl(import.meta.url))`
4849
- 长任务:`cliTimeoutPrefix()`;视频用例加 `test(..., 3_600_000)` 等显式超时
4950

5051
## 双层 describe(固定结构)
5152

5253
```ts
53-
// 1) 不 skip:--help,无密钥、无真实 API(分组 help 由 bl registry.smoke 覆盖)
54+
// 1) 不 skip:进程内 --help,无密钥、无真实 API(分组 help 由 bl registry.smoke 覆盖)
5455
describe("e2e: <topic>", () => {
55-
test("<subcommand> --help 正常退出", ...);
56+
test("<subcommand> --help 正常退出", async () => {
57+
const result = await runCommandHelp(FOO_ROUTES, ["foo", "bar", "--help"]);
58+
expect(result.exitCode, result.stderr).toBe(0);
59+
});
5660
});
5761

5862
// 2) skipIf:缺参 / dry-run / 真实集成
@@ -76,7 +80,7 @@ describe.skipIf(<ready>)("e2e: <topic>(DashScope …)", () => {
7680

7781
## 用例类型
7882

79-
1. **--help**`runCommandE2e(ROUTES, [..., "--help"])` → stderr 含主要 flags
83+
1. **--help**`runCommandHelp(ROUTES, [..., "--help"])` → stderr 含主要 flags;产品层另保留少量真实子进程 help 验证 shell/stdio/env
8084
2. **缺参**:带无害全局 flag(如 `--quiet`)且不传 required flag → `exitCode === 2`
8185
3. **--dry-run**:实现在联网/上传/写盘**之前**返回;断言 stdout JSON/文本
8286
4. **真实集成**:放在 skip 块**末尾**
@@ -107,7 +111,7 @@ describe.skipIf(<ready>)("e2e: <topic>(DashScope …)", () => {
107111
- [ ] `packages/commands/src/index.ts` 导出 + `packages/cli/src/commands.ts` 暴露路径 + `topic-routes.ts` 补最小路由
108112
- [ ] `packages/commands/tests/e2e/<topic>.e2e.test.ts`(新建或扩展)
109113
- [ ] 若改了 `usageArgs` / `flags` / `exampleArgs`,跑 `pnpm --filter bailian-cli run generate:reference` 更新各 `skills/<skill>/reference/` 并提交
110-
- [ ] 子命令 `--help`(分组 help 由 bl `registry.smoke` 覆盖)
114+
- [ ] 子命令 `--help` 使用 `runCommandHelp`(分组 help 由 bl `registry.smoke` 覆盖)
111115
- [ ] skip 块:每个 required flag 缺参;可 dry-run 则加一条
112116
- [ ] 至少一条真实集成(或说明为何仅 smoke);不破坏已有集成用例顺序
113117
- [ ] `vp test packages/commands/tests/e2e/<file>` 通过

packages/commands/tests/e2e/advisor-recommend.e2e.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { describe, expect, test } from "vite-plus/test";
2-
import { isDashScopeE2EReady, parseStdoutJson, runCommandE2e } from "./helpers.ts";
2+
import { isDashScopeE2EReady, parseStdoutJson, runCommandHelp, runCommandE2e } from "./helpers.ts";
33
import { ADVISOR_ROUTES } from "./topic-routes.ts";
44

55
describe("e2e: advisor recommend", () => {
66
test("advisor recommend --help exits successfully", async () => {
7-
const { stderr, exitCode } = await runCommandE2e(ADVISOR_ROUTES, [
7+
const { stderr, exitCode } = await runCommandHelp(ADVISOR_ROUTES, [
88
"advisor",
99
"recommend",
1010
"--help",

packages/commands/tests/e2e/auth.e2e.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
isOpenApiE2EReady,
88
makeE2eOutputDir,
99
parseStdoutJson,
10+
runCommandHelp,
1011
runCommandE2e,
1112
} from "./helpers.ts";
1213
import { AUTH_ROUTES } from "./topic-routes.ts";
@@ -15,7 +16,7 @@ import { AUTH_ROUTES } from "./topic-routes.ts";
1516

1617
describe("e2e: auth", () => {
1718
test("auth login --help 正常退出", async () => {
18-
const { stderr, exitCode } = await runCommandE2e(AUTH_ROUTES, ["auth", "login", "--help"]);
19+
const { stderr, exitCode } = await runCommandHelp(AUTH_ROUTES, ["auth", "login", "--help"]);
1920
expect(exitCode, stderr).toBe(0);
2021
expect(stderr).toMatch(/login|api-key/i);
2122
expect(stderr).toMatch(/--console-site/);
@@ -108,13 +109,13 @@ describe("e2e: auth", () => {
108109
});
109110

110111
test("auth logout --help 正常退出", async () => {
111-
const { stderr, exitCode } = await runCommandE2e(AUTH_ROUTES, ["auth", "logout", "--help"]);
112+
const { stderr, exitCode } = await runCommandHelp(AUTH_ROUTES, ["auth", "logout", "--help"]);
112113
expect(exitCode, stderr).toBe(0);
113114
expect(stderr).toMatch(/logout|dry-run|yes/i);
114115
});
115116

116117
test("auth status --help 正常退出", async () => {
117-
const { stderr, exitCode } = await runCommandE2e(AUTH_ROUTES, ["auth", "status", "--help"]);
118+
const { stderr, exitCode } = await runCommandHelp(AUTH_ROUTES, ["auth", "status", "--help"]);
118119
expect(exitCode, stderr).toBe(0);
119120
expect(stderr).toMatch(/status|output/i);
120121
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { expect, test } from "vite-plus/test";
2+
import { captureCommandHelp, runCommandHelp } from "./helpers.ts";
3+
import { QUOTA_ROUTES } from "./topic-routes.ts";
4+
5+
test("captureCommandHelp 用 topic 最小路由渲染真实命令帮助", async () => {
6+
const helpOutput = await captureCommandHelp(QUOTA_ROUTES, ["quota", "list"]);
7+
8+
expect(helpOutput).toContain("Usage: bl quota list");
9+
expect(helpOutput).toContain("--model <model>");
10+
expect(helpOutput).toContain("--name <name>");
11+
expect(helpOutput).toContain("--output <format>");
12+
});
13+
14+
test("runCommandHelp 保持 commands E2E 的 stderr 与 exitCode 断言接口", async () => {
15+
const result = await runCommandHelp(QUOTA_ROUTES, ["quota", "list", "--help"]);
16+
17+
expect(result.exitCode).toBe(0);
18+
expect(result.stdout).toBe("");
19+
expect(result.stderr).toContain("Usage: bl quota list");
20+
});

packages/commands/tests/e2e/config.e2e.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { mkdtempSync, readFileSync, rmSync, writeFileSync, existsSync } from "fs
22
import { tmpdir } from "os";
33
import { join } from "path";
44
import { describe, expect, test } from "vite-plus/test";
5-
import { parseStdoutJson, runCommandE2e } from "./helpers.ts";
5+
import { parseStdoutJson, runCommandHelp, runCommandE2e } from "./helpers.ts";
66
import { CONFIG_ROUTES } from "./topic-routes.ts";
77

88
/**
@@ -11,29 +11,29 @@ import { CONFIG_ROUTES } from "./topic-routes.ts";
1111

1212
describe("e2e: config", () => {
1313
test("config show --help 正常退出", async () => {
14-
const { stderr, exitCode } = await runCommandE2e(CONFIG_ROUTES, ["config", "show", "--help"]);
14+
const { stderr, exitCode } = await runCommandHelp(CONFIG_ROUTES, ["config", "show", "--help"]);
1515
expect(exitCode, stderr).toBe(0);
1616
expect(stderr).toMatch(/show|config/i);
1717
});
1818

1919
test("config set --help 正常退出", async () => {
20-
const { stderr, exitCode } = await runCommandE2e(CONFIG_ROUTES, ["config", "set", "--help"]);
20+
const { stderr, exitCode } = await runCommandHelp(CONFIG_ROUTES, ["config", "set", "--help"]);
2121
expect(exitCode, stderr).toBe(0);
2222
expect(stderr).toMatch(/set|--key|--value/i);
2323
});
2424

2525
test("config list/use --help 正常退出", async () => {
26-
const listResult = await runCommandE2e(CONFIG_ROUTES, ["config", "list", "--help"]);
26+
const listResult = await runCommandHelp(CONFIG_ROUTES, ["config", "list", "--help"]);
2727
expect(listResult.exitCode, listResult.stderr).toBe(0);
2828
expect(listResult.stderr).toMatch(/list|active|profile/i);
2929

30-
const useResult = await runCommandE2e(CONFIG_ROUTES, ["config", "use", "--help"]);
30+
const useResult = await runCommandHelp(CONFIG_ROUTES, ["config", "use", "--help"]);
3131
expect(useResult.exitCode, useResult.stderr).toBe(0);
3232
expect(useResult.stderr).toMatch(/use|--name|active/i);
3333
});
3434

3535
test("config ui --help 正常退出", async () => {
36-
const { stderr, exitCode } = await runCommandE2e(CONFIG_ROUTES, ["config", "ui", "--help"]);
36+
const { stderr, exitCode } = await runCommandHelp(CONFIG_ROUTES, ["config", "ui", "--help"]);
3737
expect(exitCode, stderr).toBe(0);
3838
expect(stderr).toMatch(/ui|--port|--no-open|web/i);
3939
});
@@ -464,7 +464,7 @@ describe("e2e: config", () => {
464464
});
465465

466466
test("config agent --help 正常退出", async () => {
467-
const { stderr, exitCode } = await runCommandE2e(CONFIG_ROUTES, ["config", "agent", "--help"]);
467+
const { stderr, exitCode } = await runCommandHelp(CONFIG_ROUTES, ["config", "agent", "--help"]);
468468
expect(exitCode, stderr).toBe(0);
469469
expect(stderr).toMatch(/agent|--base-url|--model/i);
470470
});

packages/commands/tests/e2e/console-flags-dry-run.e2e.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, test } from "vite-plus/test";
2-
import { parseStdoutJson, runCommandE2e } from "./helpers.ts";
2+
import { parseStdoutJson, runCommandHelp, runCommandE2e } from "./helpers.ts";
33
import { CONSOLE_FLAGS_DRY_RUN_ROUTES } from "./topic-routes.ts";
44

55
type ConsoleDryRunMeta = {
@@ -10,7 +10,7 @@ type ConsoleDryRunMeta = {
1010

1111
describe("e2e: console global flags (dry-run)", () => {
1212
test("console call --help 不暴露命令级 region/site,示例使用 --console-region", async () => {
13-
const { stderr, exitCode } = await runCommandE2e(CONSOLE_FLAGS_DRY_RUN_ROUTES, [
13+
const { stderr, exitCode } = await runCommandHelp(CONSOLE_FLAGS_DRY_RUN_ROUTES, [
1414
"console",
1515
"call",
1616
"--help",
@@ -24,7 +24,7 @@ describe("e2e: console global flags (dry-run)", () => {
2424
});
2525

2626
test("auth login --help:自有 flag 含 --console-site,不含其余 console 域 flag", async () => {
27-
const { stderr, exitCode } = await runCommandE2e(CONSOLE_FLAGS_DRY_RUN_ROUTES, [
27+
const { stderr, exitCode } = await runCommandHelp(CONSOLE_FLAGS_DRY_RUN_ROUTES, [
2828
"auth",
2929
"login",
3030
"--help",

packages/commands/tests/e2e/dataset.e2e.test.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { describe, expect, test } from "vite-plus/test";
22
import { join } from "path";
3-
import { isDashScopeE2EReady, parseStdoutJson, runCommandE2e, e2eFixturesDir } from "./helpers.ts";
3+
import {
4+
isDashScopeE2EReady,
5+
parseStdoutJson,
6+
runCommandHelp,
7+
runCommandE2e,
8+
e2eFixturesDir,
9+
} from "./helpers.ts";
410
import { DATASET_ROUTES } from "./topic-routes.ts";
511

612
/**
@@ -18,7 +24,7 @@ import { DATASET_ROUTES } from "./topic-routes.ts";
1824

1925
describe.skipIf(!isDashScopeE2EReady())("e2e: dataset (offline)", () => {
2026
test("dataset upload --help 正常退出并展示 --file", async () => {
21-
const { stderr, exitCode } = await runCommandE2e(DATASET_ROUTES, [
27+
const { stderr, exitCode } = await runCommandHelp(DATASET_ROUTES, [
2228
"dataset",
2329
"upload",
2430
"--help",

packages/commands/tests/e2e/deploy.e2e.test.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, test } from "vite-plus/test";
2-
import { isDashScopeE2EReady, parseStdoutJson, runCommandE2e } from "./helpers.ts";
2+
import { isDashScopeE2EReady, parseStdoutJson, runCommandHelp, runCommandE2e } from "./helpers.ts";
33
import { DEPLOY_ROUTES } from "./topic-routes.ts";
44

55
/**
@@ -16,14 +16,13 @@ import { DEPLOY_ROUTES } from "./topic-routes.ts";
1616

1717
describe.skipIf(!isDashScopeE2EReady())("e2e: deploy (offline)", () => {
1818
test("deploy 列出子命令", async () => {
19-
const { stdout, stderr, exitCode } = await runCommandE2e(DEPLOY_ROUTES, ["deploy"]);
19+
const { stderr, exitCode } = await runCommandHelp(DEPLOY_ROUTES, ["deploy", "--help"]);
2020
expect(exitCode, stderr).toBe(0);
21-
const out = `${stdout}\n${stderr}`;
22-
expect(out).toMatch(/create|list|get|delete|update|scale|models/);
21+
expect(stderr).toMatch(/create|list|get|delete|update|scale|models/);
2322
});
2423

2524
test("deploy create --help 正常退出并展示必填项", async () => {
26-
const { stderr, exitCode } = await runCommandE2e(DEPLOY_ROUTES, [
25+
const { stderr, exitCode } = await runCommandHelp(DEPLOY_ROUTES, [
2726
"deploy",
2827
"text",
2928
"create",

packages/commands/tests/e2e/file-upload.e2e.test.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { describe, expect, test } from "vite-plus/test";
22
import { join } from "path";
3-
import { isDashScopeE2EReady, parseStdoutJson, runCommandE2e, e2eFixturesDir } from "./helpers.ts";
3+
import {
4+
isDashScopeE2EReady,
5+
parseStdoutJson,
6+
runCommandHelp,
7+
runCommandE2e,
8+
e2eFixturesDir,
9+
} from "./helpers.ts";
410
import { FILE_UPLOAD_ROUTES } from "./topic-routes.ts";
511

612
/**
@@ -9,7 +15,7 @@ import { FILE_UPLOAD_ROUTES } from "./topic-routes.ts";
915

1016
describe("e2e: file upload", () => {
1117
test("file upload --help 正常退出", async () => {
12-
const { stderr, exitCode } = await runCommandE2e(FILE_UPLOAD_ROUTES, [
18+
const { stderr, exitCode } = await runCommandHelp(FILE_UPLOAD_ROUTES, [
1319
"file",
1420
"upload",
1521
"--help",

packages/commands/tests/e2e/finetune.e2e.test.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { describe, expect, test } from "vite-plus/test";
22
import { join } from "path";
3-
import { isDashScopeE2EReady, parseStdoutJson, runCommandE2e, e2eFixturesDir } from "./helpers.ts";
3+
import {
4+
isDashScopeE2EReady,
5+
parseStdoutJson,
6+
runCommandHelp,
7+
runCommandE2e,
8+
e2eFixturesDir,
9+
} from "./helpers.ts";
410
import { FINETUNE_ROUTES } from "./topic-routes.ts";
511

612
/**
@@ -17,14 +23,15 @@ import { FINETUNE_ROUTES } from "./topic-routes.ts";
1723

1824
describe.skipIf(!isDashScopeE2EReady())("e2e: finetune (offline)", () => {
1925
test("finetune 列出子命令", async () => {
20-
const { stdout, stderr, exitCode } = await runCommandE2e(FINETUNE_ROUTES, ["finetune"]);
26+
const { stderr, exitCode } = await runCommandHelp(FINETUNE_ROUTES, ["finetune", "--help"]);
2127
expect(exitCode, stderr).toBe(0);
22-
const out = `${stdout}\n${stderr}`;
23-
expect(out).toMatch(/create|list|get|cancel|delete|logs|checkpoints|export|watch|capability/);
28+
expect(stderr).toMatch(
29+
/create|list|get|cancel|delete|logs|checkpoints|export|watch|capability/,
30+
);
2431
});
2532

2633
test("finetune create --help 正常退出并展示必填项", async () => {
27-
const { stderr, exitCode } = await runCommandE2e(FINETUNE_ROUTES, [
34+
const { stderr, exitCode } = await runCommandHelp(FINETUNE_ROUTES, [
2835
"finetune",
2936
"text",
3037
"create",
@@ -336,7 +343,7 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: finetune (offline)", () => {
336343
test("finetune audio create --help 不暴露文本超参 flag", async () => {
337344
// Audio models don't consume --training-type / --n-epochs / --batch-size /
338345
// --learning-rate / --max-length, so those flags are not offered.
339-
const { stderr, exitCode } = await runCommandE2e(FINETUNE_ROUTES, [
346+
const { stderr, exitCode } = await runCommandHelp(FINETUNE_ROUTES, [
340347
"finetune",
341348
"audio",
342349
"create",
@@ -369,7 +376,7 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: finetune (offline)", () => {
369376
test("finetune video create --help 暴露视频超参且不含文本超参", async () => {
370377
// Video exposes --n-epochs / --batch-size / --learning-rate; the text-only
371378
// --training-type / --max-length surface is not offered.
372-
const { stderr, exitCode } = await runCommandE2e(FINETUNE_ROUTES, [
379+
const { stderr, exitCode } = await runCommandHelp(FINETUNE_ROUTES, [
373380
"finetune",
374381
"video",
375382
"create",

0 commit comments

Comments
 (0)