tester v2: 术语对齐 pytest/JUnit/Robot,补齐 automation 与 not-run
字段改名(每项取舍的理由见新增的 references/concepts.md):
- 用例 status -> lifecycle, suite -> tag, surface -> driver,
cleanup -> teardown, skip_unless -> assume;删除 requires
- risk 取值 safe/act/harm -> readonly/mutating/destructive(单调三级,
与 gate 正交)
- catalog auth_env -> authorize;授权变量统一为
AUTHORIZE_MUTATING / AUTHORIZE_DESTRUCTIVE
新增用例字段 automation(automated/assisted/manual),默认 automated,
并在 validate 里机器校验:manual 禁用 driver 与 how.run 且必须有
how.steps;assisted 至少一步以 [人工] 开头。select 新增 --automation。
结果词新增 not-run,区分「主动不跑」(skipped:retired/pending/超风险
上限/未授权/assume 不成立)与「没轮到」(失败即停后、被分类前置挡住整组、
需人工而无人值守)。
status_probe 定为分类级前置条件:catalog 顶层为默认,categories[] 同名键
覆盖(缺省继承 / false 关闭 / mapping 整体替换)。select 按分类返回生效值,
并把 probe.url 用 vars 展开(solve Agent 无法自行解析 ${VAR} 的问题),
同批返回 vars 供 how 里的 ${...} 替换。
新增 references/concepts.md:术语表(每词一句唯一定义 + 业界锚点 +
反例)、v1->v2 映射、以及 risk/automation/not-run/status_probe 的设计理由。
其余文档(SKILL/README/schema/levels/run/maintain)与三个脚本(catalog、
generate_cases_csv、generate_run_csv)同步更新。
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
# tester 概念、命名与设计理由
|
||||
|
||||
这是 tester 的**唯一术语真相**。写用例、改脚本、看文档遇到词义冲突时,以本文件为准。
|
||||
|
||||
## 0. 三层结构
|
||||
|
||||
```
|
||||
catalog(整个测试集)
|
||||
└── category(= 一个 suite,一个 cases/<cat>.yaml)
|
||||
└── case(一条用例)
|
||||
```
|
||||
|
||||
| 层 | 是什么 | 业界对应 |
|
||||
|---|---|---|
|
||||
| catalog | 全部用例 + 全局配置(`catalog.yaml`) | pytest 的一次 collection;整个 test suite |
|
||||
| category | 一组用例 + 自己的前置条件(`status_probe`) | Robot **suite**;pytest 一个目录 + conftest.py |
|
||||
| case | 一条用例 | test case |
|
||||
|
||||
**「suite」只作总称**(泛指整个测试集),不作 YAML 键。集合级的角色由 **category** 承担:`cases/<cat>.yaml` 就是 suite 容器,`categories[].status_probe` 就是 Suite Setup。所以框架里没有 `suite` 字段。
|
||||
|
||||
## 1. 术语表
|
||||
|
||||
每个词一句话定义 + 业界锚点 + 「它不是什么」。
|
||||
|
||||
### 用例字段
|
||||
|
||||
| 词 | 定义 | 业界锚点 | 不是什么 |
|
||||
|---|---|---|---|
|
||||
| `case` | 一条可独立判定的测试 | test case | 不是一次运行记录(那是 `runs/`) |
|
||||
| `tag` | 成本档:`smoke` / `full` / `slow`,单选 | pytest `mark`;JUnit / Robot `tag` | 不是分类,不是「一个 suite」 |
|
||||
| `risk` | 执行副作用等级:`readonly` / `mutating` / `destructive` | read-only vs mutating;destructive tests;K8s e2e `[Disruptive]` | 不是概率,不是「要不要人」(那是 `automation`) |
|
||||
| `lifecycle` | 用例生命周期:`active` / `pending` / `retired` | JUnit `@Disabled`;pytest skip / deselect | 不是 HTTP 状态,不是前置探测(那是 `api-status` / `status_probe`) |
|
||||
| `driver` | 谁来驱动执行:`script` / `browser` / `mobile` | Selenium / Appium **driver** | 不是「要不要人」(那是 `automation`) |
|
||||
| `automation` | 人工参与度:`automated` / `assisted` / `manual` | TestRail *Automation Type*;ADO *Automation status*;Xray *Test Type* | 不是执行器(那是 `driver`) |
|
||||
| `session` | 可复用会话(浏览器 / 模拟器) | pytest fixture;Robot suite variable | 不是一条用例 |
|
||||
| `gate` | 项目细分闸门名 → 环境变量(`motion` / `hardware` / `weld`) | 授权开关 | 不是 `risk`,不要拿它代替风险等级 |
|
||||
| `authorize` | 风险档 → 环境变量。该档执行前必须为 `true` | pytest `skipif` 条件 | 不是认证(auth),是**授权** |
|
||||
| `assume` | 额外跳过条件(gate / authorize 之外的环境表达式) | JUnit `assumeTrue`;pytest `skipif` | 不是授权开关的重复写法 |
|
||||
| `status_probe` | 分类级前置条件:请求一个 URL,命中即视为前置不满足 | pytest session fixture;Robot Suite Setup | 不是产品失败,不是整轮硬闸 |
|
||||
| `how` | 怎么测:`steps` / `run` / `url` / `timeout` | test steps | 不是判定标准 |
|
||||
| `expected` | 怎么算过:`{kind, value}` 列表 | pytest `assert`;Robot `Should …` | 不是步骤 |
|
||||
| `run_together` | 必须成组执行的用例(有序、可选 continue_on_fail) | 场景组(scenario) | 不是 suite,不是 tag |
|
||||
|
||||
### 结果词
|
||||
|
||||
| 结果 | 定义 | 业界锚点 |
|
||||
|---|---|---|
|
||||
| `pass` | 通过 | passed |
|
||||
| `fail` | 产品缺陷 | failed |
|
||||
| `environment-fail` | 前置/环境导致无法执行,**永远不是产品失败** | pytest error;JUnit aborted |
|
||||
| `skipped` | **主动**不跑:`retired` / `pending` / 超风险上限 / 未授权 / `assume` 不成立 | pytest skipped;JUnit disabled |
|
||||
| `not-run` | **被动**没轮到:失败即停后剩下的;分类被 `status_probe` 挡住整组没执行;`automation` 需人工而无人值守 | pytest not run;deselected |
|
||||
|
||||
## 2. 命名迁移对照(v1 → v2)
|
||||
|
||||
| v1 | v2 | 为什么改 |
|
||||
|---|---|---|
|
||||
| 用例 `status` | `lifecycle` | v1 的 `status` 一词三义:用例状态、`api-status` 的 HTTP 状态、`status_probe`。拆开后各是各的 |
|
||||
| 用例 `suite` | `tag` | Robot 里 suite 是**集合**,`cases/<cat>.yaml` 才是集合;我们却拿它当标记。改 `tag` 与 pytest mark / JUnit tag 对齐 |
|
||||
| 用例 `surface` | `driver` | `surface` 是自造词;`driver` 是 Selenium / Appium 的既有概念,且正好回答「谁来驱动」 |
|
||||
| 用例 `cleanup` | `teardown` | 与 pytest teardown / JUnit `@AfterEach` / Robot `[Teardown]` 对齐 |
|
||||
| 用例 `skip_unless` | `assume` | 语义是「假设成立才跑」,对应 JUnit `assumeTrue`;旧名与 `gate` 含义重叠 |
|
||||
| 用例 `requires` | **删除** | 只出现在导出的 CSV,不参与任何逻辑;126 条标注还不一致。前置能力改用 `tag` + `driver` + `gate`/`assume` 表达 |
|
||||
| catalog `auth_env` | `authorize` | `auth` 被读成「认证」,实际是「授权放行」 |
|
||||
| `risk` 值 `safe/act/harm` | `readonly/mutating/destructive` | `act` 太含糊(谁都在 act),`harm` 情绪化,且把「改了状态」与「难恢复」混成一维。新词是单调三级,见下 |
|
||||
| 结果 `skipped`(含被动) | `skipped` + `not-run` | v1 把「主动不跑」和「被动没轮到」混在一个词里,成绩单看不出是哪种 |
|
||||
|
||||
## 3. 关键设计理由(为什么是这些值)
|
||||
|
||||
### 3.1 `risk`:为什么是 `readonly / mutating / destructive`
|
||||
|
||||
- 这一维回答的是「跑它会不会改变被测系统、有多难退回」,不是概率,也不是「要不要人」。
|
||||
- `readonly` / `mutating` 是数据库、文件系统、API 测试里的通用二分,事实性描述。
|
||||
- `destructive` 是硬件 / 内核 / 集群测试的标准词(Kubernetes e2e 用 `[Disruptive]` 标记会打断集群的用例)。
|
||||
- 三级**单调**:`readonly ⊂ mutating ⊂ destructive`,所以只需要一个上限 `default_max_risk` 和一个 `--max-risk`,不用组合矩阵。
|
||||
- 与 `gate` 正交:`risk` 说「影响多大」,`gate` 说「碰哪个子系统」。点动 vs 焊接这种细分授权用 `gate`,不要再造 risk 名。
|
||||
- `tag=smoke` 只能配 `risk=readonly`:冒烟测试不许留下变化。
|
||||
|
||||
### 3.2 `automation`:为什么是 `automated / assisted / manual`
|
||||
|
||||
- 有些用例无法自动化,或需要人在现场配合(急停、断电、物理复位、目视 LED)。这需要在**选范围**阶段就能筛出来。
|
||||
- 这是测试管理工具的标准字段(TestRail *Automation Type*、ADO *Automation status*、Xray *Test Type*),因此沿用 `automation` 这个名字。
|
||||
- 与 `driver` 正交:`driver` 说「用什么驱动」,`automation` 说「要不要人」。把 `manual` 塞进 `driver` 就无法表达 `assisted`(它仍由 script/browser 驱动)。
|
||||
- 三档的含义与约束:
|
||||
- `automated`(默认):Agent 端到端跑完。
|
||||
- `assisted`:Agent 驱动,但至少一步需要人在场;该步在 `how.steps` 里以 `[人工]` 开头(可被 `validate` 机器判定)。
|
||||
- `manual`:只能人工执行;禁止 `driver`、禁止 `how.run`,必须有 `how.steps`。
|
||||
- 默认只跑 `automated`。无人值守时 `assisted` / `manual` 记 `not-run`;有人在场时 `assisted` 暂停等人确认,`manual` 由人工回报结果。
|
||||
|
||||
### 3.3 结果为什么拆出 `not-run`
|
||||
|
||||
- `skipped` = **我们决定不跑**(retired / pending / 超上限 / 未授权 / assume 不成立)。
|
||||
- `not-run` = **没轮到**(失败即停后剩下的 / 分类被前置挡住 / 需人工而无人值守)。
|
||||
- 混在一起,成绩单就看不出「本轮到底跑没跑」。拆开后 `executed` 列(`yes` / `no`)与原因都清楚。
|
||||
|
||||
### 3.4 `status_probe` 为什么是分类级
|
||||
|
||||
- 不同分类的前置条件不同:相机链路走 `API_BASE`(8095) / 相机内网,独立于机械臂 FSM(8096)。机械臂没连上不应该挡住相机测试。
|
||||
- 因此它是**分类级前置条件**:`catalog.status_probe` 是全局默认,`categories[].status_probe` 可覆盖(缺省继承 / `false` 关闭 / mapping 整体替换)。对应 pytest 的 session fixture、Robot 的 Suite Setup。
|
||||
- 命中或不可达只影响用它的分类;只有所有选中分类都被挡住,整轮结论才是 `environment-fail`。`select` 会按分类返回解析后的生效值,Agent 不要自己读 catalog 猜。
|
||||
|
||||
## 4. 不采用的词
|
||||
|
||||
| 词 | 不用它的原因 |
|
||||
|---|---|
|
||||
| `suite`(作字段) | 与 Robot 的「集合」语义冲突;集合角色已由 category 承担 |
|
||||
| `status`(作字段) | 与 HTTP 状态、前置探测撞词 |
|
||||
| `surface` | 自造词,业界无此概念 |
|
||||
| `requires` | 不参与逻辑,标注还容易失真 |
|
||||
| `sanity` / `regression` / `acceptance` | 与 `smoke` / `full` 分不清,且已有 category / id / risk / automation 多层筛选 |
|
||||
| `preconditions`(通用列表) | 当前只有 `status_probe` 一种前置,出现第二种再抽象 |
|
||||
@@ -1,8 +1,8 @@
|
||||
# suite 与 risk
|
||||
# tag 与 risk
|
||||
|
||||
维护或用户问套件/安全级别时读本文件。两套词对所有项目相同,不要自造名字。
|
||||
维护或用户问套件/安全级别时读本文件。两套词对所有项目相同,不要自造名字。命名理由见 [concepts.md](concepts.md)。
|
||||
|
||||
## suite:跑哪一批
|
||||
## tag:跑哪一批
|
||||
|
||||
只表示耗时和依赖,不表示危不危险。危不危险看 `risk`。
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
| 值 | 何时用 |
|
||||
|----|--------|
|
||||
| `smoke` | 最短路径证明系统还活着。必须快、少依赖、**`risk` 只能是 `safe`**。 |
|
||||
| `smoke` | 最短路径证明系统还活着。必须快、少依赖、**`risk` 只能是 `readonly`**。 |
|
||||
| `full` | 日常回归里其余自动化用例。说「跑 full」仍受风险上限约束。 |
|
||||
| `slow` | 太慢或环境太重,不进日常 full:真实焊接、付费 API、要工件/账号、跑很久。 |
|
||||
|
||||
@@ -23,18 +23,20 @@
|
||||
3. 其余自动化 → `full`。
|
||||
4. 拿不准 → `full`。不要把会动真机或会删数据的放进 `smoke`。
|
||||
|
||||
未指定套件时只跑 `smoke`。`跑 full` 不含 `slow`。
|
||||
未指定 tag 时只跑 `smoke`。`跑 full` 不含 `slow`。
|
||||
|
||||
## risk:副作用有多大
|
||||
|
||||
三个短词,网站 / App / 机器人共用。`catalog.yaml` 的 `risks` 必须按此顺序,且以 `safe` 开头:
|
||||
三个词,网站 / App / 机器人共用,单调升级。`catalog.yaml` 的 `risks` 必须按此顺序,且以 `readonly` 开头:
|
||||
|
||||
| 值 | 含义 | 网站 | App | 机器人 |
|
||||
|----|------|------|-----|--------|
|
||||
| `safe` | 只看,不留下变化 | 打开页、只读 API | 打开界面 | 读位姿、查状态 |
|
||||
| `act` | 会动手,一般可补救 | 提交表单、改配置 | 发消息、保存 | 点动、使能、开盖扫描 |
|
||||
| `harm` | 难恢复或伤人/伤钱/伤环境 | 删库、真实扣费 | 清数据、内购 | 急停、断电、真焊 |
|
||||
| `readonly` | 只看,不留下变化 | 打开页、只读 API | 打开界面 | 读位姿、查状态 |
|
||||
| `mutating` | 会改状态,一般可补救 | 提交表单、改配置 | 发消息、保存 | 点动、使能、开盖扫描 |
|
||||
| `destructive` | 难恢复或伤人/伤钱/伤环境 | 删库、真实扣费 | 清数据、内购 | 急停、断电、真焊 |
|
||||
|
||||
默认只跑 `safe`。项目细分闸(点动 vs 焊接)用可选 `gate`,不要再发明 risk 名。
|
||||
默认只跑 `readonly`。项目细分闸(点动 vs 焊接)用可选 `gate`,不要再发明 risk 名。
|
||||
|
||||
`suite=smoke` 且 `risk!=safe` 为非法。
|
||||
`tag=smoke` 且 `risk!=readonly` 为非法。
|
||||
|
||||
不想跑某一档时,`authorize.<risk>` 指向的环境变量保持 false 即可——该档用例记 `skipped`,不是 `not-run`。
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
python3 <skill-dir>/scripts/catalog.py next-id --root <root> --category <CAT>
|
||||
```
|
||||
|
||||
3. 拟一条符合 [schema.md](schema.md) 的用例。`suite`/`risk` 按 [levels.md](levels.md):`smoke` 只能 `safe`;会动手用 `act`,难恢复用 `harm`。细分授权用 `gate`,不要发明新的 risk 名。
|
||||
4. `surface=browser|mobile` 必须填 `session`,且 catalog 里已声明。
|
||||
3. 拟一条符合 [schema.md](schema.md) 的用例。`tag`/`risk` 按 [levels.md](levels.md):`smoke` 只能 `readonly`;会改状态用 `mutating`,难恢复用 `destructive`。细分授权用 `gate`,不要发明新的 risk 名。需人工参与的填 `automation`(见 [concepts.md](concepts.md))。
|
||||
4. `driver=browser|mobile` 必须填 `session`,且 catalog 里已声明。
|
||||
5. `what` 和 `expected` 写成可观察信号,禁止「应该正常」。
|
||||
6. 给用户看拟写内容。确认后写入 `cases/<category小写>.yaml` 的 `cases` 列表末尾。
|
||||
7. `validate`。失败则修好再结束。通过后跑 `generate_cases_csv.py --root <root>` 刷新 `.pouch/tester/cases.csv`。不要手改这份 CSV。
|
||||
@@ -20,13 +20,13 @@ python3 <skill-dir>/scripts/catalog.py next-id --root <root> --category <CAT>
|
||||
## 完善 / 更新
|
||||
|
||||
1. `show --id <id>`。
|
||||
2. 只改用户点名的字段(常见:`how`、`expected`、`risk`、`notes`)。
|
||||
2. 只改用户点名的字段(常见:`how`、`expected`、`risk`、`automation`、`notes`)。
|
||||
3. 展示 diff 或更新后的整条。确认后写回。
|
||||
4. `validate`。通过后跑 `generate_cases_csv.py` 刷新 `cases.csv`。
|
||||
|
||||
## 退役
|
||||
|
||||
把 `status` 改为 `retired`,不要删文件、不要复用 id。`notes` 写原因。确认后写回,再跑 `generate_cases_csv.py`。
|
||||
把 `lifecycle` 改为 `retired`,不要删文件、不要复用 id。`notes` 写原因。确认后写回,再跑 `generate_cases_csv.py`。
|
||||
|
||||
## 禁止
|
||||
|
||||
|
||||
@@ -4,16 +4,21 @@
|
||||
|
||||
## 1. 选范围
|
||||
|
||||
未指定范围 → `--suite smoke`,风险上限 = `default_max_risk`。
|
||||
未指定范围 → `--tag smoke`,风险上限 = `default_max_risk`,`automation` 默认只跑 `automated`。
|
||||
|
||||
用户说分类、id、suite、风险上限时,原样传给 select:
|
||||
用户说分类、id、tag、风险上限时,原样传给 select:
|
||||
|
||||
```bash
|
||||
python3 <skill-dir>/scripts/catalog.py select --root <root> \
|
||||
[--category CAT] [--id ID]... [--suite smoke|full|slow] \
|
||||
[--max-risk <risk>] [--surface script|browser|mobile]
|
||||
[--category CAT] [--id ID]... [--tag smoke|full|slow] \
|
||||
[--max-risk <risk>] [--driver script|browser|mobile] \
|
||||
[--automation automated|assisted|manual|any]
|
||||
```
|
||||
|
||||
`--automation` 默认 `automated`(需人工的条目会被跳过,并在 JSON `skipped` 里说明);用户明确要求现场配合时用 `assisted`,`any` 表示不按人工参与度筛。
|
||||
|
||||
`select` 的 JSON 字段:`max_risk` / `automation` / `count` / `vars`(已展开的变量,用来替换 `how` 里的 `${…}`)/ `skipped` / `run_together` / `status_probe`(按分类,`url` 已展开)/ `cases`。
|
||||
|
||||
`select` 已排除 `retired` 和风险高于上限的条目(后者若用户点了具体 id,会在 JSON 的 `skipped` 里说明,不执行)。
|
||||
|
||||
若 catalog 有 `run_together`,命中组内任一条就把整组按 `ids` 顺序扩进列表(JSON 带 `run_together`)。这不是擅自扩大范围。
|
||||
@@ -23,8 +28,19 @@ python3 <skill-dir>/scripts/catalog.py select --root <root> \
|
||||
## 2. 运行前闸
|
||||
|
||||
- `HOST` 未设(环境变量 / `HX_TEST_HOST` / `local.yaml` 都没有):全部停,environment-fail。
|
||||
- 命中条的 `risk` 在 catalog `auth_env` 里有对应变量:变量不是 `true` 则该条 `skipped`。有 `gate` 时还要 `catalog.gates` 对应变量为 `true`。即使用户点了 id 也不执行。
|
||||
- 若 catalog 声明了 `status_probe`:先请求它。命中 `stop_if` → 整轮停,environment-fail。
|
||||
- 命中条的 `risk` 在 catalog `authorize` 里有对应变量:变量不是 `true` 则该条 `skipped`。有 `gate` 时还要 `catalog.gates` 对应变量为 `true`;`assume` 不成立同样 `skipped`。即使用户点了 id 也不执行。
|
||||
- 授权/假设不成立是**主动不跑**(`skipped`),不是 `not-run`:这是我们决定不跑,不是没轮到。
|
||||
- `status_probe` 是**分类级前置条件**(业界对应 pytest 的 session fixture / Robot 的 Suite Setup)。用 `select` 返回的 `status_probe` 解析生效值,不要自己读 catalog 猜。返回的 `probe.url` 已用 `vars` 展开,直接请求即可;`select` 同批返回 `vars`:
|
||||
|
||||
| `categories[].status_probe` | 生效探测 |
|
||||
|------------------------------|----------|
|
||||
| 未声明 | 继承顶层 `catalog.status_probe`(`source: global`)|
|
||||
| `false` | 该分类不做闸(`source: disabled`)|
|
||||
| mapping | 整体覆盖顶层,不合并(`source: category`)|
|
||||
|
||||
- 同一生效探测一次运行只请求一次,结果复用。
|
||||
- 探测不可达:用它的分类标 environment-fail,其余分类照跑。
|
||||
- 命中 `stop_if`:把用它的分类标 environment-fail 并记 `observed` 值,其余分类照跑;所有选中分类都被挡住时,整轮结论才是 environment-fail。
|
||||
|
||||
## 3. Session
|
||||
|
||||
@@ -33,7 +49,7 @@ python3 <skill-dir>/scripts/catalog.py select --root <root> \
|
||||
- 尚未初始化:按 catalog `sessions.<name>.init` 做。`reuse: persist` 的先 `probe`,活着就附着。
|
||||
- 本轮已初始化:直接用,不要重开浏览器或模拟器。
|
||||
- `reuse: run`:整轮结束再关。`persist`:不关。
|
||||
- 中途 session 挂了:后续同 surface 标 environment-fail,不要标产品失败。
|
||||
- 中途 session 挂了:后续同 driver 标 environment-fail,不要标产品失败。
|
||||
|
||||
## 4. 执行
|
||||
|
||||
@@ -41,6 +57,12 @@ python3 <skill-dir>/scripts/catalog.py select --root <root> \
|
||||
|
||||
例外:当前条和下一条同属 `run_together` 且该组 `continue_on_fail: true` 时,前一条产品 `fail` 仍跑后一条。`environment-fail` 仍整轮停。
|
||||
|
||||
**automation**
|
||||
|
||||
- `automated`:Agent 端到端执行。
|
||||
- `assisted`:照常驱动,但遇到以 `[人工]` 开头的 `how.steps` 时停下,请现场人员执行并回报,确认后再继续。无人值守时该条记 `not-run`。
|
||||
- `manual`:没有 `driver`,`run-script` 会直接返回 `not-run`。只能由人执行、回报结果,Agent 负责记录。
|
||||
|
||||
**script**
|
||||
|
||||
有 `how.run`:
|
||||
@@ -59,9 +81,9 @@ python3 <skill-dir>/scripts/catalog.py run-script --root <root> --id <id>
|
||||
|
||||
## 5. 记录与报告
|
||||
|
||||
每条:`id, result, evidence, notes`。`result` 只能是 `pass` / `fail` / `environment-fail` / `skipped`。
|
||||
每条:`id, result, evidence, notes`。`result` 只能是 `pass` / `fail` / `environment-fail` / `skipped` / `not-run`。`skipped` 是主动不跑(retired / pending / 超风险上限 / 未授权 / `assume` 不成立),`not-run` 是没轮到(失败即停后剩下的 / 被前置整组挡住的 / 需人工而无人值守的)。
|
||||
|
||||
写入 `.pouch/tester/runs/<UTC时间>-<范围>.yaml`,包含筛选条件、HOST 是否已设(可以写 HOST 值)、session 是否复用、逐条结果。立刻用同一快照生成 CSV,不要手改:
|
||||
写入 `.pouch/tester/runs/<UTC时间>-<范围>.yaml`,包含筛选条件、HOST 是否已设(可以写 HOST 值)、session 是否复用、每个分类的 `status_probe` 生效值与观测结果(`source` / `disabled` 原因 / `hit` / `observed`)、逐条结果。立刻用同一快照生成 CSV,不要手改:
|
||||
|
||||
```bash
|
||||
python3 <skill-dir>/scripts/generate_run_csv.py --root <root> --run <yaml>
|
||||
@@ -77,7 +99,7 @@ python3 <skill-dir>/scripts/generate_run_csv.py --root <root> --run <yaml>
|
||||
范围: …
|
||||
风险上限: …
|
||||
session: browser=复用|新开|未用;mobile=…
|
||||
计数: pass=N fail=N environment-fail=N skipped=N
|
||||
计数: pass=N fail=N environment-fail=N skipped=N not-run=N
|
||||
失败: id + 一句证据
|
||||
跳过: id + 原因
|
||||
快照: .pouch/tester/runs/<file>.yaml
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# tester 目录字段
|
||||
|
||||
术语与命名理由见 [concepts.md](concepts.md);本文件只列字段与约束。
|
||||
|
||||
项目根 `.pouch/tester/`:
|
||||
|
||||
```
|
||||
@@ -16,15 +18,16 @@ runs/*.csv 同一次运行的成绩单;generate_run_csv.py 从对
|
||||
|
||||
| 字段 | 含义 |
|
||||
|------|------|
|
||||
| `categories` | `{id, title}` 列表,id 与 `cases/<id小写>.yaml` 对应;列表顺序即 `select` / 执行顺序 |
|
||||
| `risks` | 从低到高,只能是 `safe` → `act` → `harm` 的一段,且以 `safe` 开头。含义见 [levels.md](levels.md) |
|
||||
| `categories` | `{id, title, status_probe?}` 列表,id 与 `cases/<id小写>.yaml` 对应;列表顺序即 `select` / 执行顺序。`status_probe` 见下 |
|
||||
| `risks` | 从低到高,只能是 `readonly` → `mutating` → `destructive` 的一段,且以 `readonly` 开头。含义见 [levels.md](levels.md) |
|
||||
| `default_max_risk` | 未指定时的运行上限,必须是 `risks` 里的值 |
|
||||
| `vars` | 可替换变量,如 `API_BASE: http://${HOST}:8095` |
|
||||
| `auth_env` | 风险名 → 环境变量名。该档执行前变量必须为 `true` |
|
||||
| `authorize` | 风险名 → 环境变量名。该档执行前变量必须为 `true`(`readonly` 不需要) |
|
||||
| `gates` | 可选。项目细分闸名 → 环境变量。用在用例的 `gate` 上,不要拿它代替 `risk` |
|
||||
| `sessions` | 可复用会话。`kind`: `browser` / `mobile`;`reuse`: `run`(本轮结束关掉)或 `persist`(probe 活着就附着) |
|
||||
| `id_pattern` | 用例 id 正则 |
|
||||
| `run_together` | 可选。成组执行:`[{ids: [TC-…, TC-…], continue_on_fail: true}]`。点到组内任一条,`select` 会按 `ids` 顺序把整组扩进命中列表。`continue_on_fail: true` 时组内前一条产品 fail 仍跑后一条(返回/收尾)。一个 id 只能属于一组。 |
|
||||
| `status_probe` | 可选。顶层默认值,一个**分类级前置条件**:`{url, stop_if: {字段: [命中值]}}`。运行前请求 `url`,响应命中任一 `stop_if` 条件即视为前置不满足。分类项可用同名键覆盖:缺省继承顶层、`false` 关闭、mapping 整体替换。命中或不可达只影响用它的分类,不是产品失败。业界对应 pytest 的 session fixture / Robot 的 Suite Setup。`select` 会按分类返回解析后的生效值,`url` 已用 `vars` 展开 |
|
||||
|
||||
`local.yaml` 与环境变量覆盖 `HOST` 和授权开关。查找顺序:环境变量 > `local.yaml` > `catalog.yaml` 的 vars。也认 `HX_TEST_HOST` 作为 `HOST`。
|
||||
|
||||
@@ -38,33 +41,37 @@ runs/*.csv 同一次运行的成绩单;generate_run_csv.py 从对
|
||||
| `category` | 是 | 必须等于文件声明的 category |
|
||||
| `title` | 是 | 短名 |
|
||||
| `what` | 是 | 一句可观察的「测什么」 |
|
||||
| `status` | 是 | `active` / `pending` / `retired`。`pending` = 暂缓,仍保留在目录里但不执行,原因写在 `notes` |
|
||||
| `suite` | 是 | `smoke` / `full` / `slow`,判定见 [levels.md](levels.md) |
|
||||
| `risk` | 是 | `safe` / `act` / `harm` |
|
||||
| `gate` | 否 | 项目细分闸,必须在 catalog `gates` 里 |
|
||||
| `surface` | 是 | `script` / `browser` / `mobile` |
|
||||
| `lifecycle` | 是 | `active` / `pending` / `retired`。`pending` = 暂缓,仍保留在目录里但不执行,原因写在 `notes` |
|
||||
| `tag` | 是 | `smoke` / `full` / `slow`,判定见 [levels.md](levels.md)。`tag=smoke` 只能配 `risk=readonly` |
|
||||
| `risk` | 是 | `readonly` / `mutating` / `destructive` |
|
||||
| `gate` | 否 | 项目细分闸,必须在 catalog `gates` 里;`risk=readonly` 不要填 |
|
||||
| `driver` | 否 | `script` / `browser` / `mobile`;`automation=manual` 时禁止填 |
|
||||
| `automation` | 否 | `automated`(默认)/ `assisted` / `manual`,见 [concepts.md](concepts.md) |
|
||||
| `session` | browser/mobile 必填 | 指向 catalog `sessions` 的键 |
|
||||
| `how` | 是 | 怎么测,见下 |
|
||||
| `expected` | 是 | 怎么过,至少一条 `{kind, value}` |
|
||||
| `maps_to` | 否 | 对人文档的功能项 id |
|
||||
| `requires` | 否 | 前置标签 |
|
||||
| `skip_unless` | 否 | 额外跳过条件(环境变量表达式) |
|
||||
| `assume` | 否 | 额外跳过条件(环境变量表达式)。不要重复 `risk` / `gate` 已覆盖的授权变量,否则 `validate` 报错 |
|
||||
| `setup` | 否 | 人话前置 |
|
||||
| `cleanup` | 否 | 跑完回退 |
|
||||
| `teardown` | 否 | 跑完回退 |
|
||||
| `notes` | 否 | 备注 |
|
||||
|
||||
## how
|
||||
|
||||
按 `driver` 分派;`automation=manual` 时没有 `driver`,只有 `how.steps`(人工步骤)。
|
||||
|
||||
- `script`:`how.run` 是一条可在项目根执行的命令(可多行脚本)。没有 `run` 时 Agent 按 `how.steps` 做,仍不得发明未写明的请求。
|
||||
- `browser`:`how.url` + `how.steps`。`expected.kind` 用 `visible-text` / `url` / `interaction`。
|
||||
- `mobile`:`how.steps`,挂到 `session` 指定的模拟器。`expected` 同样是可观察信号。
|
||||
|
||||
可选 `how.timeout`,秒,默认 60。
|
||||
|
||||
`automation=assisted` 时,至少一个 `how.steps` 条目要以 `[人工]` 开头(写在 YAML 里要加引号,`[` 开头会被当成流式序列)。
|
||||
|
||||
## expected.kind
|
||||
|
||||
`api-status` / `api-field` / `script-exit` / `visible-text` / `url` / `interaction`
|
||||
|
||||
## 判定
|
||||
|
||||
`pass` / `fail` / `environment-fail` / `skipped`
|
||||
`pass` / `fail` / `environment-fail` / `skipped` / `not-run`
|
||||
|
||||
Reference in New Issue
Block a user