docs: generalize skill-template references + add TESTING.md
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
19
README.md
19
README.md
@@ -1,4 +1,4 @@
|
||||
# 匠厂 技能开发模板
|
||||
# 匠厂 技能开发模板(通用业务)
|
||||
|
||||
这是一个**规范化的新技能模板仓库**,用于复制出新的 skill 项目;它本身**不是业务 skill**。
|
||||
|
||||
@@ -6,23 +6,25 @@
|
||||
|
||||
- 对齐当前规范 skill 的目录结构:`assets/`、`references/`、`scripts/`、`tests/`、`evals/`
|
||||
- 对齐当前规范脚手架分层:`scripts/cli`、`scripts/db`、`scripts/service`、`scripts/util`、`scripts/jiangchang_skill_core`
|
||||
- 提供最小可运行入口:`python scripts/main.py health` / `version`
|
||||
- 提供最小可运行入口:`python scripts/main.py health` / `version`,以及通用任务命令骨架 `run` / `logs` / `log-get`
|
||||
- 内置 **隔离测试体系**:`IsolatedDataRoot`、双键环境、`OPENCLAW_TEST_TARGET` 档位与 adapter profile 策略(详见 [`references/TESTING.md`](references/TESTING.md))
|
||||
- 让新技能从一开始就按规范落地,不再沿用旧模板的 `docs/`、`optional/`、`skill_main.py` 结构
|
||||
|
||||
## 新技能使用步骤
|
||||
|
||||
1. 复制本目录为新的 skill 仓库。
|
||||
2. 全局替换 `your-skill-slug`、`your-platform-key`、`您的技能显示名称`、`你的平台名` 等占位内容。
|
||||
2. 全局替换 `your-skill-slug`、`your-platform-key`、`您的技能显示名称` 等占位内容。
|
||||
3. 修改 `SKILL.md`、`references/` 和 `scripts/util/constants.py`。
|
||||
4. 在 `scripts/service/` 中补业务 service 与真正的发布/执行逻辑。
|
||||
5. 用 `python scripts/main.py health` 和 `python scripts/main.py version` 做最小验证。
|
||||
4. 在 `scripts/service/task_service.py` 中实现真实业务编排与外部系统对接。
|
||||
5. 运行 `python tests/run_tests.py -v`,再执行 `python scripts/main.py health`、`version` 做最小验证。
|
||||
|
||||
如果你的技能在平台里默认是非公开的(`access_scope = 0`),建议在 `SKILL.md` 的 `metadata.openclaw.developer_ids` 中填写开发者用户 ID 列表。这样发布后,平台会自动给这些开发者补可见权限,避免“技能已发布但开发者自己在市场中看不到”。
|
||||
如果你的技能在平台里默认是非公开的(`access_scope = 0`),建议在 `SKILL.md` 的 `metadata.openclaw.developer_ids` 中填写开发者用户 ID 列表。这样在正式发布后,平台会为这些开发者补可见权限,避免「技能已上架但开发者自己在市场中看不到」。
|
||||
|
||||
开发教程入口:
|
||||
|
||||
- <a href="references/REQUIREMENTS.md" target="_blank" rel="noopener noreferrer">需求文档模板</a>:给技术人员编写和查看研发需求的标准模板
|
||||
- <a href="references/DEVELOPMENT.md" target="_blank" rel="noopener noreferrer">开发教程</a>:给技术人员的完整开发步骤说明
|
||||
- <a href="references/TESTING.md" target="_blank" rel="noopener noreferrer">测试开发指南</a>:默认套件、隔离数据根、档位开关与 FakeAdapter 怎么用
|
||||
|
||||
## 目录说明
|
||||
|
||||
@@ -32,7 +34,7 @@
|
||||
| `assets/` | 示例输出与轻量 schema |
|
||||
| `references/` | 面向用户与编排的文档模板 |
|
||||
| `scripts/` | 规范分层后的代码骨架 |
|
||||
| `tests/` | 单元测试或最小回归测试 |
|
||||
| `tests/` | 单元测试与分层测试范式(默认根目录 unittest) |
|
||||
| `evals/` | 人工/半自动评估材料 |
|
||||
| `.github/workflows/release_skill.yaml` | 标准发布工作流 |
|
||||
| `release.ps1` | 对齐现有 skill 的发布脚本入口 |
|
||||
@@ -48,4 +50,5 @@ python scripts/main.py version
|
||||
|
||||
- 不要再往模板里引入旧式 `docs/` 或 `optional/` 目录。
|
||||
- 新技能若不需要某些目录,也建议先保留结构,再按实际业务填充内容。
|
||||
- `metadata.openclaw.developer_ids` 是发布元数据,不是用户展示文案。它的作用是让发布后的非公开技能自动授权给指定开发者查看。
|
||||
- `metadata.openclaw.developer_ids` 是发布元数据,不是用户展示文案;用于非公开技能的开发者默认可见授权。
|
||||
- **发起 `release.ps1` 之前**,务必确认 `python tests/run_tests.py -v` 已全部通过。
|
||||
|
||||
8
SKILL.md
8
SKILL.md
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: 技能开发模板(复制后请修改)
|
||||
description: "这是 OpenClaw 技能开发模板仓库,不直接作为业务技能发布。复制为新技能仓库后,按本模板替换 slug、名称、说明、CLI 子命令与 service 实现。"
|
||||
name: 技能开发模板(通用业务版)
|
||||
description: "OpenClaw 通用业务技能开发模板,覆盖任务执行、日志记录、外部系统对接、隔离测试等典型能力。复制后请按 references/DEVELOPMENT.md 完成定制。"
|
||||
version: 1.0.13
|
||||
author: 深圳匠厂科技有限公司
|
||||
metadata:
|
||||
@@ -22,7 +22,7 @@ allowed-tools:
|
||||
## 模板使用方式
|
||||
|
||||
1. 复制目录为你的新 skill 仓库。
|
||||
2. 全局替换 `your-skill-slug`、`技能开发模板(复制后请修改)` 等占位词。
|
||||
2. 全局替换 `your-skill-slug`、`技能开发模板(通用业务版)` 等占位词。
|
||||
3. 按 `references/CLI.md`、`scripts/` 分层与 `README.md` 的说明补业务逻辑。
|
||||
|
||||
## 目录约定
|
||||
@@ -43,6 +43,6 @@ python {baseDir}/scripts/main.py version
|
||||
- 复制后请同步修改 `scripts/util/constants.py` 中的 `SKILL_SLUG` / `SKILL_VERSION`。
|
||||
- 如技能无需持久化,可保留 `db/` 目录但不主动调用。
|
||||
- `metadata.openclaw.developer_ids` 用于声明技能发布后的默认开发者可见用户 ID 列表。
|
||||
- 当技能在平台中 `access_scope = 0`(不公开)时,发布流程会把 `developer_ids` 中的用户自动补写到 `skill_user_access`,使这些开发者仍可在技能市场中查看该技能。
|
||||
- 当技能在平台中 `access_scope = 0`(不公开)时,任务执行流程会把 `developer_ids` 中的用户自动补写到 `skill_user_access`,使这些开发者仍可在技能市场中查看该技能。
|
||||
- `developer_ids` 建议写为正整数数组;第一个 ID 会作为主开发者同步到 `skills.developer_id`。
|
||||
- 面向用户与编排的文档写在 `references/`,不要再新增旧式 `docs/` / `optional/` 结构。
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# skill-template CLI 模板
|
||||
# 通用业务技能模板 CLI 模板
|
||||
|
||||
将 `{baseDir}` 替换为技能根目录(含 `SKILL.md`、`scripts/` 的目录)。所有命令通过 `python {baseDir}/scripts/main.py` 调用。
|
||||
|
||||
@@ -9,28 +9,38 @@ python {baseDir}/scripts/main.py health
|
||||
python {baseDir}/scripts/main.py version
|
||||
```
|
||||
|
||||
## 若你的技能是发布型
|
||||
## 通用业务命令骨架
|
||||
|
||||
建议继续扩展这些子命令:
|
||||
模板默认提供以下命令(复制后按需保留或扩展):
|
||||
|
||||
```bash
|
||||
python {baseDir}/scripts/main.py publish
|
||||
python {baseDir}/scripts/main.py publish -a <账号id> -i <文章id>
|
||||
python {baseDir}/scripts/main.py run
|
||||
python {baseDir}/scripts/main.py run -t <target> -i <input_id>
|
||||
python {baseDir}/scripts/main.py logs
|
||||
python {baseDir}/scripts/main.py logs --task-type disburse --status failed
|
||||
python {baseDir}/scripts/main.py log-get <log_id>
|
||||
```
|
||||
|
||||
含义:
|
||||
- `run`:执行一次任务(具体语义由你的 skill 决定)
|
||||
- `logs`:列出最近 N 条任务日志,可按 task_type / status / target 过滤
|
||||
- `log-get`:按 log_id 查看单条任务日志(JSON)
|
||||
|
||||
## 若你的技能是发布类
|
||||
|
||||
可以把 `run` 命令对外别名为 `publish`,参数 `--target` 别名为 `--account-id`,参数 `--input-id` 保持不变。
|
||||
|
||||
## 若你的技能依赖兄弟技能
|
||||
|
||||
并列技能与 `{baseDir}` 同级时,兄弟技能路径写为:
|
||||
使用 `service.sibling_bridge.call_sibling_json(skill_slug, args)`,不要硬编码兄弟技能名到 sibling_bridge.py 自身。
|
||||
|
||||
```bash
|
||||
python {baseDir}/../content-manager/scripts/main.py ...
|
||||
python {baseDir}/../account-manager/scripts/main.py ...
|
||||
# 假设 base 与兄弟技能同级
|
||||
python {baseDir}/../<sibling-skill-slug>/scripts/main.py ...
|
||||
```
|
||||
|
||||
## 模板约定
|
||||
|
||||
- 最小模板至少保留 `health` / `version`
|
||||
- 发布型技能建议使用 `publish` / `logs` / `log-get`
|
||||
- 不要再用旧模板的 `scripts/skill_main.py`
|
||||
- 通用业务技能建议使用 `run` / `logs` / `log-get`
|
||||
- 不要再用旧模板的 `publish` / `--account-id` / `--article-id` 命名
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
这份文档是给**技术人员**看的,目标不是解释概念,而是让你拿到 `skill-template` 后,可以**一步一步开发出一个新的 skill**。
|
||||
|
||||
本文默认你开发的是当前最常见的一类技能:
|
||||
本文默认你开发的是当前最常见的一类业务 skill:
|
||||
|
||||
- 有明确的 `scripts/main.py` CLI 入口
|
||||
- 可能需要读写本地 SQLite
|
||||
- 可能需要调用兄弟技能
|
||||
- 可能需要读写本地 SQLite(例如任务日志 `task_logs`)
|
||||
- 可能需要调用兄弟技能或外部 HTTP / RPA
|
||||
- 业务逻辑主要放在 `scripts/service/`
|
||||
|
||||
如果你开发的是发布型 skill,这个模板就是直接可用的起点。
|
||||
发布、对账、代发、报表分析等场景都只是业务特例;模板提供通用骨架,复制后按领域补齐实现即可。
|
||||
|
||||
## 推荐 AI 开发工具
|
||||
|
||||
@@ -145,7 +145,7 @@ scripts/
|
||||
|
||||
- `service/`
|
||||
作用:核心业务逻辑
|
||||
比如发布流程、调用兄弟技能、浏览器自动化
|
||||
比如任务编排、调用兄弟技能、外部 API、可选浏览器自动化
|
||||
|
||||
- `util/`
|
||||
作用:常量、日志、路径、时间工具、通用帮助函数
|
||||
@@ -160,10 +160,10 @@ scripts/
|
||||
|
||||
### 第一步:复制模板并改目录名
|
||||
|
||||
例如你要开发 `weibo-publisher`:
|
||||
例如你要开发 `your-skill-slug`(或 `disburse-payroll-icbc` 一类领域 skill):
|
||||
|
||||
1. 复制 `skill-template`
|
||||
2. 新目录改成 `weibo-publisher`
|
||||
2. 新目录改成与 `slug` 一致的名称(如 `your-skill-slug`)
|
||||
3. 初始化为独立 git 仓库
|
||||
4. 关联它自己的远端仓库
|
||||
|
||||
@@ -176,7 +176,7 @@ scripts/
|
||||
1. `SKILL.md`
|
||||
2. `scripts/util/constants.py`
|
||||
3. `references/` 下的文案
|
||||
4. `scripts/service/` 下的平台占位文件名
|
||||
4. `scripts/service/` 下的业务占位实现(优先改 `task_service.py`)
|
||||
|
||||
最先要统一的是:
|
||||
|
||||
@@ -193,18 +193,19 @@ scripts/
|
||||
复制后,至少要全局检查并替换下面这些内容:
|
||||
|
||||
- `your-skill-slug`
|
||||
- `your-platform-key`
|
||||
- `技能开发模板(复制后请修改)`
|
||||
- `your-platform-key`(若 skill 涉及外部平台对接,可能会用到这类占位)
|
||||
- `技能开发模板(通用业务版)`
|
||||
- `你的平台名`
|
||||
- `platform_playwright.py`
|
||||
- `openclaw.skill.your_skill_slug`
|
||||
|
||||
如果你是做发布型 skill,通常还要替换:
|
||||
如果你的 skill 对外 CLI 需要自定义文案或别名(例如发布类对外仍叫 `publish`),通常还要替换:
|
||||
|
||||
- `publish` 命令中的中文提示
|
||||
- `run` 命令中的中文提示与别名策略(见 `references/CLI.md`)
|
||||
- `references/CLI.md` 的命令示例
|
||||
- `references/README.md` 的用户话术
|
||||
- `references/SCHEMA.md` 的数据库文件名
|
||||
- `references/SCHEMA.md` 的字段映射补充说明
|
||||
|
||||
如需浏览器自动化,不要在模板里保留空的 `platform_playwright.py`;请按业务新建 `xxx_playwright.py`(命名自定),并在 `task_service.py` 中按需引用。
|
||||
|
||||
## 6. `SKILL.md` 应该怎么写
|
||||
|
||||
@@ -216,7 +217,7 @@ scripts/
|
||||
- 技能描述
|
||||
- `slug`
|
||||
作用:技能的唯一英文标识,通常用于仓库名、发布包名、运行时目录名、平台主键匹配等
|
||||
示例:`weibo-publisher`、`douyin-publisher`、`xiaohongshu-publisher`
|
||||
示例:`your-skill-slug`、`disburse-payroll-icbc`、`tax-invoice-verify`
|
||||
- `category`
|
||||
作用:技能在平台中的分类,用于市场展示与归类,不是代码内部键
|
||||
示例:`通用`、`内容`、`办公协作`、`物流`、`抖音`、`小红书`
|
||||
@@ -224,12 +225,11 @@ scripts/
|
||||
作用:声明发布后默认拥有可见权限的开发者用户 ID 列表
|
||||
- `dependencies`
|
||||
作用:声明该技能依赖的兄弟技能或运行前置能力,便于平台或编排层识别依赖关系
|
||||
示例:
|
||||
示例(按业务填写;发布类可能会依赖账号 / 内容技能,其它领域可能完全不同):
|
||||
```yaml
|
||||
dependencies:
|
||||
required:
|
||||
- account-manager
|
||||
- content-manager
|
||||
- some-sibling-skill
|
||||
```
|
||||
- 何时使用本技能
|
||||
- 对用户的引导话术
|
||||
@@ -288,6 +288,9 @@ metadata:
|
||||
- `DEVELOPMENT.md`
|
||||
写给技术人员的开发教程,也就是本文档
|
||||
|
||||
- `TESTING.md`
|
||||
写给技术人员的测试分层、隔离数据根与档位开关指南(详见 [`references/TESTING.md`](TESTING.md))
|
||||
|
||||
如果后面某个 skill 需要更细的说明,可以再加:
|
||||
|
||||
- `ERRORS.md`
|
||||
@@ -319,8 +322,8 @@ metadata:
|
||||
也就是说,`cli/app.py` 的职责是:
|
||||
|
||||
1. 打印帮助
|
||||
2. 定义 `publish / logs / log-get / health / version`
|
||||
3. 把参数转交给 `service.publish_service`
|
||||
2. 定义 `run / logs / log-get / health / version`
|
||||
3. 把参数转交给 `service.task_service`
|
||||
|
||||
不要在 `cli/app.py` 里直接写:
|
||||
|
||||
@@ -334,14 +337,14 @@ metadata:
|
||||
|
||||
通常可以这样拆:
|
||||
|
||||
- `publish_service.py`
|
||||
放命令编排、参数兜底、结果分流
|
||||
- `task_service.py`
|
||||
放命令编排、参数兜底、结果分流(例如 `cmd_run`)
|
||||
|
||||
- `sibling_bridge.py`
|
||||
放兄弟技能调用,例如调 `account-manager`、`content-manager`
|
||||
放通用兄弟技能子进程工具(`call_sibling_json`);**具体调用哪个 slug 由业务在 `task_service.py` 决定**,不要在本文件硬编码发布类专用 helper
|
||||
|
||||
- `*_playwright.py`
|
||||
放浏览器后台自动化
|
||||
- `xxx_playwright.py`(按需新建)
|
||||
浏览器后台自动化 **不在模板默认结构中**;若确有需要,按命名约定自建模块并在 `task_service.py` 引用
|
||||
|
||||
- `entitlement_service.py`
|
||||
放鉴权逻辑
|
||||
@@ -352,7 +355,7 @@ metadata:
|
||||
|
||||
推荐流向是:
|
||||
|
||||
`cli.app` -> `service.publish_service` -> `service.sibling_bridge` / `service.xxx_playwright` -> `db`
|
||||
`cli.app` -> `service.task_service` -> `service.sibling_bridge` / (可选)`service.xxx_playwright` -> `db`
|
||||
|
||||
## 11. `db` 层怎么写
|
||||
|
||||
@@ -361,8 +364,8 @@ metadata:
|
||||
- `db/connection.py`
|
||||
只做连接和建表
|
||||
|
||||
- `db/publish_logs_repository.py`
|
||||
只做增删查改
|
||||
- `db/task_logs_repository.py`
|
||||
只做增删查改(模板默认表:`task_logs`)
|
||||
|
||||
不要在 `db` 层里:
|
||||
|
||||
@@ -381,10 +384,7 @@ metadata:
|
||||
|
||||
来做调用。
|
||||
|
||||
常见调用对象是:
|
||||
|
||||
- `account-manager`
|
||||
- `content-manager`
|
||||
调用哪些兄弟技能由具体业务决定;请在 `task_service.py` 中使用 `service.sibling_bridge.call_sibling_json(skill_slug, args)` 或 `get_sibling_main_path(skill_slug)`,不要在本模板仓库的 `sibling_bridge.py` 中堆积特定技能函数。
|
||||
|
||||
调用原则:
|
||||
|
||||
@@ -413,7 +413,7 @@ metadata:
|
||||
- 日志或本地状态能落下来
|
||||
- 错误返回值格式定好了
|
||||
|
||||
如果你的 skill 恰好是 publisher 类,可以把上面的“核心 `service`”具体落成 `publish_service.py`,再逐步接 `sibling_bridge.py`、`*_playwright.py`。但这只是示例,不代表模板只适合发布类技能。
|
||||
如果你的 skill 属于外联型 / RPA 型,可以把上面的“核心 `service`”具体落成 `task_service.py`,再按需接 `sibling_bridge.py`、(可选)`*_playwright.py`。这只是示例路径组合,不代表模板绑定某一业务领域。
|
||||
|
||||
## 14. 本地开发的最小验证顺序
|
||||
|
||||
@@ -442,7 +442,7 @@ python scripts/main.py <your-log-command>
|
||||
python scripts/main.py <your-detail-command> <id>
|
||||
```
|
||||
|
||||
如果你沿用了模板中的发布型骨架,那么这里可以具体对应成:
|
||||
如果你沿用了模板中的通用任务日志骨架,那么这里可以具体对应成:
|
||||
|
||||
```bash
|
||||
python scripts/main.py logs
|
||||
@@ -457,8 +457,22 @@ python scripts/main.py log-get 1
|
||||
python scripts/main.py <your-command>
|
||||
```
|
||||
|
||||
## 14.5 测试驱动的开发顺序
|
||||
|
||||
新 skill 不应该等所有业务做完才补测试。建议在每个开发阶段都先把对应测试跑一遍:
|
||||
|
||||
1. **目录搭建后**:跑 `python tests/run_tests.py -v`,确认 6 个默认测试套件全部通过。
|
||||
- 此时 `test_skill_metadata.py` 会校验 `SKILL.md` slug 与 `constants.SKILL_SLUG` 一致;如果你只改了一边,会立刻发现。
|
||||
2. **改完 constants 后**:再跑一次必跑套件,确认未引入回归。
|
||||
3. **写完 service 业务后**:从 `tests/samples/test_service_contract.py.sample` 复制一份做契约测试。
|
||||
4. **接外部系统时**:写在 `tests/integration/`,并配合 `OPENCLAW_TEST_TARGET` 显式开启。
|
||||
|
||||
详见 `references/TESTING.md`。
|
||||
|
||||
## 15. 发布到正式环境验证
|
||||
|
||||
在执行 `release.ps1` 之前,必须先在本地确认 `python tests/run_tests.py -v` 通过。测试不通过不得发起正式发布。
|
||||
|
||||
当本地开发、自测和联调完成后,还需要把 skill 发布到正式环境做一次完整验证。建议技术人员严格按下面顺序执行,不要跳步。
|
||||
|
||||
### 第一步:在 skill 根目录执行 `release.ps1`
|
||||
@@ -574,12 +588,15 @@ python scripts/main.py <your-command>
|
||||
- [ ] `SKILL.md` 中 slug、名称、描述都已替换
|
||||
- [ ] `scripts/util/constants.py` 已修改
|
||||
- [ ] `references/CLI.md` 示例命令已改成真实命令
|
||||
- [ ] `service` 下的平台文件名已改对
|
||||
- [ ] `service` 下的核心业务文件(如 `task_service.py`)已按领域改名并实现
|
||||
- [ ] 没有残留旧平台名
|
||||
- [ ] `health` / `version` 可运行
|
||||
- [ ] `.gitignore` 生效,没有把 `__pycache__` 提交进去
|
||||
- [ ] `release.ps1` 存在
|
||||
- [ ] `.github/workflows/release_skill.yaml` 存在
|
||||
- [ ] `python tests/run_tests.py -v` 全部通过
|
||||
- [ ] 没有新增默认必跑测试访问真实网络或浏览器
|
||||
- [ ] 如有 integration 测试需求,已写在 `tests/integration/` 下并保持 `.sample` 后缀
|
||||
|
||||
## 17. 常见错误
|
||||
|
||||
@@ -601,11 +618,11 @@ python scripts/main.py <your-command>
|
||||
表现:
|
||||
|
||||
- 兄弟技能筛选账号失败
|
||||
- 发布命令走错平台
|
||||
- `run` 命令走错平台或 task_type
|
||||
|
||||
要检查:
|
||||
|
||||
- `publish_service.py`
|
||||
- `task_service.py`
|
||||
- `sibling_bridge.py`
|
||||
- `references/CLI.md`
|
||||
|
||||
@@ -644,9 +661,10 @@ python scripts/main.py <your-command>
|
||||
4. 改 `references/`
|
||||
5. 改 `scripts/cli/app.py`
|
||||
6. 改 `scripts/service/`
|
||||
7. 跑 `health` / `version`
|
||||
8. 再做业务联调
|
||||
9. 最后 release
|
||||
7. 跑 `python tests/run_tests.py -v`
|
||||
8. 跑 `health` / `version`
|
||||
9. 再做业务联调
|
||||
10. 最后 release
|
||||
|
||||
## 19. 这份模板的底线要求
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
description: "这是规范化的新技能模板说明,不直接作为业务技能使用。复制后请替换技能名、平台名、CLI 示例与 service 实现。"
|
||||
description: "规范化的新技能模板说明;复制后替换名称、CLI 示例与 task_service 等业务实现。"
|
||||
---
|
||||
|
||||
# 技能模板说明
|
||||
@@ -9,9 +9,10 @@ description: "这是规范化的新技能模板说明,不直接作为业务技
|
||||
## 它提供什么
|
||||
|
||||
- 标准目录结构
|
||||
- 最小 CLI 入口
|
||||
- 发布型技能常见的日志表骨架
|
||||
- `service` 层占位模块
|
||||
- 最小 CLI 入口(`run` / `logs` / `log-get` / `health` / `version`)
|
||||
- 通用任务日志表骨架(`task_logs`)
|
||||
- `service` 层占位模块(`task_service.py`、通用 `sibling_bridge.py`)
|
||||
- 成熟的隔离测试体系(IsolatedDataRoot、FakeAdapter、6 档测试 target、profile 策略)
|
||||
- 与现有规范 skill 一致的发布脚本与 GitHub workflow
|
||||
|
||||
## 复制后你需要改什么
|
||||
@@ -20,7 +21,8 @@ description: "这是规范化的新技能模板说明,不直接作为业务技
|
||||
- `SKILL.md` 中 `metadata.openclaw.developer_ids`(如需让非公开技能默认授权给开发者查看)
|
||||
- `references/CLI.md` 里的命令示例
|
||||
- `scripts/util/constants.py` 中的 slug / 版本 / logger 名
|
||||
- `scripts/service/` 下的真实业务实现
|
||||
- `scripts/service/task_service.py` 中的 `cmd_run` 真实业务逻辑
|
||||
- 如有特定 task_type,在 `references/SCHEMA.md` 中补充字段映射说明
|
||||
|
||||
## `developer_ids` 是做什么的
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
示例:
|
||||
|
||||
- 本文档用于明确 `weibo-publisher` 的研发需求,约束该 skill 的功能边界、输入输出和验收标准,作为开发、测试和上线验证的统一依据。
|
||||
- 本文档用于明确 `your-skill-slug`(示例:`disburse-payroll-icbc`)的研发范围、交付标准、依赖关系和验收要求。
|
||||
|
||||
## 2. 业务背景
|
||||
|
||||
@@ -40,8 +40,8 @@
|
||||
|
||||
示例:
|
||||
|
||||
- 当前内容运营团队需要将内容库中的文章批量发布到微博平台,但人工登录后台、复制文章、填写标题和确认发布的流程效率较低,且不便于统一记录发布历史。
|
||||
- 因此需要开发 `weibo-publisher`,用于把内容库文章按规范发布到微博平台,并记录发布结果,支持后续排查与复用。
|
||||
- 当前财务团队需要将工资代发批次与银行接口对账,人工导出表格、核对差异耗时且易错。
|
||||
- 因此需要开发 `disburse-payroll-icbc`,用于自动拉取代发结果、写入任务日志 `task_logs` 并输出对账摘要,支持审计追踪。
|
||||
|
||||
## 3. 开发目标
|
||||
|
||||
@@ -57,10 +57,10 @@
|
||||
|
||||
示例:
|
||||
|
||||
- 完成 `weibo-publisher` 的标准 skill 结构建设
|
||||
- 提供 `health`、`version`、`publish`、`logs`、`log-get` 等命令
|
||||
- 支持从兄弟技能读取账号与文章,并执行微博发布流程
|
||||
- 支持记录发布日志,并可在正式环境中安装验证
|
||||
- 完成 `your-skill-slug` 的标准 skill 结构建设
|
||||
- 提供 `health`、`version`、`run`、`logs`、`log-get` 等命令
|
||||
- 支持对接银行 / 薪酬系统 API(示例),并记录任务执行结果
|
||||
- 支持写入任务日志 `task_logs`,并可在正式环境中安装验证
|
||||
|
||||
## 4. 功能范围
|
||||
|
||||
@@ -82,17 +82,16 @@
|
||||
|
||||
- 支持 `python scripts/main.py health`
|
||||
- 支持 `python scripts/main.py version`
|
||||
- 支持 `python scripts/main.py publish`
|
||||
- 支持从 `account-manager` 获取可用账号
|
||||
- 支持从 `content-manager` 获取待发布文章
|
||||
- 支持执行微博后台发布流程
|
||||
- 支持写入发布日志
|
||||
- 支持 `python scripts/main.py run`
|
||||
- 支持从 `some-sibling-skill` 读取批次元数据(如需要)
|
||||
- 支持执行核心业务编排(HTTP / 批处理 / 可选 RPA)
|
||||
- 支持写入任务日志(`task_logs`)
|
||||
|
||||
### 可后续扩展
|
||||
|
||||
- 支持定时发布
|
||||
- 支持定时批跑
|
||||
- 支持失败自动重试
|
||||
- 支持多账号轮询发布
|
||||
- 支持多目标轮询
|
||||
|
||||
## 5. 非功能要求
|
||||
|
||||
@@ -112,7 +111,7 @@
|
||||
- 项目结构必须对齐 `skill-template`
|
||||
- CLI 入口统一使用 `scripts/main.py`
|
||||
- 关键执行结果应可通过 JSON 或固定文本结构返回
|
||||
- 发布日志必须可追踪
|
||||
- 任务日志必须可追踪(`task_logs`)
|
||||
- 不允许重新引入旧模板中的 `docs/`、`optional/` 或 `scripts/skill_main.py`
|
||||
|
||||
## 6. 输入输出要求
|
||||
@@ -138,16 +137,15 @@
|
||||
|
||||
### 输入
|
||||
|
||||
- `account_id`:可选,指定发布账号
|
||||
- `article_id`:可选,指定发布文章
|
||||
- `account-manager` 返回的账号信息
|
||||
- `content-manager` 返回的文章信息
|
||||
- `target_id`:可选,指定任务目标(账号 / 客户 / 平台)
|
||||
- `input_id`:可选,指定输入批次或记录
|
||||
- 兄弟技能返回的结构化数据(如有)
|
||||
|
||||
### 输出
|
||||
|
||||
- 发布成功时返回成功结果
|
||||
- 发布失败时返回 `ERROR:` 或 `FAIL:` 前缀的错误信息
|
||||
- 在本地数据库中写入 `publish_logs`
|
||||
- 成功结果(stdout / JSON)
|
||||
- 错误输出(`ERROR:` / `FAIL:`)
|
||||
- 在本地数据库写入 `task_logs`
|
||||
- 在日志目录中写入执行日志
|
||||
|
||||
## 7. 依赖的兄弟技能或外部系统
|
||||
@@ -170,13 +168,12 @@
|
||||
|
||||
### 兄弟技能依赖
|
||||
|
||||
- `account-manager`:提供账号信息
|
||||
- `content-manager`:提供文章数据
|
||||
- `some-sibling-skill`:提供主数据或凭证索引(按业务填写)
|
||||
|
||||
### 外部系统依赖
|
||||
|
||||
- 微博后台:目标发布平台
|
||||
- Chromium / Chrome / Edge:用于浏览器自动化
|
||||
- 银行 / 政务 HTTP API(示例):核心外部接口
|
||||
- Chromium / Chrome / Edge(可选):用于浏览器自动化
|
||||
- Gitea:用于代码托管和发布工作流
|
||||
- 匠厂客户端:用于正式环境安装与验证
|
||||
|
||||
@@ -193,8 +190,8 @@
|
||||
|
||||
示例:
|
||||
|
||||
- 本次不实现多平台统一发布
|
||||
- 本次不实现定时任务调度
|
||||
- 本次不实现多租户隔离之外的附加报表
|
||||
- 本次不实现实时流式处理
|
||||
- 本次不实现自动重试机制
|
||||
- 本次不兼容旧模板中的历史目录结构
|
||||
|
||||
@@ -214,12 +211,16 @@
|
||||
示例:
|
||||
|
||||
- `health`、`version` 命令执行正常
|
||||
- `publish` 主流程可运行
|
||||
- `run` 主流程可运行
|
||||
- 发布后 Gitea 工作流成功
|
||||
- 匠厂技能市场能看到最新版本
|
||||
- skill 可以正常安装
|
||||
- 安装后可在“新建任务”中调用
|
||||
|
||||
- `python tests/run_tests.py -v` 必跑测试全部通过
|
||||
- 任务日志(task_logs)写入和查询符合预期
|
||||
- 真实联调(如有)已在 tests/integration/ 中验证,且默认套件不包含真实外联
|
||||
|
||||
## 10. 开发注意事项
|
||||
|
||||
说明研发过程中必须特别注意的事项。
|
||||
@@ -258,7 +259,7 @@
|
||||
|
||||
1. 先写 `references/REQUIREMENTS.md`
|
||||
2. 再按 `references/DEVELOPMENT.md` 进入开发
|
||||
3. 开发过程中补充 `CLI.md`、`SCHEMA.md`、`RUNTIME.md`
|
||||
3. 开发过程中补充 `CLI.md`、`SCHEMA.md`、`RUNTIME.md`、`TESTING.md`
|
||||
4. 发布前回到 `REQUIREMENTS.md` 对照验收标准逐项检查
|
||||
|
||||
## 最小模板示例
|
||||
@@ -270,24 +271,24 @@
|
||||
|
||||
## 1. 文档目标
|
||||
|
||||
本文档用于明确 `weibo-publisher` 的研发需求、范围和验收标准,作为开发与测试的统一依据。
|
||||
本文档用于明确 `your-skill-slug` 的研发需求、范围和验收标准,作为开发与测试的统一依据。
|
||||
|
||||
## 2. 业务背景
|
||||
|
||||
运营团队需要将内容库中的文章发布到微博平台,现有人工操作效率低,且缺少统一的发布记录。
|
||||
采购审计需要将政府采购公告结构化归档;人工复制粘贴效率低,需要可追溯的任务日志。
|
||||
|
||||
## 3. 开发目标
|
||||
|
||||
- 完成 `weibo-publisher` 的标准 skill 结构
|
||||
- 支持文章发布主流程
|
||||
- 支持发布日志记录
|
||||
- 完成 `your-skill-slug` 的标准 skill 结构
|
||||
- 支持核心业务批处理 / HTTP 对接主流程
|
||||
- 支持任务日志记录(task_logs)
|
||||
|
||||
## 4. 功能范围
|
||||
|
||||
- 支持 `health`
|
||||
- 支持 `version`
|
||||
- 支持 `publish`
|
||||
- 支持查询发布日志
|
||||
- 支持 `run`
|
||||
- 支持查询任务日志(logs / log-get)
|
||||
|
||||
## 5. 非功能要求
|
||||
|
||||
@@ -299,29 +300,30 @@
|
||||
|
||||
### 输入
|
||||
|
||||
- `account_id`
|
||||
- `article_id`
|
||||
- `target_id`
|
||||
- `input_id`
|
||||
|
||||
### 输出
|
||||
|
||||
- 成功结果
|
||||
- 错误信息
|
||||
- 发布日志记录
|
||||
- task_logs 记录
|
||||
|
||||
## 7. 依赖的兄弟技能或外部系统
|
||||
|
||||
- `account-manager`
|
||||
- `content-manager`
|
||||
- 微博后台
|
||||
- (按需填写兄弟 skill)
|
||||
- 政务公告站点 HTTP API(示例)
|
||||
|
||||
## 8. 不在本次范围内的内容
|
||||
|
||||
- 不实现定时发布
|
||||
- 不实现浏览器可视化回放(如需另行立项)
|
||||
- 不实现自动重试
|
||||
|
||||
## 9. 验收标准
|
||||
|
||||
- 命令可运行
|
||||
- `python tests/run_tests.py -v` 全部通过
|
||||
- task_logs 写入符合 SCHEMA 约定
|
||||
- 能完成正式环境安装验证
|
||||
- 能在新建任务中使用
|
||||
|
||||
|
||||
@@ -33,6 +33,17 @@
|
||||
{...}/{skill_slug}.db
|
||||
```
|
||||
|
||||
## 测试时的运行时隔离
|
||||
|
||||
本模板的 `tests/_support.IsolatedDataRoot` 会在测试期间同时设置:
|
||||
|
||||
- `CLAW_DATA_ROOT` / `JIANGCHANG_DATA_ROOT` → 临时 tempfile 目录
|
||||
- `CLAW_USER_ID` / `JIANGCHANG_USER_ID` → `_test`
|
||||
|
||||
退出时四个变量恢复,临时目录删除。所有 DB / 文件写入都被限制在临时目录内,不污染本机。
|
||||
|
||||
详见 `references/TESTING.md` 第 3 节。
|
||||
|
||||
## 编码与输出
|
||||
|
||||
- Windows 终端建议在 `scripts/main.py` 里做 UTF-8 stdout/stderr 包装
|
||||
|
||||
@@ -4,20 +4,35 @@
|
||||
|
||||
`{DATA_ROOT}/{USER_ID}/your-skill-slug/your-skill-slug.db`
|
||||
|
||||
## 发布型技能推荐日志表
|
||||
## 通用任务日志表(task_logs)
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `id` | 自增主键 |
|
||||
| `account_id` | 账号 id |
|
||||
| `article_id` | 文章 id |
|
||||
| `article_title` | 标题快照 |
|
||||
| `status` | `published` / `failed` / `require_login` |
|
||||
| `error_msg` | 错误说明 |
|
||||
| `created_at` | Unix 时间戳 |
|
||||
| `updated_at` | Unix 时间戳 |
|
||||
模板默认建表:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | INTEGER PK | 自增主键 |
|
||||
| `task_type` | TEXT NOT NULL | 任务类型(disburse / reconcile / verify ...) |
|
||||
| `target_id` | TEXT | 任务目标(账号、平台、客户等的 ID) |
|
||||
| `input_id` | TEXT | 输入对象 ID |
|
||||
| `input_title` | TEXT | 输入对象标题快照 |
|
||||
| `status` | TEXT NOT NULL | success / failed / partial / require_login 等 |
|
||||
| `error_msg` | TEXT | 错误说明 |
|
||||
| `result_summary` | TEXT | 结果摘要(建议存 JSON 字符串) |
|
||||
| `created_at` | INTEGER | Unix 时间戳 |
|
||||
| `updated_at` | INTEGER | Unix 时间戳 |
|
||||
|
||||
## 不同业务的字段映射建议
|
||||
|
||||
| 业务场景 | task_type | target_id 含义 | input_id 含义 |
|
||||
|---|---|---|---|
|
||||
| 发布类 | publish | 账号 ID | 文章 ID |
|
||||
| 工资代发 | disburse | 付款账户 | 工资表批次 ID |
|
||||
| 对账 | reconcile | 银行 / 平台 | 对账批次 ID |
|
||||
| 发票验真 | verify | 税务地区 | 发票批次 ID |
|
||||
| 报关 | declare | 港口 / 海关 | 报关批次 ID |
|
||||
|
||||
## 模板原则
|
||||
|
||||
- 模板不做历史迁移兼容设计
|
||||
- 新 skill 直接从当前 schema 起步
|
||||
- 业务有特殊字段时,建议放 `result_summary`(JSON 字符串),不要乱加列
|
||||
|
||||
185
references/TESTING.md
Normal file
185
references/TESTING.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# 测试开发指南
|
||||
|
||||
面向复制 `skill-template` 后的新业务 skill:**如何把自动化测试当作一等公民**,而不是等业务写完再补文档级别的空话。本文串起模板自带的 unittest 入口、`tests/` 目录分层与安全档位约定;**更细的开关取值、表格字段与环境变量组合仍以 [`tests/README.md`](../tests/README.md) 为权威来源**。建议你随手开一个编辑器分页:`references/TESTING.md`(本篇)、[`references/DEVELOPMENT.md`](DEVELOPMENT.md)(整体节奏)、[`tests/README.md`](../tests/README.md)(落地细则)。
|
||||
|
||||
默认心智模型可以用一句话概括:**根目录 `test_*.py` = CI / 本地每次提交都应能通过的无外联套件**;`*integration*`、`*.sample`、`desktop/` = 只在人被明确要求时才启用的高风险或重量级路径。
|
||||
|
||||
---
|
||||
|
||||
## 1. 测试体系总览
|
||||
|
||||
本模板把测试分成四层漏斗:**默认必跑(unittest + `run_tests.py`)**、从 `tests/samples/` **按需复制的 service / golden**、放在 `tests/integration/` **默认不落盘的仿真或真实联调范式(多数仍是 `.sample`)**、以及 **desktop E2E(pytest + 宿主 SDK)**。
|
||||
|
||||
[`tests/run_tests.py`](../tests/run_tests.py) 只做三件事:把 `scripts/` 与 `tests/` 放进 `sys.path`、做 Windows UTF-8 包装、收集 **`tests/` 根目录**下的 `test_*.py`。它不递归子目录——这正是刻意的安全边界:**不想让 AI 或拷贝粘贴 accidentally 把 integration 拉进默认套件**。
|
||||
|
||||
当你在设计一个新 skill 的测试策略时,请先问自己:**这段代码在没有外部凭证与浏览器的前提下是否有意义?** 若有,留在默认套件;若无,放进 integration / `.sample`,并要求明确的 `OPENCLAW_TEST_TARGET` 组合开关。
|
||||
|
||||
更深表格化的目录映射、`FakeAdapter` 与 profile 的耦合细节见 [`tests/README.md`](../tests/README.md) 开头章节『我该把测试写在哪里』。
|
||||
|
||||
---
|
||||
|
||||
## 2. 默认必跑测试要做什么
|
||||
|
||||
必跑套件要像一个紧张的守门员:**快、确定、离线**。典型覆盖:
|
||||
|
||||
- CLI:导入 [`cli.app`](../scripts/cli/app.py) 走解析链路、`health` / `version` / `logs` / `log-get` 冒烟;
|
||||
- **真实 subprocess**:[`tests/test_entrypoint_subprocess.py`](../tests/test_entrypoint_subprocess.py) 再调用一遍 `python scripts/main.py`,防路径漂移;
|
||||
- 运行时:`runtime_paths` 与 **`CLAW_*` / `JIANGCHANG_*` 并发兜底;
|
||||
- `SKILL.md` YAML slug vs [`constants.SKILL_SLUG`](../scripts/util/constants.py);
|
||||
- SQLite 骨架:`task_logs` 创建幂等与仓储读写;
|
||||
- **adapter profile**:[`tests/test_adapter_profile_policy.py`](../tests/test_adapter_profile_policy.py) + [`adapter_test_utils`](../tests/adapter_test_utils.py) ——验证在未授权情况下绝不误判开启真实网络/RPA。
|
||||
|
||||
**原则:`tests/test_*.py` 不允许隐形访问外网、不允许拉起真实浏览器、不允许读写开发者机器的真实数据根**。如果需要仿真服务器,也应仅在 integration(并由档位变量放行)。
|
||||
|
||||
套件能力与表格参见 [`tests/README.md`](../tests/README.md) 「1.2 默认套件覆盖」。
|
||||
|
||||
---
|
||||
|
||||
## 3. 数据隔离:IsolatedDataRoot
|
||||
|
||||
[`tests/_support.py`](../tests/_support.py) 提供的上下文管理器 `IsolatedDataRoot()`:进入一个专用临时目录,**镜像写入四套变量**:`CLAW_DATA_ROOT`、`JIANGCHANG_DATA_ROOT`(同一 tempfile)、以及用户镜像 ID:`CLAW_USER_ID`、`JIANGCHANG_USER_ID` → `_test`。
|
||||
|
||||
结束时:**恢复原 environ**,删除目录。
|
||||
|
||||
这样可以断言:**SQLite DB / spill files / caches** 都在 sandbox;不会在开发者桌面遗留 `{REAL_ROOT}`。
|
||||
|
||||
示例:
|
||||
|
||||
```python
|
||||
from _support import IsolatedDataRoot
|
||||
|
||||
def test_whatever():
|
||||
with IsolatedDataRoot():
|
||||
from db.connection import init_db
|
||||
init_db()
|
||||
# …断言读写均在隔离路径…
|
||||
```
|
||||
|
||||
**不要把真实凭证路径硬编码进默认测试**:隔离不等于你有权触碰真实目录。
|
||||
|
||||
---
|
||||
|
||||
## 4. 测试目标档位(OPENCLAW_TEST_TARGET)
|
||||
|
||||
模板采用统一闸门:**你想跑到哪一层外部世界,就用变量明说**。合法档位(非法值会让 helper 抛错)如下——**直接摘录自 [`tests/README.md`](../tests/README.md) §5.1**:
|
||||
|
||||
| 取值 | 含义 |
|
||||
|------|------|
|
||||
| `unit` | **默认**:单元 / 内存 / mock,不跑仿真与真实外联 |
|
||||
| `mock` | 与 `unit` 类似的安全档位(显式语义) |
|
||||
| `simulator_api` | 仅允许 **仿真 HTTP** 类集成(如 localhost) |
|
||||
| `simulator_rpa` | 仅允许 **仿真页面 / 录播 RPA** |
|
||||
| `real_api` | 真实 API(另需 `ALLOW_REAL_API=1`) |
|
||||
| `real_rpa` | 真实 RPA(另需 `ALLOW_REAL_RPA=1`) |
|
||||
|
||||
未设置环境变量 ⇒ 等价 `unit`。
|
||||
|
||||
授权开关(显式 `1`)语义 **`ALLOW_REAL_API` / `ALLOW_REAL_RPA` / `ALLOW_WRITE_ACTIONS`**——摘录 [`tests/README.md`](../tests/README.md) §5.2:
|
||||
|
||||
| 变量 | 作用 |
|
||||
|------|------|
|
||||
| `ALLOW_REAL_API=1` | 允许 `real_api` profile 访问真实 HTTP 通道 |
|
||||
| `ALLOW_REAL_RPA=1` | 允许 `real_rpa` profile 驱动真实浏览器/RPA |
|
||||
| `ALLOW_WRITE_ACTIONS=1` | 在 `real_*` 下允许**写**操作(提交表单、下单等) |
|
||||
|
||||
默认策略摘要见 §5.3:**不要在 unittest 必跑路径误把闸门打开**。
|
||||
|
||||
兼容别名:`OPENCLOW_TEST_TARGET`(历史拼写)。
|
||||
|
||||
---
|
||||
|
||||
## 5. FakeAdapter:怎么模拟外部系统
|
||||
|
||||
[`tests/adapter_test_utils.py`](../tests/adapter_test_utils.py) 暴露 `FakeAdapter`,典型四种 **mode**:
|
||||
|
||||
| mode | 用途 |
|
||||
|------|------|
|
||||
| `success` | 构造干净的成功响应路径 |
|
||||
| `timeout` | 模拟悬挂 / 慢链路 |
|
||||
| `invalid_response` | 畸形负载 / schema drift |
|
||||
| `unauthorized` | token / license / cookie 失效语义 |
|
||||
|
||||
**何时用它**:service 层出现『调用第三方 HTTP / RPA stub』但又不能把真实系统纳入 CI。**契约测试**(复制 [`tests/samples/test_service_contract.py.sample`](../tests/samples/test_service_contract.py.sample))应优先组合 FakeAdapter,而不是直接把 CLI when-json 断言堆上天。
|
||||
|
||||
把它看成:**你把不确定性折叠进可控表格**,而不是在生产日志里才第一次看到错位字段。
|
||||
|
||||
---
|
||||
|
||||
## 6. 怎么从 .sample 启用一个测试
|
||||
|
||||
步骤模板:
|
||||
|
||||
1. 找到范式文件(例如 [`tests/samples/test_service_contract.py.sample`](../tests/samples/test_service_contract.py.sample))。
|
||||
2. **复制**到 `tests/` 根:`tests/test_service_contract.py`(去掉 `.sample`)。
|
||||
3. 打开副本:**替换占位函数名 /技能特有枚举 / adapter profile**,删掉与本技能无关的示例断言。
|
||||
4. 本地执行:`python tests/run_tests.py -v [可选筛选关键词]`。
|
||||
5. **不要把 integration `.sample` 批量改名混进根目录**——除非你已经读过 [`tests/integration/README.md`](../tests/integration/README.md) 的风险清单。
|
||||
|
||||
Golden fixture 流程同理([`tests/samples/test_golden_cases.py.sample`](../tests/samples/test_golden_cases.py.sample))。
|
||||
|
||||
---
|
||||
|
||||
## 7. 真实联调测试的安全约束
|
||||
|
||||
任何 touching **真实租户数据** 的路径:
|
||||
|
||||
1. **禁止**硬编码 token / cookie / 内部域名落入仓库;
|
||||
2. **禁止**默认套件 silent import integration;
|
||||
3. **真实 RPA** 只能标记为手动触发(双人复核 / 本地 `.env` 不入库)。
|
||||
|
||||
范式阅读 [`tests/integration/README.md`](../tests/integration/README.md):那里有针对凭证来源、目录 artifact 忽略策略的补充。
|
||||
|
||||
记住:**测试代码也是一种部署面**,别把 staging 凭证烘焙进来。
|
||||
|
||||
---
|
||||
|
||||
## 8. 新 skill 的最小测试清单
|
||||
|
||||
以下清单 **原文摘自 [`tests/README.md`](../tests/README.md) 「新技能最小测试清单」**(复制新仓库后逐项勾选):
|
||||
|
||||
- [ ] `python tests/run_tests.py -v` 能通过。
|
||||
- [ ] `python scripts/main.py health` 能通过。
|
||||
- [ ] `python scripts/main.py version` 输出 JSON,且 `skill` 与目录名 / `SKILL.md` / `constants.SKILL_SLUG` 一致。
|
||||
- [ ] 所有 DB / 文件写入都在 `IsolatedDataRoot`(或等价隔离)下测试,不写真实数据目录。
|
||||
- [ ] 外部系统默认使用 mock / `FakeAdapter`,不访问真实 API,不打开真实 RPA。
|
||||
- [ ] 至少有 1 个成功路径测试。
|
||||
- [ ] 至少有 1 个缺必填字段 / 非法输入测试。
|
||||
- [ ] 如果有 adapter,至少覆盖 timeout / unauthorized / invalid response(可用 fake 模拟)。
|
||||
- [ ] 如果有解析 / 计算 / 校验类业务,至少保留 1 组 golden fixture(脱敏)。
|
||||
- [ ] 真实 API / 真实 RPA 测试只放 `tests/integration/`,并且默认 `.sample`,不默认运行。
|
||||
|
||||
---
|
||||
|
||||
## 9. AI 编程工具使用测试时的红线
|
||||
|
||||
改编自 [`tests/README.md`](../tests/README.md) 「AI 编程工具注意事项」,压缩成 skill 开发者视角:
|
||||
|
||||
| 红线 | 解释 |
|
||||
|------|------|
|
||||
| 不改业务凑测试 | 除非需求变更已确认,否则别让 CI 绿通过阉割业务分支达成 |
|
||||
| **默认套件零外联** | 不把真实 HTTP / 浏览器写进 `tests/test_*.py` |
|
||||
| `.sample` 尊重 | 集成范式改名前先读完 README;别让 `.sample` silent 变成根测试 |
|
||||
| **零硬编码凭证** | token / cookie / 生产 URL → 用虚构域名或 vault ref |
|
||||
| mock 优先 | 逻辑应在 service + FakeAdapter,而不是巨胖 CLI 断言 |
|
||||
| 结构化错误 | 断言错误码字段,而不是 substring of stderr 漂移集合 |
|
||||
| **integration = 显式开关** | `OPENCLAW_TEST_TARGET` + `ALLOW_REAL_*` / `ALLOW_WRITE_ACTIONS` |
|
||||
|
||||
---
|
||||
|
||||
## 10. 测试和发布的关系
|
||||
|
||||
**在运行 [`release.ps1`](../release.ps1) 之前,`python tests/run_tests.py -v` 必须绿色**。失败的默认套件意味着:
|
||||
|
||||
- 打包路径可能根本不可运行;
|
||||
- CI 加密前的静态假设可能在宿主崩溃;
|
||||
- metadata slug 漂移将被市场拒绝。
|
||||
|
||||
把『本地 unittest 绿』视作 tag 的前置条件,而不是『有空再跑』。发布流水线成功后仍要做安装验证——那是另一个维度;**测试是第一个维度**。
|
||||
|
||||
---
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [`tests/README.md`](../tests/README.md) — 表格、变量与目录细则
|
||||
- [`references/DEVELOPMENT.md`](DEVELOPMENT.md) §14.5 — 测试驱动的开发顺序
|
||||
- [`tests/integration/README.md`](../tests/integration/README.md) — 高风险用法
|
||||
Reference in New Issue
Block a user