From 98ebeb398c19d534b1d4b681b640daf4d79f6767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=A5=E9=BA=92?= Date: Fri, 28 Aug 2026 16:54:55 +0800 Subject: [PATCH 1/2] perf(test): run registry smoke help checks in process --- docs/agents/cli-e2e-tests.md | 20 ++++---- .../cli/tests/e2e/registry.smoke.e2e.test.ts | 48 ++++++++++++++----- packages/e2e/src/registry-smoke.ts | 22 ++++++++- packages/e2e/tests/registry-smoke.test.ts | 17 +++++++ .../tests/e2e/registry.smoke.e2e.test.ts | 28 ++++++++--- 5 files changed, 105 insertions(+), 30 deletions(-) create mode 100644 packages/e2e/tests/registry-smoke.test.ts diff --git a/docs/agents/cli-e2e-tests.md b/docs/agents/cli-e2e-tests.md index 5449e1ab0..aa86c6c4c 100644 --- a/docs/agents/cli-e2e-tests.md +++ b/docs/agents/cli-e2e-tests.md @@ -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 契约 ### 共享 diff --git a/packages/cli/tests/e2e/registry.smoke.e2e.test.ts b/packages/cli/tests/e2e/registry.smoke.e2e.test.ts index aa87d00f9..b94b9eecf 100644 --- a/packages/cli/tests/e2e/registry.smoke.e2e.test.ts +++ b/packages/cli/tests/e2e/registry.smoke.e2e.test.ts @@ -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 /); @@ -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} [flags]`, + ); }); }); diff --git a/packages/e2e/src/registry-smoke.ts b/packages/e2e/src/registry-smoke.ts index bed14cc7e..6b91aa79f 100644 --- a/packages/e2e/src/registry-smoke.ts +++ b/packages/e2e/src/registry-smoke.ts @@ -6,8 +6,8 @@ export function deriveGroupPaths(commandPaths: string[]): string[] { const groups = new Set(); 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; +} diff --git a/packages/e2e/tests/registry-smoke.test.ts b/packages/e2e/tests/registry-smoke.test.ts new file mode 100644 index 000000000..4209ac250 --- /dev/null +++ b/packages/e2e/tests/registry-smoke.test.ts @@ -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"]]); +}); diff --git a/packages/kscli/tests/e2e/registry.smoke.e2e.test.ts b/packages/kscli/tests/e2e/registry.smoke.e2e.test.ts index 892aa1f97..8a0f8080d 100644 --- a/packages/kscli/tests/e2e/registry.smoke.e2e.test.ts +++ b/packages/kscli/tests/e2e/registry.smoke.e2e.test.ts @@ -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} [flags]`, + ); }); }); From 59250c4f44a346b09feceb446ee3f3cbcbbe9b97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=A5=E9=BA=92?= Date: Fri, 28 Aug 2026 19:14:06 +0800 Subject: [PATCH 2/2] perf(test): streamline command e2e execution --- docs/agents/cli-e2e-tests.md | 18 ++++--- .../tests/e2e/advisor-recommend.e2e.test.ts | 4 +- packages/commands/tests/e2e/auth.e2e.test.ts | 7 +-- .../commands/tests/e2e/command-help.test.ts | 20 ++++++++ .../commands/tests/e2e/config.e2e.test.ts | 14 +++--- .../e2e/console-flags-dry-run.e2e.test.ts | 6 +-- .../commands/tests/e2e/dataset.e2e.test.ts | 10 +++- .../commands/tests/e2e/deploy.e2e.test.ts | 9 ++-- .../tests/e2e/file-upload.e2e.test.ts | 10 +++- .../commands/tests/e2e/finetune.e2e.test.ts | 21 +++++--- packages/commands/tests/e2e/helpers.ts | 49 +++++++++++++++++++ .../commands/tests/e2e/image-edit.e2e.test.ts | 3 +- .../tests/e2e/image-generate.e2e.test.ts | 7 ++- .../e2e/knowledge/knowledge-chat.e2e.test.ts | 3 +- .../knowledge-chunk-category-file.e2e.test.ts | 5 +- .../knowledge-doc-delete.e2e.test.ts | 4 +- .../knowledge/knowledge-doc-list.e2e.test.ts | 4 +- .../knowledge-doc-status.e2e.test.ts | 4 +- .../knowledge/knowledge-doc-tag.e2e.test.ts | 4 +- .../knowledge-doc-upload.e2e.test.ts | 4 +- .../knowledge/knowledge-kb-create.e2e.test.ts | 4 +- .../knowledge/knowledge-kb-delete.e2e.test.ts | 4 +- .../knowledge/knowledge-kb-info.e2e.test.ts | 4 +- .../knowledge/knowledge-kb-list.e2e.test.ts | 4 +- .../knowledge/knowledge-kb-update.e2e.test.ts | 4 +- .../knowledge/knowledge-search.e2e.test.ts | 3 +- .../knowledge/knowledge-service.e2e.test.ts | 4 +- .../tests/e2e/knowledge/knowledge.e2e.test.ts | 3 +- .../tests/e2e/managed-agent.e2e.test.ts | 6 +-- packages/commands/tests/e2e/mcp.e2e.test.ts | 10 ++-- .../commands/tests/e2e/memory.e2e.test.ts | 13 +++-- packages/commands/tests/e2e/omni.e2e.test.ts | 3 +- .../commands/tests/e2e/permission.e2e.test.ts | 8 +-- .../commands/tests/e2e/pipeline.e2e.test.ts | 6 +-- packages/commands/tests/e2e/quota.e2e.test.ts | 13 ++--- .../commands/tests/e2e/search-web.e2e.test.ts | 4 +- packages/commands/tests/e2e/skill.e2e.test.ts | 12 ++--- .../tests/e2e/speech-list-voices.e2e.test.ts | 6 +-- .../tests/e2e/speech-recognize.e2e.test.ts | 3 +- .../tests/e2e/speech-synthesize.e2e.test.ts | 3 +- .../commands/tests/e2e/text-chat.e2e.test.ts | 4 +- .../commands/tests/e2e/token-plan.e2e.test.ts | 4 +- .../commands/tests/e2e/update.e2e.test.ts | 6 +-- .../tests/e2e/usage-coding-plan.e2e.test.ts | 5 +- .../commands/tests/e2e/usage-free.e2e.test.ts | 5 +- .../tests/e2e/usage-stats.e2e.test.ts | 7 +-- .../tests/e2e/usage-token-plan.e2e.test.ts | 5 +- .../tests/e2e/video-download.e2e.test.ts | 7 ++- .../commands/tests/e2e/video-edit.e2e.test.ts | 3 +- .../tests/e2e/video-generate-i2v.e2e.test.ts | 7 ++- .../tests/e2e/video-generate-t2v.e2e.test.ts | 7 ++- .../tests/e2e/video-ref-r2v.e2e.test.ts | 3 +- .../tests/e2e/video-task-get.e2e.test.ts | 3 +- packages/e2e/src/run-subprocess.ts | 27 +++++----- packages/e2e/tests/fixtures/echo-argv.ts | 17 +++++++ packages/e2e/tests/run-subprocess.test.ts | 37 ++++++++++++++ 56 files changed, 331 insertions(+), 139 deletions(-) create mode 100644 packages/commands/tests/e2e/command-help.test.ts create mode 100644 packages/e2e/tests/fixtures/echo-argv.ts create mode 100644 packages/e2e/tests/run-subprocess.test.ts diff --git a/docs/agents/cli-e2e-tests.md b/docs/agents/cli-e2e-tests.md index aa86c6c4c..c58f2c1b8 100644 --- a/docs/agents/cli-e2e-tests.md +++ b/docs/agents/cli-e2e-tests.md @@ -5,7 +5,7 @@ | 层级 | 路径 | 测什么 | | --------------- | ----------------------------------------------------- | --------------------------------------------------------------------------------------- | | **共享基建** | `packages/e2e` | gating、子进程 runner、registry help 捕获、output、globalSetup(`private`,不发布) | -| **命令 E2E** | `packages/commands/tests/e2e` | help、缺参、dry-run、live(gated);每用例最小路由 | +| **命令 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` 等代表性子进程冒烟 | @@ -29,7 +29,8 @@ ### commands E2E - 路径:`packages/commands/tests/e2e/.e2e.test.ts`;knowledge 领域集中在 `packages/commands/tests/e2e/knowledge/` 子目录(新增 knowledge 命令测试放这里) -- 子进程:`runCommandE2e(routes, args)` from `./helpers.ts`(spawn `harness/main.ts`,`routes` 为本 topic 最小 path → export 映射) +- help:`runCommandHelp(routes, [...path, "--help"])` from `./helpers.ts`(当前 Vitest worker 内用真实 command + `CommandRegistry` 渲染,不启动子进程) +- 子进程:缺参、dry-run、live 使用 `runCommandE2e(routes, args)`(spawn `harness/main.ts`,`routes` 为本 topic 最小 path → export 映射) - fixtures:`packages/commands/tests/e2e/fixtures/` - 路由常量:`topic-routes.ts`(按 topic 维护,**非**全量产品 map) @@ -42,7 +43,7 @@ ### 共享 -- gating / output / runner:`e2e/gating`、`e2e/output`、`e2e/runner` +- gating / output / runner:`e2e/gating`、`e2e/output`、`e2e/runner`;runner 使用 `node --import tsx` 执行 TypeScript 入口,不启动 tsx CLI IPC server - globalSetup:根 `vite.config.ts` → `packages/e2e/src/global-setup.ts` - 解析 JSON stdout:`parseStdoutJson`;输出目录:`makeE2eOutputDir(e2eLabelFromMetaUrl(import.meta.url))` - 长任务:`cliTimeoutPrefix()`;视频用例加 `test(..., 3_600_000)` 等显式超时 @@ -50,9 +51,12 @@ ## 双层 describe(固定结构) ```ts -// 1) 不 skip:--help,无密钥、无真实 API(分组 help 由 bl registry.smoke 覆盖) +// 1) 不 skip:进程内 --help,无密钥、无真实 API(分组 help 由 bl registry.smoke 覆盖) describe("e2e: ", () => { - test(" --help 正常退出", ...); + test(" --help 正常退出", async () => { + const result = await runCommandHelp(FOO_ROUTES, ["foo", "bar", "--help"]); + expect(result.exitCode, result.stderr).toBe(0); + }); }); // 2) skipIf:缺参 / dry-run / 真实集成 @@ -76,7 +80,7 @@ describe.skipIf()("e2e: (DashScope …)", () => { ## 用例类型 -1. **--help**:`runCommandE2e(ROUTES, [..., "--help"])` → stderr 含主要 flags +1. **--help**:`runCommandHelp(ROUTES, [..., "--help"])` → stderr 含主要 flags;产品层另保留少量真实子进程 help 验证 shell/stdio/env 2. **缺参**:带无害全局 flag(如 `--quiet`)且不传 required flag → `exitCode === 2` 3. **--dry-run**:实现在联网/上传/写盘**之前**返回;断言 stdout JSON/文本 4. **真实集成**:放在 skip 块**末尾** @@ -107,7 +111,7 @@ describe.skipIf()("e2e: (DashScope …)", () => { - [ ] `packages/commands/src/index.ts` 导出 + `packages/cli/src/commands.ts` 暴露路径 + `topic-routes.ts` 补最小路由 - [ ] `packages/commands/tests/e2e/.e2e.test.ts`(新建或扩展) - [ ] 若改了 `usageArgs` / `flags` / `exampleArgs`,跑 `pnpm --filter bailian-cli run generate:reference` 更新各 `skills//reference/` 并提交 -- [ ] 子命令 `--help`(分组 help 由 bl `registry.smoke` 覆盖) +- [ ] 子命令 `--help` 使用 `runCommandHelp`(分组 help 由 bl `registry.smoke` 覆盖) - [ ] skip 块:每个 required flag 缺参;可 dry-run 则加一条 - [ ] 至少一条真实集成(或说明为何仅 smoke);不破坏已有集成用例顺序 - [ ] `vp test packages/commands/tests/e2e/` 通过 diff --git a/packages/commands/tests/e2e/advisor-recommend.e2e.test.ts b/packages/commands/tests/e2e/advisor-recommend.e2e.test.ts index 7f2287092..a14164d7f 100644 --- a/packages/commands/tests/e2e/advisor-recommend.e2e.test.ts +++ b/packages/commands/tests/e2e/advisor-recommend.e2e.test.ts @@ -1,10 +1,10 @@ import { describe, expect, test } from "vite-plus/test"; -import { isDashScopeE2EReady, parseStdoutJson, runCommandE2e } from "./helpers.ts"; +import { isDashScopeE2EReady, parseStdoutJson, runCommandHelp, runCommandE2e } from "./helpers.ts"; import { ADVISOR_ROUTES } from "./topic-routes.ts"; describe("e2e: advisor recommend", () => { test("advisor recommend --help exits successfully", async () => { - const { stderr, exitCode } = await runCommandE2e(ADVISOR_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(ADVISOR_ROUTES, [ "advisor", "recommend", "--help", diff --git a/packages/commands/tests/e2e/auth.e2e.test.ts b/packages/commands/tests/e2e/auth.e2e.test.ts index 0d3348297..fcaee69ce 100644 --- a/packages/commands/tests/e2e/auth.e2e.test.ts +++ b/packages/commands/tests/e2e/auth.e2e.test.ts @@ -7,6 +7,7 @@ import { isOpenApiE2EReady, makeE2eOutputDir, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { AUTH_ROUTES } from "./topic-routes.ts"; @@ -15,7 +16,7 @@ import { AUTH_ROUTES } from "./topic-routes.ts"; describe("e2e: auth", () => { test("auth login --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(AUTH_ROUTES, ["auth", "login", "--help"]); + const { stderr, exitCode } = await runCommandHelp(AUTH_ROUTES, ["auth", "login", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/login|api-key/i); expect(stderr).toMatch(/--console-site/); @@ -108,13 +109,13 @@ describe("e2e: auth", () => { }); test("auth logout --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(AUTH_ROUTES, ["auth", "logout", "--help"]); + const { stderr, exitCode } = await runCommandHelp(AUTH_ROUTES, ["auth", "logout", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/logout|dry-run|yes/i); }); test("auth status --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(AUTH_ROUTES, ["auth", "status", "--help"]); + const { stderr, exitCode } = await runCommandHelp(AUTH_ROUTES, ["auth", "status", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/status|output/i); }); diff --git a/packages/commands/tests/e2e/command-help.test.ts b/packages/commands/tests/e2e/command-help.test.ts new file mode 100644 index 000000000..e15de97fa --- /dev/null +++ b/packages/commands/tests/e2e/command-help.test.ts @@ -0,0 +1,20 @@ +import { expect, test } from "vite-plus/test"; +import { captureCommandHelp, runCommandHelp } from "./helpers.ts"; +import { QUOTA_ROUTES } from "./topic-routes.ts"; + +test("captureCommandHelp 用 topic 最小路由渲染真实命令帮助", async () => { + const helpOutput = await captureCommandHelp(QUOTA_ROUTES, ["quota", "list"]); + + expect(helpOutput).toContain("Usage: bl quota list"); + expect(helpOutput).toContain("--model "); + expect(helpOutput).toContain("--name "); + expect(helpOutput).toContain("--output "); +}); + +test("runCommandHelp 保持 commands E2E 的 stderr 与 exitCode 断言接口", async () => { + const result = await runCommandHelp(QUOTA_ROUTES, ["quota", "list", "--help"]); + + expect(result.exitCode).toBe(0); + expect(result.stdout).toBe(""); + expect(result.stderr).toContain("Usage: bl quota list"); +}); diff --git a/packages/commands/tests/e2e/config.e2e.test.ts b/packages/commands/tests/e2e/config.e2e.test.ts index cbf740265..173b6b84f 100644 --- a/packages/commands/tests/e2e/config.e2e.test.ts +++ b/packages/commands/tests/e2e/config.e2e.test.ts @@ -2,7 +2,7 @@ import { mkdtempSync, readFileSync, rmSync, writeFileSync, existsSync } from "fs import { tmpdir } from "os"; import { join } from "path"; import { describe, expect, test } from "vite-plus/test"; -import { parseStdoutJson, runCommandE2e } from "./helpers.ts"; +import { parseStdoutJson, runCommandHelp, runCommandE2e } from "./helpers.ts"; import { CONFIG_ROUTES } from "./topic-routes.ts"; /** @@ -11,29 +11,29 @@ import { CONFIG_ROUTES } from "./topic-routes.ts"; describe("e2e: config", () => { test("config show --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(CONFIG_ROUTES, ["config", "show", "--help"]); + const { stderr, exitCode } = await runCommandHelp(CONFIG_ROUTES, ["config", "show", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/show|config/i); }); test("config set --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(CONFIG_ROUTES, ["config", "set", "--help"]); + const { stderr, exitCode } = await runCommandHelp(CONFIG_ROUTES, ["config", "set", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/set|--key|--value/i); }); test("config list/use --help 正常退出", async () => { - const listResult = await runCommandE2e(CONFIG_ROUTES, ["config", "list", "--help"]); + const listResult = await runCommandHelp(CONFIG_ROUTES, ["config", "list", "--help"]); expect(listResult.exitCode, listResult.stderr).toBe(0); expect(listResult.stderr).toMatch(/list|active|profile/i); - const useResult = await runCommandE2e(CONFIG_ROUTES, ["config", "use", "--help"]); + const useResult = await runCommandHelp(CONFIG_ROUTES, ["config", "use", "--help"]); expect(useResult.exitCode, useResult.stderr).toBe(0); expect(useResult.stderr).toMatch(/use|--name|active/i); }); test("config ui --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(CONFIG_ROUTES, ["config", "ui", "--help"]); + const { stderr, exitCode } = await runCommandHelp(CONFIG_ROUTES, ["config", "ui", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/ui|--port|--no-open|web/i); }); @@ -464,7 +464,7 @@ describe("e2e: config", () => { }); test("config agent --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(CONFIG_ROUTES, ["config", "agent", "--help"]); + const { stderr, exitCode } = await runCommandHelp(CONFIG_ROUTES, ["config", "agent", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/agent|--base-url|--model/i); }); diff --git a/packages/commands/tests/e2e/console-flags-dry-run.e2e.test.ts b/packages/commands/tests/e2e/console-flags-dry-run.e2e.test.ts index 4fafea54d..71e83b508 100644 --- a/packages/commands/tests/e2e/console-flags-dry-run.e2e.test.ts +++ b/packages/commands/tests/e2e/console-flags-dry-run.e2e.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "vite-plus/test"; -import { parseStdoutJson, runCommandE2e } from "./helpers.ts"; +import { parseStdoutJson, runCommandHelp, runCommandE2e } from "./helpers.ts"; import { CONSOLE_FLAGS_DRY_RUN_ROUTES } from "./topic-routes.ts"; type ConsoleDryRunMeta = { @@ -10,7 +10,7 @@ type ConsoleDryRunMeta = { describe("e2e: console global flags (dry-run)", () => { test("console call --help 不暴露命令级 region/site,示例使用 --console-region", async () => { - const { stderr, exitCode } = await runCommandE2e(CONSOLE_FLAGS_DRY_RUN_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(CONSOLE_FLAGS_DRY_RUN_ROUTES, [ "console", "call", "--help", @@ -24,7 +24,7 @@ describe("e2e: console global flags (dry-run)", () => { }); test("auth login --help:自有 flag 含 --console-site,不含其余 console 域 flag", async () => { - const { stderr, exitCode } = await runCommandE2e(CONSOLE_FLAGS_DRY_RUN_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(CONSOLE_FLAGS_DRY_RUN_ROUTES, [ "auth", "login", "--help", diff --git a/packages/commands/tests/e2e/dataset.e2e.test.ts b/packages/commands/tests/e2e/dataset.e2e.test.ts index 9ef4d26e3..0da6bb497 100644 --- a/packages/commands/tests/e2e/dataset.e2e.test.ts +++ b/packages/commands/tests/e2e/dataset.e2e.test.ts @@ -1,6 +1,12 @@ import { describe, expect, test } from "vite-plus/test"; import { join } from "path"; -import { isDashScopeE2EReady, parseStdoutJson, runCommandE2e, e2eFixturesDir } from "./helpers.ts"; +import { + isDashScopeE2EReady, + parseStdoutJson, + runCommandHelp, + runCommandE2e, + e2eFixturesDir, +} from "./helpers.ts"; import { DATASET_ROUTES } from "./topic-routes.ts"; /** @@ -18,7 +24,7 @@ import { DATASET_ROUTES } from "./topic-routes.ts"; describe.skipIf(!isDashScopeE2EReady())("e2e: dataset (offline)", () => { test("dataset upload --help 正常退出并展示 --file", async () => { - const { stderr, exitCode } = await runCommandE2e(DATASET_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(DATASET_ROUTES, [ "dataset", "upload", "--help", diff --git a/packages/commands/tests/e2e/deploy.e2e.test.ts b/packages/commands/tests/e2e/deploy.e2e.test.ts index 47d5b8148..0510c3c32 100644 --- a/packages/commands/tests/e2e/deploy.e2e.test.ts +++ b/packages/commands/tests/e2e/deploy.e2e.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "vite-plus/test"; -import { isDashScopeE2EReady, parseStdoutJson, runCommandE2e } from "./helpers.ts"; +import { isDashScopeE2EReady, parseStdoutJson, runCommandHelp, runCommandE2e } from "./helpers.ts"; import { DEPLOY_ROUTES } from "./topic-routes.ts"; /** @@ -16,14 +16,13 @@ import { DEPLOY_ROUTES } from "./topic-routes.ts"; describe.skipIf(!isDashScopeE2EReady())("e2e: deploy (offline)", () => { test("deploy 列出子命令", async () => { - const { stdout, stderr, exitCode } = await runCommandE2e(DEPLOY_ROUTES, ["deploy"]); + const { stderr, exitCode } = await runCommandHelp(DEPLOY_ROUTES, ["deploy", "--help"]); expect(exitCode, stderr).toBe(0); - const out = `${stdout}\n${stderr}`; - expect(out).toMatch(/create|list|get|delete|update|scale|models/); + expect(stderr).toMatch(/create|list|get|delete|update|scale|models/); }); test("deploy create --help 正常退出并展示必填项", async () => { - const { stderr, exitCode } = await runCommandE2e(DEPLOY_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(DEPLOY_ROUTES, [ "deploy", "text", "create", diff --git a/packages/commands/tests/e2e/file-upload.e2e.test.ts b/packages/commands/tests/e2e/file-upload.e2e.test.ts index 5b7912c1b..146c3c4c2 100644 --- a/packages/commands/tests/e2e/file-upload.e2e.test.ts +++ b/packages/commands/tests/e2e/file-upload.e2e.test.ts @@ -1,6 +1,12 @@ import { describe, expect, test } from "vite-plus/test"; import { join } from "path"; -import { isDashScopeE2EReady, parseStdoutJson, runCommandE2e, e2eFixturesDir } from "./helpers.ts"; +import { + isDashScopeE2EReady, + parseStdoutJson, + runCommandHelp, + runCommandE2e, + e2eFixturesDir, +} from "./helpers.ts"; import { FILE_UPLOAD_ROUTES } from "./topic-routes.ts"; /** @@ -9,7 +15,7 @@ import { FILE_UPLOAD_ROUTES } from "./topic-routes.ts"; describe("e2e: file upload", () => { test("file upload --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(FILE_UPLOAD_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(FILE_UPLOAD_ROUTES, [ "file", "upload", "--help", diff --git a/packages/commands/tests/e2e/finetune.e2e.test.ts b/packages/commands/tests/e2e/finetune.e2e.test.ts index 53ee29a6e..8e0ea7aa8 100644 --- a/packages/commands/tests/e2e/finetune.e2e.test.ts +++ b/packages/commands/tests/e2e/finetune.e2e.test.ts @@ -1,6 +1,12 @@ import { describe, expect, test } from "vite-plus/test"; import { join } from "path"; -import { isDashScopeE2EReady, parseStdoutJson, runCommandE2e, e2eFixturesDir } from "./helpers.ts"; +import { + isDashScopeE2EReady, + parseStdoutJson, + runCommandHelp, + runCommandE2e, + e2eFixturesDir, +} from "./helpers.ts"; import { FINETUNE_ROUTES } from "./topic-routes.ts"; /** @@ -17,14 +23,15 @@ import { FINETUNE_ROUTES } from "./topic-routes.ts"; describe.skipIf(!isDashScopeE2EReady())("e2e: finetune (offline)", () => { test("finetune 列出子命令", async () => { - const { stdout, stderr, exitCode } = await runCommandE2e(FINETUNE_ROUTES, ["finetune"]); + const { stderr, exitCode } = await runCommandHelp(FINETUNE_ROUTES, ["finetune", "--help"]); expect(exitCode, stderr).toBe(0); - const out = `${stdout}\n${stderr}`; - expect(out).toMatch(/create|list|get|cancel|delete|logs|checkpoints|export|watch|capability/); + expect(stderr).toMatch( + /create|list|get|cancel|delete|logs|checkpoints|export|watch|capability/, + ); }); test("finetune create --help 正常退出并展示必填项", async () => { - const { stderr, exitCode } = await runCommandE2e(FINETUNE_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(FINETUNE_ROUTES, [ "finetune", "text", "create", @@ -336,7 +343,7 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: finetune (offline)", () => { test("finetune audio create --help 不暴露文本超参 flag", async () => { // Audio models don't consume --training-type / --n-epochs / --batch-size / // --learning-rate / --max-length, so those flags are not offered. - const { stderr, exitCode } = await runCommandE2e(FINETUNE_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(FINETUNE_ROUTES, [ "finetune", "audio", "create", @@ -369,7 +376,7 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: finetune (offline)", () => { test("finetune video create --help 暴露视频超参且不含文本超参", async () => { // Video exposes --n-epochs / --batch-size / --learning-rate; the text-only // --training-type / --max-length surface is not offered. - const { stderr, exitCode } = await runCommandE2e(FINETUNE_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(FINETUNE_ROUTES, [ "finetune", "video", "create", diff --git a/packages/commands/tests/e2e/helpers.ts b/packages/commands/tests/e2e/helpers.ts index a81f08ee5..eef65fc81 100644 --- a/packages/commands/tests/e2e/helpers.ts +++ b/packages/commands/tests/e2e/helpers.ts @@ -8,7 +8,10 @@ import { makeE2eOutputDir, parseStdoutJson, } from "e2e/output"; +import { captureRegistryHelp } from "e2e/registry-smoke"; import { runNodeMain, type RunCliResult } from "e2e/runner"; +import type { AnyCommand } from "bailian-cli-core"; +import { CommandRegistry, resolve } from "bailian-cli-runtime"; import type { E2eRouteExports } from "./topic-routes.ts"; export { @@ -42,6 +45,7 @@ export { const e2eDir = dirname(fileURLToPath(import.meta.url)); const harnessMainTs = join(e2eDir, "harness", "main.ts"); +const registryCache = new WeakMap>(); /** `packages/commands` 根目录 */ export const commandsPackageRoot = join(e2eDir, "..", ".."); @@ -55,6 +59,51 @@ function serializeRoutes(routes: E2eRouteExports): string { ); } +function getCommandRegistry(routes: E2eRouteExports): Promise { + const cachedRegistry = registryCache.get(routes); + if (cachedRegistry) return cachedRegistry; + + const registryPromise = import("bailian-cli-commands").then((commandExports) => { + const commandLibrary = commandExports as Record; + const commands: Record = {}; + for (const [path, exportName] of Object.entries(routes)) { + const command = commandLibrary[exportName]; + if (typeof command !== "object" || command === null || !("run" in command)) { + throw new Error(`Unknown bailian-cli-commands export: ${exportName}`); + } + commands[path] = command as AnyCommand; + } + return new CommandRegistry(commands, "bl"); + }); + registryCache.set(routes, registryPromise); + return registryPromise; +} + +/** 使用 topic 最小路由在当前 Vitest worker 中渲染命令 help,不启动 CLI 子进程。 */ +export async function captureCommandHelp( + routes: E2eRouteExports, + commandPath: string[], +): Promise { + const registry = await getCommandRegistry(routes); + const resolution = resolve([...commandPath, "--help"], registry); + if (resolution.kind !== "help" || resolution.path.join(" ") !== commandPath.join(" ")) { + throw new Error(`Command help did not resolve: ${commandPath.join(" ")}`); + } + return captureRegistryHelp(registry, commandPath); +} + +/** 与 runCommandE2e 的结果结构兼容,但 help 在当前 Vitest worker 中渲染。 */ +export async function runCommandHelp( + routes: E2eRouteExports, + args: string[], +): Promise { + if (args.at(-1) !== "--help") { + throw new Error("runCommandHelp requires --help as the final argument"); + } + const stderr = await captureCommandHelp(routes, args.slice(0, -1)); + return { stdout: "", stderr, exitCode: 0 }; +} + /** * 通过 harness 子进程执行命令 E2E。 * `routes` 为本用例所需的最小 path → export 映射,不维护全量产品 map。 diff --git a/packages/commands/tests/e2e/image-edit.e2e.test.ts b/packages/commands/tests/e2e/image-edit.e2e.test.ts index c17ad64d6..f5942b51d 100644 --- a/packages/commands/tests/e2e/image-edit.e2e.test.ts +++ b/packages/commands/tests/e2e/image-edit.e2e.test.ts @@ -8,6 +8,7 @@ import { isDashScopeE2EReady, makeE2eOutputDir, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { IMAGE_ROUTES } from "./topic-routes.ts"; @@ -18,7 +19,7 @@ import { IMAGE_ROUTES } from "./topic-routes.ts"; describe("e2e: image edit", () => { test("image edit --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(IMAGE_ROUTES, ["image", "edit", "--help"]); + const { stderr, exitCode } = await runCommandHelp(IMAGE_ROUTES, ["image", "edit", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/edit|--image|--prompt|--async|--concurrent/i); }); diff --git a/packages/commands/tests/e2e/image-generate.e2e.test.ts b/packages/commands/tests/e2e/image-generate.e2e.test.ts index 3a347e8ac..cc85a7ede 100644 --- a/packages/commands/tests/e2e/image-generate.e2e.test.ts +++ b/packages/commands/tests/e2e/image-generate.e2e.test.ts @@ -7,6 +7,7 @@ import { isDashScopeE2EReady, makeE2eOutputDir, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { IMAGE_ROUTES } from "./topic-routes.ts"; @@ -19,7 +20,11 @@ import { IMAGE_ROUTES } from "./topic-routes.ts"; describe("e2e: image generate", () => { test("image generate --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(IMAGE_ROUTES, ["image", "generate", "--help"]); + const { stderr, exitCode } = await runCommandHelp(IMAGE_ROUTES, [ + "image", + "generate", + "--help", + ]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/generate|--prompt|--model/i); }); diff --git a/packages/commands/tests/e2e/knowledge/knowledge-chat.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-chat.e2e.test.ts index 9d09f7ada..55cf64dcb 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-chat.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-chat.e2e.test.ts @@ -3,6 +3,7 @@ import { isChatE2EReady, isMultimodalChatE2EReady, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "../helpers.ts"; import { KNOWLEDGE_CHAT_ROUTES } from "../topic-routes.ts"; @@ -31,7 +32,7 @@ interface DryRunBody { describe("e2e: knowledge chat", () => { test("knowledge chat --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_CHAT_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(KNOWLEDGE_CHAT_ROUTES, [ "knowledge", "chat", "--help", diff --git a/packages/commands/tests/e2e/knowledge/knowledge-chunk-category-file.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-chunk-category-file.e2e.test.ts index ef3e4a604..372fc2892 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-chunk-category-file.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-chunk-category-file.e2e.test.ts @@ -11,6 +11,7 @@ import { isOssImportE2EReady, isTableKbE2EReady, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "../helpers.ts"; import { @@ -47,7 +48,7 @@ const HELP_SMOKE_CASES: Array<[string[], RegExp]> = [ describe("e2e: chunk/category/file 全命令 --help 冒烟", () => { test("12 条命令 help 退出 0 且展示代表性 flag(chunk add 单独测)", async () => { for (const [commandPath, flagPattern] of HELP_SMOKE_CASES) { - const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_CHUNK_CATEGORY_FILE_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(KNOWLEDGE_CHUNK_CATEGORY_FILE_ROUTES, [ ...commandPath, "--help", ]); @@ -59,7 +60,7 @@ describe("e2e: chunk/category/file 全命令 --help 冒烟", () => { describe("e2e: knowledge chunk 组 (静态)", () => { test("chunk add: --help 展示双通道 flags", async () => { - const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_CHUNK_CATEGORY_FILE_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(KNOWLEDGE_CHUNK_CATEGORY_FILE_ROUTES, [ "knowledge", "chunk", "add", diff --git a/packages/commands/tests/e2e/knowledge/knowledge-doc-delete.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-doc-delete.e2e.test.ts index dba73e3b5..b8d9c680f 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-doc-delete.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-doc-delete.e2e.test.ts @@ -1,10 +1,10 @@ import { describe, expect, test } from "vite-plus/test"; -import { parseStdoutJson, runCommandE2e } from "../helpers.ts"; +import { parseStdoutJson, runCommandHelp, runCommandE2e } from "../helpers.ts"; import { KNOWLEDGE_DOC_DELETE_ROUTES } from "../topic-routes.ts"; describe("e2e: knowledge doc delete", () => { test("--help 展示 flags", async () => { - const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_DOC_DELETE_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(KNOWLEDGE_DOC_DELETE_ROUTES, [ "knowledge", "doc", "delete", diff --git a/packages/commands/tests/e2e/knowledge/knowledge-doc-list.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-doc-list.e2e.test.ts index 3fb530d85..785dd83f3 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-doc-list.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-doc-list.e2e.test.ts @@ -1,10 +1,10 @@ import { describe, expect, test } from "vite-plus/test"; -import { isKbAdminE2EReady, parseStdoutJson, runCommandE2e } from "../helpers.ts"; +import { isKbAdminE2EReady, parseStdoutJson, runCommandHelp, runCommandE2e } from "../helpers.ts"; import { KNOWLEDGE_DOC_LIST_ROUTES } from "../topic-routes.ts"; describe("e2e: knowledge doc list", () => { test("--help 展示 flags", async () => { - const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_DOC_LIST_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(KNOWLEDGE_DOC_LIST_ROUTES, [ "knowledge", "doc", "list", diff --git a/packages/commands/tests/e2e/knowledge/knowledge-doc-status.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-doc-status.e2e.test.ts index 6b98c4b6d..68c016a29 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-doc-status.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-doc-status.e2e.test.ts @@ -2,7 +2,7 @@ import { mkdtempSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, test } from "vite-plus/test"; -import { isKbAdminE2EReady, parseStdoutJson, runCommandE2e } from "../helpers.ts"; +import { isKbAdminE2EReady, parseStdoutJson, runCommandHelp, runCommandE2e } from "../helpers.ts"; import { KNOWLEDGE_DOC_STATUS_ROUTES } from "../topic-routes.ts"; // Live coverage depends on a real job_id produced by doc upload; it is exercised @@ -10,7 +10,7 @@ import { KNOWLEDGE_DOC_STATUS_ROUTES } from "../topic-routes.ts"; describe("e2e: knowledge doc status", () => { test("--help 展示 flags", async () => { - const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_DOC_STATUS_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(KNOWLEDGE_DOC_STATUS_ROUTES, [ "knowledge", "doc", "status", diff --git a/packages/commands/tests/e2e/knowledge/knowledge-doc-tag.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-doc-tag.e2e.test.ts index b3facd35c..4e13a421e 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-doc-tag.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-doc-tag.e2e.test.ts @@ -2,12 +2,12 @@ import { mkdtempSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, test } from "vite-plus/test"; -import { isKbAdminE2EReady, parseStdoutJson, runCommandE2e } from "../helpers.ts"; +import { isKbAdminE2EReady, parseStdoutJson, runCommandHelp, runCommandE2e } from "../helpers.ts"; import { KNOWLEDGE_DOC_TAG_ROUTES } from "../topic-routes.ts"; describe("e2e: knowledge doc tag", () => { test("--help 展示 flags", async () => { - const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_DOC_TAG_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(KNOWLEDGE_DOC_TAG_ROUTES, [ "knowledge", "doc", "tag", diff --git a/packages/commands/tests/e2e/knowledge/knowledge-doc-upload.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-doc-upload.e2e.test.ts index 8890575e2..ad05959d2 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-doc-upload.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-doc-upload.e2e.test.ts @@ -2,7 +2,7 @@ import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, test } from "vite-plus/test"; -import { isKbAdminE2EReady, parseStdoutJson, runCommandE2e } from "../helpers.ts"; +import { isKbAdminE2EReady, parseStdoutJson, runCommandHelp, runCommandE2e } from "../helpers.ts"; import { KNOWLEDGE_DOC_UPLOAD_ROUTES } from "../topic-routes.ts"; const fixtureDir = mkdtempSync(join(tmpdir(), "doc-upload-e2e-")); @@ -17,7 +17,7 @@ interface DryRunStep { describe("e2e: knowledge doc upload", () => { test("--help 展示 flags", async () => { - const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_DOC_UPLOAD_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(KNOWLEDGE_DOC_UPLOAD_ROUTES, [ "knowledge", "doc", "upload", diff --git a/packages/commands/tests/e2e/knowledge/knowledge-kb-create.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-kb-create.e2e.test.ts index a7dc33b9c..54341c978 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-kb-create.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-kb-create.e2e.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "vite-plus/test"; -import { parseStdoutJson, runCommandE2e } from "../helpers.ts"; +import { parseStdoutJson, runCommandHelp, runCommandE2e } from "../helpers.ts"; import { KNOWLEDGE_KB_CREATE_ROUTES } from "../topic-routes.ts"; interface DryRunBody { @@ -17,7 +17,7 @@ interface DryRunBody { describe("e2e: knowledge kb create", () => { test("--help 展示 flags", async () => { - const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_KB_CREATE_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(KNOWLEDGE_KB_CREATE_ROUTES, [ "knowledge", "create", "--help", diff --git a/packages/commands/tests/e2e/knowledge/knowledge-kb-delete.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-kb-delete.e2e.test.ts index 04629e751..64ac4ae95 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-kb-delete.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-kb-delete.e2e.test.ts @@ -4,13 +4,13 @@ import { mkdtempSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { basename, join } from "node:path"; import { describe, expect, test } from "vite-plus/test"; -import { isKbAdminE2EReady, parseStdoutJson, runCommandE2e } from "../helpers.ts"; +import { isKbAdminE2EReady, parseStdoutJson, runCommandHelp, runCommandE2e } from "../helpers.ts"; import { deleteKbWithRetry } from "./journeys/journey-helpers.ts"; import { KNOWLEDGE_KB_DELETE_ROUTES } from "../topic-routes.ts"; describe("e2e: knowledge kb delete", () => { test("--help 展示 flags", async () => { - const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_KB_DELETE_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(KNOWLEDGE_KB_DELETE_ROUTES, [ "knowledge", "delete", "--help", diff --git a/packages/commands/tests/e2e/knowledge/knowledge-kb-info.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-kb-info.e2e.test.ts index be0206bcc..41236cac5 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-kb-info.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-kb-info.e2e.test.ts @@ -1,10 +1,10 @@ import { describe, expect, test } from "vite-plus/test"; -import { isKbAdminE2EReady, parseStdoutJson, runCommandE2e } from "../helpers.ts"; +import { isKbAdminE2EReady, parseStdoutJson, runCommandHelp, runCommandE2e } from "../helpers.ts"; import { KNOWLEDGE_KB_INFO_ROUTES } from "../topic-routes.ts"; describe("e2e: knowledge kb info", () => { test("--help 展示 flags", async () => { - const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_KB_INFO_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(KNOWLEDGE_KB_INFO_ROUTES, [ "knowledge", "info", "--help", diff --git a/packages/commands/tests/e2e/knowledge/knowledge-kb-list.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-kb-list.e2e.test.ts index af1063bdf..2866dd8d3 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-kb-list.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-kb-list.e2e.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "vite-plus/test"; -import { isKbAdminE2EReady, parseStdoutJson, runCommandE2e } from "../helpers.ts"; +import { isKbAdminE2EReady, parseStdoutJson, runCommandHelp, runCommandE2e } from "../helpers.ts"; import { KNOWLEDGE_KB_LIST_ROUTES } from "../topic-routes.ts"; interface DryRunBody { @@ -9,7 +9,7 @@ interface DryRunBody { describe("e2e: knowledge kb list", () => { test("--help 展示 flags", async () => { - const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_KB_LIST_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(KNOWLEDGE_KB_LIST_ROUTES, [ "knowledge", "list", "--help", diff --git a/packages/commands/tests/e2e/knowledge/knowledge-kb-update.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-kb-update.e2e.test.ts index c528e55f6..eeb466276 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-kb-update.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-kb-update.e2e.test.ts @@ -1,10 +1,10 @@ import { describe, expect, test } from "vite-plus/test"; -import { parseStdoutJson, runCommandE2e } from "../helpers.ts"; +import { parseStdoutJson, runCommandHelp, runCommandE2e } from "../helpers.ts"; import { KNOWLEDGE_KB_UPDATE_ROUTES } from "../topic-routes.ts"; describe("e2e: knowledge kb update", () => { test("--help 展示 flags", async () => { - const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_KB_UPDATE_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(KNOWLEDGE_KB_UPDATE_ROUTES, [ "knowledge", "update", "--help", diff --git a/packages/commands/tests/e2e/knowledge/knowledge-search.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-search.e2e.test.ts index d2979aa89..5a84e22e6 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-search.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-search.e2e.test.ts @@ -4,6 +4,7 @@ import { isSearchE2EReady, isTableSearchE2EReady, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "../helpers.ts"; import { KNOWLEDGE_SEARCH_ROUTES } from "../topic-routes.ts"; @@ -20,7 +21,7 @@ interface DryRunBody { describe("e2e: knowledge search", () => { test("knowledge search --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_SEARCH_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(KNOWLEDGE_SEARCH_ROUTES, [ "knowledge", "search", "--help", diff --git a/packages/commands/tests/e2e/knowledge/knowledge-service.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge-service.e2e.test.ts index 5f60768cf..078ac21ed 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge-service.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge-service.e2e.test.ts @@ -5,7 +5,7 @@ import { mkdtempSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, test } from "vite-plus/test"; -import { isKbAdminE2EReady, parseStdoutJson, runCommandE2e } from "../helpers.ts"; +import { isKbAdminE2EReady, parseStdoutJson, runCommandHelp, runCommandE2e } from "../helpers.ts"; import { KNOWLEDGE_SERVICE_ROUTES } from "../topic-routes.ts"; import { pickDifferentAgentModel } from "./verified-models.ts"; @@ -16,7 +16,7 @@ interface DryRunBody { describe("e2e: knowledge service list", () => { test("--help 展示 flags", async () => { - const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(KNOWLEDGE_SERVICE_ROUTES, [ "knowledge", "service", "list", diff --git a/packages/commands/tests/e2e/knowledge/knowledge.e2e.test.ts b/packages/commands/tests/e2e/knowledge/knowledge.e2e.test.ts index 682d5267a..949c6957c 100644 --- a/packages/commands/tests/e2e/knowledge/knowledge.e2e.test.ts +++ b/packages/commands/tests/e2e/knowledge/knowledge.e2e.test.ts @@ -3,6 +3,7 @@ import { isDashScopeE2EReady, isKbAdminE2EReady, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "../helpers.ts"; import { KNOWLEDGE_ROUTES } from "../topic-routes.ts"; @@ -28,7 +29,7 @@ interface DryRunBody { describe("e2e: knowledge retrieve", () => { test("knowledge retrieve --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(KNOWLEDGE_ROUTES, [ "knowledge", "retrieve", "--help", diff --git a/packages/commands/tests/e2e/managed-agent.e2e.test.ts b/packages/commands/tests/e2e/managed-agent.e2e.test.ts index fc6b05881..03cba43d9 100644 --- a/packages/commands/tests/e2e/managed-agent.e2e.test.ts +++ b/packages/commands/tests/e2e/managed-agent.e2e.test.ts @@ -1,6 +1,6 @@ import { join } from "node:path"; import { describe, expect, test } from "vite-plus/test"; -import { e2eFixturesDir, parseStdoutJson, runCommandE2e } from "./helpers.ts"; +import { e2eFixturesDir, parseStdoutJson, runCommandHelp, runCommandE2e } from "./helpers.ts"; import { MANAGED_AGENT_ROUTES } from "./topic-routes.ts"; const AGENTS_DEPLOYMENT_YAML = join(e2eFixturesDir, "managed-agent", "agents-deployment.yaml"); @@ -123,7 +123,7 @@ describe("e2e: managed-agent", () => { }); test("managed-agent apply --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(MANAGED_AGENT_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(MANAGED_AGENT_ROUTES, [ "managed-agent", "apply", "--help", @@ -157,7 +157,7 @@ describe("e2e: managed-agent", () => { }); test("managed-agent skill-list --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(MANAGED_AGENT_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(MANAGED_AGENT_ROUTES, [ "managed-agent", "skill-list", "--help", diff --git a/packages/commands/tests/e2e/mcp.e2e.test.ts b/packages/commands/tests/e2e/mcp.e2e.test.ts index ad5d06e1c..bcf0d3d21 100644 --- a/packages/commands/tests/e2e/mcp.e2e.test.ts +++ b/packages/commands/tests/e2e/mcp.e2e.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "vite-plus/test"; -import { isDashScopeE2EReady, parseStdoutJson, runCommandE2e } from "./helpers.ts"; +import { isDashScopeE2EReady, parseStdoutJson, runCommandHelp, runCommandE2e } from "./helpers.ts"; import { MCP_ROUTES } from "./topic-routes.ts"; /** @@ -18,25 +18,25 @@ import { MCP_ROUTES } from "./topic-routes.ts"; describe("e2e: mcp", () => { test("mcp list --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(MCP_ROUTES, ["mcp", "list", "--help"]); + const { stderr, exitCode } = await runCommandHelp(MCP_ROUTES, ["mcp", "list", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/list|--name|--type|--page/i); }); test("mcp tools --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(MCP_ROUTES, ["mcp", "tools", "--help"]); + const { stderr, exitCode } = await runCommandHelp(MCP_ROUTES, ["mcp", "tools", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/tools|--server|--url/i); }); test("mcp call --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(MCP_ROUTES, ["mcp", "call", "--help"]); + const { stderr, exitCode } = await runCommandHelp(MCP_ROUTES, ["mcp", "call", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/call|--target|--arg|--json/i); }); test("mcp list --help 不暴露 --all 入口(市场全量已下线)", async () => { - const { stderr, exitCode } = await runCommandE2e(MCP_ROUTES, ["mcp", "list", "--help"]); + const { stderr, exitCode } = await runCommandHelp(MCP_ROUTES, ["mcp", "list", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).not.toMatch(/--all/); }); diff --git a/packages/commands/tests/e2e/memory.e2e.test.ts b/packages/commands/tests/e2e/memory.e2e.test.ts index f81c6d77f..869b521b8 100644 --- a/packages/commands/tests/e2e/memory.e2e.test.ts +++ b/packages/commands/tests/e2e/memory.e2e.test.ts @@ -3,6 +3,7 @@ import { isBailianE2EEnabled, isDashScopeE2EReady, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { MEMORY_ROUTES } from "./topic-routes.ts"; @@ -38,25 +39,29 @@ function memoryLibraryCliArgs(): string[] { describe("e2e: memory", () => { test("memory add --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(MEMORY_ROUTES, ["memory", "add", "--help"]); + const { stderr, exitCode } = await runCommandHelp(MEMORY_ROUTES, ["memory", "add", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/add|--user-id|--content|messages/i); }); test("memory list --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(MEMORY_ROUTES, ["memory", "list", "--help"]); + const { stderr, exitCode } = await runCommandHelp(MEMORY_ROUTES, ["memory", "list", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/list|--user-id|memory-library/i); }); test("memory search --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(MEMORY_ROUTES, ["memory", "search", "--help"]); + const { stderr, exitCode } = await runCommandHelp(MEMORY_ROUTES, [ + "memory", + "search", + "--help", + ]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/search|--query|user-id/i); }); test("memory profile create --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(MEMORY_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(MEMORY_ROUTES, [ "memory", "profile", "create", diff --git a/packages/commands/tests/e2e/omni.e2e.test.ts b/packages/commands/tests/e2e/omni.e2e.test.ts index 93804d030..d49ede47e 100644 --- a/packages/commands/tests/e2e/omni.e2e.test.ts +++ b/packages/commands/tests/e2e/omni.e2e.test.ts @@ -6,13 +6,14 @@ import { isDashScopeE2EReady, makeE2eOutputDir, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { OMNI_ROUTES } from "./topic-routes.ts"; describe("e2e: omni", () => { test("omni --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(OMNI_ROUTES, ["omni", "--help"]); + const { stderr, exitCode } = await runCommandHelp(OMNI_ROUTES, ["omni", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/omni|--message|--audio|text-only/i); }); diff --git a/packages/commands/tests/e2e/permission.e2e.test.ts b/packages/commands/tests/e2e/permission.e2e.test.ts index f2d03e6bb..4159656e4 100644 --- a/packages/commands/tests/e2e/permission.e2e.test.ts +++ b/packages/commands/tests/e2e/permission.e2e.test.ts @@ -1,10 +1,10 @@ import { describe, expect, test } from "vite-plus/test"; -import { isDashScopeE2EReady, parseStdoutJson, runCommandE2e } from "./helpers.ts"; +import { isDashScopeE2EReady, parseStdoutJson, runCommandHelp, runCommandE2e } from "./helpers.ts"; import { PERMISSION_ROUTES } from "./topic-routes.ts"; describe("e2e: permission", () => { test("permission list --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(PERMISSION_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(PERMISSION_ROUTES, [ "permission", "list", "--help", @@ -17,7 +17,7 @@ describe("e2e: permission", () => { }); test("permission grant --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(PERMISSION_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(PERMISSION_ROUTES, [ "permission", "grant", "--help", @@ -29,7 +29,7 @@ describe("e2e: permission", () => { }); test("permission revoke --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(PERMISSION_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(PERMISSION_ROUTES, [ "permission", "revoke", "--help", diff --git a/packages/commands/tests/e2e/pipeline.e2e.test.ts b/packages/commands/tests/e2e/pipeline.e2e.test.ts index f6f01ca1b..cdf0a02b8 100644 --- a/packages/commands/tests/e2e/pipeline.e2e.test.ts +++ b/packages/commands/tests/e2e/pipeline.e2e.test.ts @@ -2,7 +2,7 @@ import { afterAll, beforeAll, describe, expect, test } from "vite-plus/test"; import { mkdtemp, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { parseStdoutJson, runCommandE2e } from "./helpers.ts"; +import { parseStdoutJson, runCommandHelp, runCommandE2e } from "./helpers.ts"; import { PIPELINE_ROUTES } from "./topic-routes.ts"; describe("e2e: pipeline", () => { @@ -65,7 +65,7 @@ describe("e2e: pipeline", () => { }); test("pipeline run --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(PIPELINE_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(PIPELINE_ROUTES, [ "pipeline", "run", "--help", @@ -76,7 +76,7 @@ describe("e2e: pipeline", () => { }); test("pipeline validate --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(PIPELINE_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(PIPELINE_ROUTES, [ "pipeline", "validate", "--help", diff --git a/packages/commands/tests/e2e/quota.e2e.test.ts b/packages/commands/tests/e2e/quota.e2e.test.ts index 337e8643f..9dbf4932d 100644 --- a/packages/commands/tests/e2e/quota.e2e.test.ts +++ b/packages/commands/tests/e2e/quota.e2e.test.ts @@ -4,20 +4,21 @@ import { isConsoleAuthFailure, isDashScopeE2EReady, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { QUOTA_ROUTES } from "./topic-routes.ts"; describe("e2e: quota", () => { test("quota list --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(QUOTA_ROUTES, ["quota", "list", "--help"]); + const { stderr, exitCode } = await runCommandHelp(QUOTA_ROUTES, ["quota", "list", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toContain("--model"); expect(stderr).toContain("--name"); }); test("quota list --help 包含所有示例", async () => { - const { stderr, exitCode } = await runCommandE2e(QUOTA_ROUTES, ["quota", "list", "--help"]); + const { stderr, exitCode } = await runCommandHelp(QUOTA_ROUTES, ["quota", "list", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toContain("bl quota list"); expect(stderr).toContain("bl quota list --model qwen3-max"); @@ -25,7 +26,7 @@ describe("e2e: quota", () => { }); test("quota update --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(QUOTA_ROUTES, ["quota", "update", "--help"]); + const { stderr, exitCode } = await runCommandHelp(QUOTA_ROUTES, ["quota", "update", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toContain("--model"); expect(stderr).toContain("--rpm"); @@ -34,21 +35,21 @@ describe("e2e: quota", () => { }); test("quota request 作为 quota update 的兼容别名可用", async () => { - const { stderr, exitCode } = await runCommandE2e(QUOTA_ROUTES, ["quota", "request", "--help"]); + const { stderr, exitCode } = await runCommandHelp(QUOTA_ROUTES, ["quota", "request", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toContain("--rpm"); expect(stderr).toContain("--delete"); }); test("quota history --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(QUOTA_ROUTES, ["quota", "history", "--help"]); + const { stderr, exitCode } = await runCommandHelp(QUOTA_ROUTES, ["quota", "history", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toContain("--page"); expect(stderr).toContain("--model"); }); test("quota check --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(QUOTA_ROUTES, ["quota", "check", "--help"]); + const { stderr, exitCode } = await runCommandHelp(QUOTA_ROUTES, ["quota", "check", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toContain("--model"); expect(stderr).toContain("--period"); diff --git a/packages/commands/tests/e2e/search-web.e2e.test.ts b/packages/commands/tests/e2e/search-web.e2e.test.ts index 89e1b30c9..d0c02789e 100644 --- a/packages/commands/tests/e2e/search-web.e2e.test.ts +++ b/packages/commands/tests/e2e/search-web.e2e.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "vite-plus/test"; -import { isDashScopeE2EReady, parseStdoutJson, runCommandE2e } from "./helpers.ts"; +import { isDashScopeE2EReady, parseStdoutJson, runCommandHelp, runCommandE2e } from "./helpers.ts"; import { SEARCH_WEB_ROUTES } from "./topic-routes.ts"; function pagesFromSearchWebStdout(stdout: string): Array<{ title?: string; url?: string }> { @@ -17,7 +17,7 @@ function pagesFromSearchWebStdout(stdout: string): Array<{ title?: string; url?: describe("e2e: search web", () => { test("search web --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(SEARCH_WEB_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(SEARCH_WEB_ROUTES, [ "search", "web", "--help", diff --git a/packages/commands/tests/e2e/skill.e2e.test.ts b/packages/commands/tests/e2e/skill.e2e.test.ts index 5c77f9b14..0f7e1234c 100644 --- a/packages/commands/tests/e2e/skill.e2e.test.ts +++ b/packages/commands/tests/e2e/skill.e2e.test.ts @@ -2,7 +2,7 @@ import { existsSync, mkdtempSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, test } from "vite-plus/test"; -import { isBailianE2EEnabled, parseStdoutJson, runCommandE2e } from "./helpers.ts"; +import { isBailianE2EEnabled, parseStdoutJson, runCommandHelp, runCommandE2e } from "./helpers.ts"; import { SKILL_ROUTES } from "./topic-routes.ts"; /** Canonical always-published skill; also the backbone of advisor wiki sync */ @@ -15,33 +15,33 @@ function makeTempConfigDir(): string { describe("e2e: skill", () => { test("skill add --help exits successfully", async () => { - const { stderr, exitCode } = await runCommandE2e(SKILL_ROUTES, ["skill", "add", "--help"]); + const { stderr, exitCode } = await runCommandHelp(SKILL_ROUTES, ["skill", "add", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/--all/); expect(stderr).toMatch(/--name/); }); test("skill update --help exits successfully", async () => { - const { stderr, exitCode } = await runCommandE2e(SKILL_ROUTES, ["skill", "update", "--help"]); + const { stderr, exitCode } = await runCommandHelp(SKILL_ROUTES, ["skill", "update", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/--all/); expect(stderr).toMatch(/--name/); }); test("skill remove --help exits successfully", async () => { - const { stderr, exitCode } = await runCommandE2e(SKILL_ROUTES, ["skill", "remove", "--help"]); + const { stderr, exitCode } = await runCommandHelp(SKILL_ROUTES, ["skill", "remove", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/--name/); }); test("skill list --help exits successfully", async () => { - const { stderr, exitCode } = await runCommandE2e(SKILL_ROUTES, ["skill", "list", "--help"]); + const { stderr, exitCode } = await runCommandHelp(SKILL_ROUTES, ["skill", "list", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/list|registry/i); }); test("skill init --help exits successfully", async () => { - const { stderr, exitCode } = await runCommandE2e(SKILL_ROUTES, ["skill", "init", "--help"]); + const { stderr, exitCode } = await runCommandHelp(SKILL_ROUTES, ["skill", "init", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/bailian/i); }); diff --git a/packages/commands/tests/e2e/speech-list-voices.e2e.test.ts b/packages/commands/tests/e2e/speech-list-voices.e2e.test.ts index df9dc5579..7d74d0715 100644 --- a/packages/commands/tests/e2e/speech-list-voices.e2e.test.ts +++ b/packages/commands/tests/e2e/speech-list-voices.e2e.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "vite-plus/test"; -import { isDashScopeE2EReady, runCommandE2e } from "./helpers.ts"; +import { isDashScopeE2EReady, runCommandHelp, runCommandE2e } from "./helpers.ts"; import { SPEECH_ROUTES } from "./topic-routes.ts"; /** @@ -8,7 +8,7 @@ import { SPEECH_ROUTES } from "./topic-routes.ts"; describe("e2e: speech list-voices", () => { test("speech synthesize --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(SPEECH_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(SPEECH_ROUTES, [ "speech", "synthesize", "--help", @@ -18,7 +18,7 @@ describe("e2e: speech list-voices", () => { }); test("speech recognize --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(SPEECH_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(SPEECH_ROUTES, [ "speech", "recognize", "--help", diff --git a/packages/commands/tests/e2e/speech-recognize.e2e.test.ts b/packages/commands/tests/e2e/speech-recognize.e2e.test.ts index bd0f2780d..415f6bd76 100644 --- a/packages/commands/tests/e2e/speech-recognize.e2e.test.ts +++ b/packages/commands/tests/e2e/speech-recognize.e2e.test.ts @@ -9,6 +9,7 @@ import { isDashScopeE2EReady, makeE2eOutputDir, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { SPEECH_ROUTES } from "./topic-routes.ts"; @@ -50,7 +51,7 @@ describe("e2e: speech recognize", () => { } test("speech recognize --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(SPEECH_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(SPEECH_ROUTES, [ "speech", "recognize", "--help", diff --git a/packages/commands/tests/e2e/speech-synthesize.e2e.test.ts b/packages/commands/tests/e2e/speech-synthesize.e2e.test.ts index 1a59a3539..d91cb3506 100644 --- a/packages/commands/tests/e2e/speech-synthesize.e2e.test.ts +++ b/packages/commands/tests/e2e/speech-synthesize.e2e.test.ts @@ -7,6 +7,7 @@ import { isDashScopeE2EReady, makeE2eOutputDir, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { SPEECH_ROUTES } from "./topic-routes.ts"; @@ -17,7 +18,7 @@ import { SPEECH_ROUTES } from "./topic-routes.ts"; describe("e2e: speech synthesize", () => { test("speech synthesize --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(SPEECH_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(SPEECH_ROUTES, [ "speech", "synthesize", "--help", diff --git a/packages/commands/tests/e2e/text-chat.e2e.test.ts b/packages/commands/tests/e2e/text-chat.e2e.test.ts index 777610240..eb46da5d2 100644 --- a/packages/commands/tests/e2e/text-chat.e2e.test.ts +++ b/packages/commands/tests/e2e/text-chat.e2e.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "vite-plus/test"; -import { isDashScopeE2EReady, parseStdoutJson, runCommandE2e } from "./helpers.ts"; +import { isDashScopeE2EReady, parseStdoutJson, runCommandHelp, runCommandE2e } from "./helpers.ts"; import { TEXT_CHAT_ROUTES } from "./topic-routes.ts"; /** @@ -8,7 +8,7 @@ import { TEXT_CHAT_ROUTES } from "./topic-routes.ts"; describe("e2e: text chat", () => { test("text chat --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(TEXT_CHAT_ROUTES, ["text", "chat", "--help"]); + const { stderr, exitCode } = await runCommandHelp(TEXT_CHAT_ROUTES, ["text", "chat", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/--api\s+/i); expect(stderr).toMatch(/chat|--message|model|stream/i); diff --git a/packages/commands/tests/e2e/token-plan.e2e.test.ts b/packages/commands/tests/e2e/token-plan.e2e.test.ts index 7bc6cdad1..8b37ea143 100644 --- a/packages/commands/tests/e2e/token-plan.e2e.test.ts +++ b/packages/commands/tests/e2e/token-plan.e2e.test.ts @@ -1,10 +1,10 @@ import { describe, expect, test } from "vite-plus/test"; -import { makeE2eOutputDir, parseStdoutJson, runCommandE2e } from "./helpers.ts"; +import { makeE2eOutputDir, parseStdoutJson, runCommandHelp, runCommandE2e } from "./helpers.ts"; import { TOKEN_PLAN_ROUTES } from "./topic-routes.ts"; describe("e2e: token-plan", () => { test("token-plan help shows centralized OpenAPI auth flags", async () => { - const { stderr, exitCode } = await runCommandE2e(TOKEN_PLAN_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(TOKEN_PLAN_ROUTES, [ "token-plan", "list-seats", "--help", diff --git a/packages/commands/tests/e2e/update.e2e.test.ts b/packages/commands/tests/e2e/update.e2e.test.ts index 9bf726f36..35a68104e 100644 --- a/packages/commands/tests/e2e/update.e2e.test.ts +++ b/packages/commands/tests/e2e/update.e2e.test.ts @@ -1,17 +1,17 @@ import { describe, expect, test } from "vite-plus/test"; -import { runCommandE2e } from "./helpers.ts"; +import { runCommandHelp, runCommandE2e } from "./helpers.ts"; import { UPDATE_ROUTES } from "./topic-routes.ts"; describe("e2e: update", () => { test("update --help 正常退出并展示 --to", async () => { - const { stderr, exitCode } = await runCommandE2e(UPDATE_ROUTES, ["update", "--help"]); + const { stderr, exitCode } = await runCommandHelp(UPDATE_ROUTES, ["update", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/--to/); expect(stderr).toMatch(//); }); test("update --help 包含 --to 示例", async () => { - const { stderr, exitCode } = await runCommandE2e(UPDATE_ROUTES, ["update", "--help"]); + const { stderr, exitCode } = await runCommandHelp(UPDATE_ROUTES, ["update", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toContain("--to 0.1.14"); }); diff --git a/packages/commands/tests/e2e/usage-coding-plan.e2e.test.ts b/packages/commands/tests/e2e/usage-coding-plan.e2e.test.ts index ca83018c1..8b315eee1 100644 --- a/packages/commands/tests/e2e/usage-coding-plan.e2e.test.ts +++ b/packages/commands/tests/e2e/usage-coding-plan.e2e.test.ts @@ -3,13 +3,14 @@ import { isConsoleAuthFailure, isConsoleE2EReady, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { USAGE_ROUTES } from "./topic-routes.ts"; describe("e2e: usage coding-plan", () => { test("usage coding-plan --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(USAGE_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(USAGE_ROUTES, [ "usage", "coding-plan", "--help", @@ -19,7 +20,7 @@ describe("e2e: usage coding-plan", () => { }); test("usage coding-plan --help 包含 --output json 示例", async () => { - const { stderr, exitCode } = await runCommandE2e(USAGE_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(USAGE_ROUTES, [ "usage", "coding-plan", "--help", diff --git a/packages/commands/tests/e2e/usage-free.e2e.test.ts b/packages/commands/tests/e2e/usage-free.e2e.test.ts index 14eea088d..cef06c9c9 100644 --- a/packages/commands/tests/e2e/usage-free.e2e.test.ts +++ b/packages/commands/tests/e2e/usage-free.e2e.test.ts @@ -3,19 +3,20 @@ import { isConsoleE2EReady, isConsoleAuthFailure, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { USAGE_ROUTES } from "./topic-routes.ts"; describe("e2e: usage free", () => { test("usage free --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(USAGE_ROUTES, ["usage", "free", "--help"]); + const { stderr, exitCode } = await runCommandHelp(USAGE_ROUTES, ["usage", "free", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/--model|quota|free-tier/i); }); test("usage free --help 包含所有示例", async () => { - const { stderr, exitCode } = await runCommandE2e(USAGE_ROUTES, ["usage", "free", "--help"]); + const { stderr, exitCode } = await runCommandHelp(USAGE_ROUTES, ["usage", "free", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toContain("bl usage free"); expect(stderr).toContain("bl usage free --model qwen3-max"); diff --git a/packages/commands/tests/e2e/usage-stats.e2e.test.ts b/packages/commands/tests/e2e/usage-stats.e2e.test.ts index 80986cf7f..643eb169c 100644 --- a/packages/commands/tests/e2e/usage-stats.e2e.test.ts +++ b/packages/commands/tests/e2e/usage-stats.e2e.test.ts @@ -3,6 +3,7 @@ import { isConsoleE2EReady, isConsoleAuthFailure, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { USAGE_ROUTES } from "./topic-routes.ts"; @@ -42,13 +43,13 @@ async function fetchDefaultWorkspaceId(): Promise { describe("e2e: usage stats", () => { test("usage stats --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(USAGE_ROUTES, ["usage", "stats", "--help"]); + const { stderr, exitCode } = await runCommandHelp(USAGE_ROUTES, ["usage", "stats", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/--model|--days|stats/i); }); test("usage stats --help 包含所有示例", async () => { - const { stderr, exitCode } = await runCommandE2e(USAGE_ROUTES, ["usage", "stats", "--help"]); + const { stderr, exitCode } = await runCommandHelp(USAGE_ROUTES, ["usage", "stats", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toContain("bl usage stats"); expect(stderr).toContain("bl usage stats --model qwen-turbo"); @@ -56,7 +57,7 @@ describe("e2e: usage stats", () => { }); test("usage stats --help 包含 --workspace-id 选项", async () => { - const { stderr, exitCode } = await runCommandE2e(USAGE_ROUTES, ["usage", "stats", "--help"]); + const { stderr, exitCode } = await runCommandHelp(USAGE_ROUTES, ["usage", "stats", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toContain("--workspace-id"); }); diff --git a/packages/commands/tests/e2e/usage-token-plan.e2e.test.ts b/packages/commands/tests/e2e/usage-token-plan.e2e.test.ts index 1adc5924a..2e2a9a817 100644 --- a/packages/commands/tests/e2e/usage-token-plan.e2e.test.ts +++ b/packages/commands/tests/e2e/usage-token-plan.e2e.test.ts @@ -3,13 +3,14 @@ import { isConsoleAuthFailure, isConsoleE2EReady, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { USAGE_ROUTES } from "./topic-routes.ts"; describe("e2e: usage token-plan", () => { test("usage token-plan --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(USAGE_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(USAGE_ROUTES, [ "usage", "token-plan", "--help", @@ -19,7 +20,7 @@ describe("e2e: usage token-plan", () => { }); test("usage token-plan --help 包含 --output json 示例", async () => { - const { stderr, exitCode } = await runCommandE2e(USAGE_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(USAGE_ROUTES, [ "usage", "token-plan", "--help", diff --git a/packages/commands/tests/e2e/video-download.e2e.test.ts b/packages/commands/tests/e2e/video-download.e2e.test.ts index ece742de1..08e1188eb 100644 --- a/packages/commands/tests/e2e/video-download.e2e.test.ts +++ b/packages/commands/tests/e2e/video-download.e2e.test.ts @@ -8,6 +8,7 @@ import { isDashScopeE2EReady, makeE2eOutputDir, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { VIDEO_ROUTES } from "./topic-routes.ts"; @@ -22,7 +23,11 @@ const PLACEHOLDER_TASK_ID = "00000000-0000-4000-8000-000000000001"; describe("e2e: video download", () => { test("video download --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(VIDEO_ROUTES, ["video", "download", "--help"]); + const { stderr, exitCode } = await runCommandHelp(VIDEO_ROUTES, [ + "video", + "download", + "--help", + ]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/download|--task-id|--out/i); }); diff --git a/packages/commands/tests/e2e/video-edit.e2e.test.ts b/packages/commands/tests/e2e/video-edit.e2e.test.ts index 95e526ab8..9a4e99df6 100644 --- a/packages/commands/tests/e2e/video-edit.e2e.test.ts +++ b/packages/commands/tests/e2e/video-edit.e2e.test.ts @@ -7,6 +7,7 @@ import { isDashScopeE2EReady, makeE2eOutputDir, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { VIDEO_ROUTES } from "./topic-routes.ts"; @@ -17,7 +18,7 @@ import { VIDEO_ROUTES } from "./topic-routes.ts"; describe("e2e: video edit", () => { test("video edit --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(VIDEO_ROUTES, ["video", "edit", "--help"]); + const { stderr, exitCode } = await runCommandHelp(VIDEO_ROUTES, ["video", "edit", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/edit|--video|--prompt|model|--async|--concurrent/i); }); diff --git a/packages/commands/tests/e2e/video-generate-i2v.e2e.test.ts b/packages/commands/tests/e2e/video-generate-i2v.e2e.test.ts index 97d7337ad..b8a424b9e 100644 --- a/packages/commands/tests/e2e/video-generate-i2v.e2e.test.ts +++ b/packages/commands/tests/e2e/video-generate-i2v.e2e.test.ts @@ -8,6 +8,7 @@ import { isDashScopeE2EReady, makeE2eOutputDir, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { VIDEO_ROUTES } from "./topic-routes.ts"; @@ -18,7 +19,11 @@ import { VIDEO_ROUTES } from "./topic-routes.ts"; describe("e2e: video generate (i2v)", () => { test("video generate --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(VIDEO_ROUTES, ["video", "generate", "--help"]); + const { stderr, exitCode } = await runCommandHelp(VIDEO_ROUTES, [ + "video", + "generate", + "--help", + ]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/generate|--prompt|--image|model/i); }); diff --git a/packages/commands/tests/e2e/video-generate-t2v.e2e.test.ts b/packages/commands/tests/e2e/video-generate-t2v.e2e.test.ts index 9151548b1..c77a27d12 100644 --- a/packages/commands/tests/e2e/video-generate-t2v.e2e.test.ts +++ b/packages/commands/tests/e2e/video-generate-t2v.e2e.test.ts @@ -7,6 +7,7 @@ import { isDashScopeE2EReady, makeE2eOutputDir, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { VIDEO_ROUTES } from "./topic-routes.ts"; @@ -17,7 +18,11 @@ import { VIDEO_ROUTES } from "./topic-routes.ts"; describe("e2e: video generate (t2v)", () => { test("video generate --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(VIDEO_ROUTES, ["video", "generate", "--help"]); + const { stderr, exitCode } = await runCommandHelp(VIDEO_ROUTES, [ + "video", + "generate", + "--help", + ]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/generate|--prompt|--model|download|image/i); }); diff --git a/packages/commands/tests/e2e/video-ref-r2v.e2e.test.ts b/packages/commands/tests/e2e/video-ref-r2v.e2e.test.ts index 559bfbce6..e4f666c20 100644 --- a/packages/commands/tests/e2e/video-ref-r2v.e2e.test.ts +++ b/packages/commands/tests/e2e/video-ref-r2v.e2e.test.ts @@ -8,6 +8,7 @@ import { isDashScopeE2EReady, makeE2eOutputDir, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { VIDEO_ROUTES } from "./topic-routes.ts"; @@ -18,7 +19,7 @@ import { VIDEO_ROUTES } from "./topic-routes.ts"; describe("e2e: video ref (r2v)", () => { test("video ref --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(VIDEO_ROUTES, ["video", "ref", "--help"]); + const { stderr, exitCode } = await runCommandHelp(VIDEO_ROUTES, ["video", "ref", "--help"]); expect(exitCode, stderr).toBe(0); expect(stderr).toMatch(/ref|--prompt|--image|model|--async|--concurrent/i); }); diff --git a/packages/commands/tests/e2e/video-task-get.e2e.test.ts b/packages/commands/tests/e2e/video-task-get.e2e.test.ts index eeeb0e4ad..9557d0c1b 100644 --- a/packages/commands/tests/e2e/video-task-get.e2e.test.ts +++ b/packages/commands/tests/e2e/video-task-get.e2e.test.ts @@ -3,6 +3,7 @@ import { isBailianE2EEnabled, isDashScopeE2EReady, parseStdoutJson, + runCommandHelp, runCommandE2e, } from "./helpers.ts"; import { VIDEO_ROUTES } from "./topic-routes.ts"; @@ -15,7 +16,7 @@ const taskId = process.env.BAILIAN_E2E_VIDEO_TASK_ID?.trim(); describe("e2e: video task get", () => { test("video task get --help 正常退出", async () => { - const { stderr, exitCode } = await runCommandE2e(VIDEO_ROUTES, [ + const { stderr, exitCode } = await runCommandHelp(VIDEO_ROUTES, [ "video", "task", "get", diff --git a/packages/e2e/src/run-subprocess.ts b/packages/e2e/src/run-subprocess.ts index 6d41cd235..ca5eced8c 100644 --- a/packages/e2e/src/run-subprocess.ts +++ b/packages/e2e/src/run-subprocess.ts @@ -4,6 +4,7 @@ import { promisify } from "util"; import { monorepoRoot } from "./monorepo-root.ts"; const execFileAsync = promisify(execFile); +const tsxLoader = import.meta.resolve("tsx"); export interface RunCliResult { stdout: string; @@ -18,24 +19,28 @@ export function resolveTsxBin(): string { return join(root, "node_modules", ".bin", name); } -/** 子进程通过 tsx 执行指定 main.ts 入口 */ +/** 子进程通过 Node 的 tsx loader 执行指定 main.ts 入口,不启动 tsx CLI 的 IPC server。 */ export async function runNodeMain( mainTs: string, args: string[], options: { cwd: string; env?: NodeJS.ProcessEnv } = { cwd: process.cwd() }, ): Promise { try { - const { stdout, stderr } = await execFileAsync(resolveTsxBin(), [mainTs, ...args], { - cwd: options.cwd, - encoding: "utf8", - maxBuffer: 32 * 1024 * 1024, - env: { - ...process.env, - NODE_NO_WARNINGS: "1", - DO_NOT_TRACK: "1", - ...options.env, + const { stdout, stderr } = await execFileAsync( + process.execPath, + ["--import", tsxLoader, mainTs, ...args], + { + cwd: options.cwd, + encoding: "utf8", + maxBuffer: 32 * 1024 * 1024, + env: { + ...process.env, + NODE_NO_WARNINGS: "1", + DO_NOT_TRACK: "1", + ...options.env, + }, }, - }); + ); return { stdout: stdout ?? "", stderr: stderr ?? "", exitCode: 0 }; } catch (err: unknown) { const e = err as { diff --git a/packages/e2e/tests/fixtures/echo-argv.ts b/packages/e2e/tests/fixtures/echo-argv.ts new file mode 100644 index 000000000..2bde4f17e --- /dev/null +++ b/packages/e2e/tests/fixtures/echo-argv.ts @@ -0,0 +1,17 @@ +interface FixtureOutput { + args: string[]; + execArgv: string[]; + marker: string | null; +} + +if (process.argv[2] === "--fail") { + process.stderr.write("fixture failure\n"); + process.exitCode = 7; +} else { + const output: FixtureOutput = { + args: process.argv.slice(2), + execArgv: process.execArgv, + marker: process.env.RUNNER_FIXTURE_MARKER ?? null, + }; + process.stdout.write(`${JSON.stringify(output)}\n`); +} diff --git a/packages/e2e/tests/run-subprocess.test.ts b/packages/e2e/tests/run-subprocess.test.ts new file mode 100644 index 000000000..daacbdb5a --- /dev/null +++ b/packages/e2e/tests/run-subprocess.test.ts @@ -0,0 +1,37 @@ +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { expect, test } from "vite-plus/test"; +import { runNodeMain } from "../src/run-subprocess.ts"; + +const testsDir = dirname(fileURLToPath(import.meta.url)); +const fixtureMainTs = join(testsDir, "fixtures", "echo-argv.ts"); + +test("runNodeMain 通过 Node 的 tsx loader 执行 TypeScript 入口", async () => { + const result = await runNodeMain(fixtureMainTs, ["alpha", "beta"], { + cwd: testsDir, + env: { RUNNER_FIXTURE_MARKER: "forwarded" }, + }); + + expect(result.exitCode, result.stderr).toBe(0); + expect(result.stderr).toBe(""); + const output = JSON.parse(result.stdout) as { + args: string[]; + execArgv: string[]; + marker: string | null; + }; + expect(output.args).toEqual(["alpha", "beta"]); + expect(output.marker).toBe("forwarded"); + const importFlagIndex = output.execArgv.indexOf("--import"); + expect(importFlagIndex).toBeGreaterThanOrEqual(0); + expect(output.execArgv[importFlagIndex + 1]).toMatch(/tsx/); +}); + +test("runNodeMain 返回 TypeScript 入口的 stderr 与非零退出码", async () => { + const result = await runNodeMain(fixtureMainTs, ["--fail"], { cwd: testsDir }); + + expect(result).toEqual({ + stdout: "", + stderr: "fixture failure\n", + exitCode: 7, + }); +});