Files
modelstudioai__cli/docs/agents/stress-batch-tests.md
若麒 66402d9868 chore(dev): run CLI workspace entries from source
- use tsx for bl/kscli dev and test runners
- point local library exports to src while keeping publishConfig on dist
- switch vendored telemetry modules to .cjs for source-run compatibility
- update stress/e2e helpers and agent docs for the new source execution path
2026-07-09 19:39:13 +08:00

223 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 批量压测脚本(多能力统一入口)
**使用者文档**方案说明、命令示例、fixtures、默认值表[../stress-testing.md](../stress-testing.md)
## 触发条件
- 新增、修改或排查 `packages/cli/tests/stress/**/*.mjs` 批量压测代码
- 用户要求并发压测 `bl` 各能力(文本 / 语音 / 图像 / 视频等)、生成 Markdown/HTML 报告
- 修改对应命令的 JSON 输出字段后,需同步 `packages/cli/tests/stress/lib/parsers.mjs` 与各 `targets/*.mjs` 的成功判定
- 在 monorepo 根 `package.json` 调整 `test:stress` 入口
**不要**为压测去改 CLI 产品行为(除非用户明确要求修 CLI bug压测侧通过子进程参数与解析逻辑适配现有命令。
## 与 E2E / `vp test` 的关系
| 维度 | E2EVitest | 批量压测(`.mjs` |
| ---- | ---------------------------------------- | ------------------------------------------------------------------ |
| 路径 | `packages/cli/tests/e2e/*.e2e.test.ts` | `packages/cli/tests/stress/run.mjs` + `targets/*.mjs``lib/*.mjs` |
| 运行 | `vp test`(真实 API 需 `BAILIAN_E2E=1` | **仅手动** `pnpm run test:stress -- <target> -- ...` |
| 目的 | 回归help、缺参、dry-run、单条集成 | 并发、限流、耗时统计、批量报告、前置资源 fixtures |
| CI | 可纳入 `vp test`skip 块默认跳过) | **禁止**默认 CI / `vp test` 自动执行 |
`vp test` 只收集 `*.test.ts` / `*.spec.ts` 等,**不会**执行 `tests/stress/*.mjs`。勿将压测逻辑写成 Vitest 用例并放进默认测试流。
E2E 规范见 [cli-e2e-tests.md](cli-e2e-tests.md)。
## 文件与入口
```
packages/cli/tests/stress/
├── run.mjs # 路由入口(解析 target、全局 flags、`--`
├── lib/
│ ├── argv-parse.mjs # 共用 --count/-n、-c、-m、--voice、--report-dir 等
│ ├── stress-config.mjs # count/concurrency 配置加载与解析
│ ├── cli-runner.mjs # spawn main.ts、限流重试、线程池
│ ├── fixtures.mjs # prerequisites.json + 前置音频/图/视频生成
│ ├── parsers.mjs # 各命令 stdout / 文件解析
│ ├── paths.mjs
│ ├── rate-limit.mjs
│ ├── finish-run.mjs # 写报告;套件模式返回摘要而不 exit
│ ├── run-suite.mjs # 全量套件编排
│ ├── suite-catalog.mjs # 用例顺序与中文名
│ ├── suite-report.mjs # SUITE_REPORT.md / .html
│ ├── trace-ids.mjs # requestId / taskId 提取
│ └── report.mjs # REPORT.md / REPORT.html / results.json
└── targets/
├── text-chat.mjs
├── speech-synthesize.mjs
├── speech-recognize.mjs
├── image-generate.mjs
├── image-edit.mjs
├── video-t2v.mjs
├── video-i2v.mjs
├── video-ref.mjs
└── video-edit.mjs
```
monorepo 根 `package.json`:
```json
"test:stress": "node packages/cli/tests/stress/run.mjs"
```
`pnpm` 会在子进程 argv 中插入 `--`;入口会**跳过**孤立的 `--`,因此 `pnpm run test:stress -- list``pnpm run test:stress list` 均可。
## 如何运行
在 **monorepo 根目录**执行(需已配置 `DASHSCOPE_API_KEY``~/.bailian/config.json`:
```sh
# 顺序执行全部 9 个用例,并生成套件总报告 SUITE_REPORT.md耗时长、会打真实 API
pnpm run test:stress
pnpm run test:stress -- all -- --count 5 -c 2
# 列出全部 target
pnpm run test:stress -- list
# 文本对话
pnpm run test:stress -- text -- --count 20 -c 5
# 语音合成(音色可用 --voice 或环境变量 STRESS_TTS_VOICE
pnpm run test:stress -- speech-tts -- --count 10
# 语音识别(先在同批次目录下生成 fixtures/setup-audio.mp3 等)
pnpm run test:stress -- speech-asr -- --count 5
# 生图 / 修图 / 视频类
pnpm run test:stress -- image-generate -- --count 50 -c 2
pnpm run test:stress -- image-edit --reuse-fixtures -- --count 10
pnpm run test:stress -- video-t2v -- --count 3 -c 1
pnpm run test:stress -- video-i2v --setup-only # 仅生成前置图 + manifest
pnpm run test:stress -- video-edit --reuse-fixtures -- --count 3
```
**环境变量**:可写在 `pnpm` 前,例如 `COUNT=5 pnpm run test:stress -- video-t2v`
**命令行参数**`--` 之后传给具体 target与旧 `stress:* -- --count` 语义一致)。
配置优先级:
- **任务数 / 并发**:命令行 `--count` / `-n``--concurrency` / `-c` > `packages/cli/tests/stress/stress.defaults.json`(或 `--stress-config` / `STRESS_CONFIG`> `lib/stress-config.mjs``CODE_DEFAULTS`
- **其它参数**(如 `MODEL`):命令行 > 环境变量 > target 内默认值
### 全局选项(由 `run.mjs` 解析,可从 argv 任意位置剥离)
| 选项 | 含义 |
| ----------------------- | ------------------------------------------------------------------------------- |
| `--reuse-fixtures` | 若当前批次的 `fixtures/prerequisites.json` 已存在且校验通过,则不再跑前置 CLI |
| `--setup-only` | 仅生成前置资源并写入 manifest不进入压测循环适用于带 fixtures 的 target |
| `--fixtures-dir <path>` | 使用**已有**目录下的 `prerequisites.json`(不拷贝),满足本 target 所需字段即可 |
前置 manifest 写入路径:`{REPORT_DIR}/fixtures/prerequisites.json`(默认 REPORT_DIR 含时间戳)。
## 脚本架构(不可随意破坏的约束)
### 子进程调用方式
- **实际执行**:仓库本地 `tsx src/main.ts <args>``cwd``packages/cli`
- **禁止**用 `pnpm run dev` 跑子任务:`pnpm` 会向 stdout 打生命周期日志,污染 JSON 解析
- **报告中的「完整命令」**:用 `pnpm run dev ...` 展示(`buildDisplayCommand`
### 必须带的 CLI 参数(通用)
-`speech recognize` 外,压测子进程宜带 `--output json`(语音识别以 `--out` 文件为准 stdout 可能为纯文本)
- 异步类命令带 `--timeout`、对应 `--poll-interval`
**禁止**对子进程加 `--quiet`(与 `--output json` 并存时可能丢 `urls` / `video_url`)。
**禁止**对视频相关子进程加 `--async`;须阻塞到任务完成(及下载路径正确时落盘)。
### 成功 / 失败判定(概要)
| Target / 能力 | 成功条件 |
| ------------- | ------------------------------------------------------------------------------------------ |
| text | JSON 含有效 `choices[0].message.content` 或流式汇总后的 `content` |
| speech-tts | JSON 含 `audio_url` / `audio_urls``saved` |
| speech-asr | 进程 exit 0`--out` JSON 可被解析出 `transcripts` 文本(或 stdout 有非空正文作为兜底) |
| image | JSON 含 `urls` 和/或 `saved`(不可仅凭 `task_id` 判失败) |
| video | JSON 含 `video_url` 和/或 `saved`(同上) |
详见 `lib/parsers.mjs`
### 墙钟耗时
报告中的 **墙钟总耗时** = `finishedAt - startedAt`(整批真实经过时间),不是各任务 `durationMs` 之和。
## 默认参数(摘要)
各 target 默认值见对应 `targets/*.mjs` 文件头与常量;常见约定:
- **count / concurrency**:见仓库内 `packages/cli/tests/stress/stress.defaults.json`,可按 target 修改
- **video-t2v / i2v / ref / edit**:默认视频类较低 `COUNT`、并发多为 1`TIMEOUT_MS` 可达 1 小时;前置 `video` fixtures 使用文生视频生成约 5s 样片
- **speech-asr**:默认 `POLL_INTERVAL=2`;输入音频由 `speech synthesize` 写入 fixtures
## 报告产物
### 单用例
每次运行在 `REPORT_DIR`(默认 `test/output/<target>-batch-<时间戳>/`)生成:
| 文件 | 内容 |
| ----------------------------- | ---------------------------------------------------------------------------------------- |
| `REPORT.md` | 汇总与明细表(含 **Request ID**、**Task ID** 列,便于排查) |
| `REPORT.html` | 同上 |
| `results.json` | 精简后的原始结果(含 `requestId` / `taskId` 字段) |
| `fixtures/prerequisites.json` | 需前置资源时:记录 `audio` / `image` / `video` 的 http URL、`saved` 本地路径与可复制命令 |
`requestId` / `taskId` 提取逻辑见 `lib/trace-ids.mjs`**仅压测脚本**
- 成功时 CLI stdout 通常不含 `request_id`;压测会默认加 `--verbose`,从 stderr 的 `request_id:` / JSON 块解析
-`task_id` 仍缺 `request_id` 时,压测会额外调用 DashScope `GET /tasks/{id}` 补齐(`lib/fetch-request-id.mjs`
- 关闭方式:`STRESS_VERBOSE_REQUEST_ID=0`(不注入 `--verbose`)、`STRESS_FETCH_REQUEST_ID=0`(不查任务 API
### 全量套件
`pnpm run test:stress`(无 target 或 `all`)在 `test/output/stress-suite-<时间戳>/` 下为每个用例建子目录,并生成:
| 文件 | 内容 |
| ----------------------- | ------------------------------------------------------------------- |
| `SUITE_REPORT.md` | 各用例中文名、墙钟耗时、任务数、并发、成功/失败、成功率、子报告路径 |
| `SUITE_REPORT.html` | 同上 |
| `suite-results.json` | 结构化汇总 |
| `<canonical>/REPORT.md` | 该用例明细报告 |
进程退出码:存在失败任务 → `1`,全部成功 → `0`
根目录 `.gitignore` 已忽略 `test/`,压测产物勿提交。
## Agent 修改清单
### 只改压测脚本时
- [ ] `lib/paths.mjs` 解析的 `CLI_PACKAGE` / `MONOREPO_ROOT` 仍正确
- [ ] 子进程仍为仓库本地 `tsx` + `src/main.ts`,未改回裸 `pnpm run dev` 执行任务
- [ ] 未对子进程加 `--quiet`,视频未加 `--async`
- [ ] `parsers.mjs` 与文档中的成功判定一致
- [ ]`package.json` 仅保留 `test:stress` 入口指向 `run.mjs`
- [ ] `node --check` 对相关 `.mjs` 通过,`pnpm run test:stress -- list` 可运行
### 改了某命令 JSON 输出时
- [ ] 同步 `lib/parsers.mjs` 及受影响 `targets/*.mjs`
- [ ] 有 API Key 时跑小规模:`pnpm run test:stress -- <target> -- --count 2 -c 1`
### 禁止(除非用户明确要求)
- [ ] 为压测专门改 CLI `--quiet` + `--output json` 行为
- [ ] 压测写进默认 `vp test` 路径且未 skip
- [ ] CI / `ready` 自动调用 `test:stress`
## 常见漏点
- 环境变量写在 `pnpm run test:stress` **之后** → 未注入进程
- **未知 target `"--"`**:已在新入口中跳过孤立 `--`;若仍报错请检查是否多空格或 shell 引用
- 高并发无节流 → DashScope Rate limitexit 4应依赖脚本内限流或降低 `-c`
- `image-generate` 大批量开启 `REPORT_THUMBNAILS=1` → HTML 变慢
- 改压测却未同步本文档与 `AGENTS.md`/`CLAUDE.md` 索引
## 与 E2E 的分工
- **改命令选项、help、缺参、单条真实调用** → `tests/e2e/*.e2e.test.ts`
- **并发、配额、fixtures、批量报告** → `tests/stress/` + **手动** `pnpm run test:stress -- <target>`