# 测试指南

本文档说明三件事：
- 单元测试怎么跑。
- 集成测试怎么组织。
- 测试辅助工具如何使用，尤其是远程 ECM 环境初始化工具。

## 1. 测试分层

### 1.1 单元测试

位置：`tests/unit/`

目标：
- 校验状态机不变量、事件约束、策略辅助逻辑。
- 校验 datasource/handler 工具链、配置严格性、导入面稳定性。
- 对具体 domain 的 update/create 行为做快速回归。

单元测试取舍原则：
- 优先测文档里承诺的行为语义，不优先测单纯转发、setter、空包装器。
- 如果一个测试只是在断言“函数 A 调了函数 B”，但不验证最终行为，通常应该删掉或改写成行为测试。
- 如果需要 test double，优先让它承载真实行为断言，不保留仅为凑接口而存在的空壳类。

常用命令：
- 全量单元测试：`pytest tests/unit -q`
- 指定文件：`pytest tests/unit/test_update_ops.py -q`

适用场景：
- 修改 `sync_state_machine/` 公共逻辑。
- 修改某个 domain 的 handler/strategy。
- 校验 schema 相关逻辑没有引入行为回退。

### 1.2 集成测试

位置：`tests/integration/`

目标：
- 覆盖 pipeline 级行为，包括 load/bind/create/update/poll/persist。
- 校验多节点、多轮次、mock datasource 与 UI smoke 场景。
- 验证运行配置、状态机配置和工具链连通性。

常用命令：
- 全量集成测试：`pytest tests/integration -q`
- UI 烟测：`pytest tests/integration/test_ui_api_smoke.py -q`
- 工具链专项：`pytest tests/integration/test_toolchain_config_and_graph.py -q`

适用场景：
- 修改 pipeline、collection、状态机执行路径。
- 修改持久化或跨 domain 依赖行为。
- 修改运行配置解析、注册表装配等入口逻辑。

### 1.3 脚本化大规模回归

位置：`scripts/auto_test_domains/`

目标：
- 用“多轮数据集 + 报告”的方式覆盖复杂业务 domain。
- 适合真实接口联调、回归复现、差异收敛分析。

特点：
- 这类脚本不直接纳入 pytest。
- 它们更偏向测试编排与问题定位，而不是轻量级快速回归。

相关文档：
- `docs/auto_test_spec/agent_auto_test_method.md`

## 2. 远程环境初始化工具

位置：`tools/remote_env.py`

这个工具是给真实 ECM 环境联调用的最小控制器，提供 4 个命令：
- `clear`：停止服务、恢复 `.env`、清空自动测试专用 CouchDB 数据库。
- `init`：停止服务，按 JSONL 数据目录执行 `run.py restore-database` 恢复测试数据库。
- `run`：启动远程 ECM 服务，不阻塞当前命令。
- `stop`：关闭远程 ECM 服务，并恢复原始 `.env`。

它不是 pipeline 执行器，只负责测试环境的基础生命周期管理。

### 2.1 配置项

示例配置：`tools/remote_env.example.yaml`

核心配置：
- `couchdb.base_url` / `couchdb.database` / `couchdb.username` / `couchdb.password`
- `service.ecm_root`
- `service.python_path`
- `data.data_dir`
- `env_file.path`
- `env_file.overrides`

按命令的最低要求：
- `run` / `stop`：只要求 `service.*` 可用。
- `clear`：要求 `service.*` 和 `couchdb.*` 可用。
- `init`：要求 `service.*`、`couchdb.*`、`data.*` 全部可用。

可选配置：
- `service.start_command`
- `service.restore_command`
- `service.ready_url`
- `service.log_file`
- `service.env`
- `env_file.restore_on_stop`

支持占位符：
- `${PROJECT_ROOT}`：当前仓库根目录。
- `${CONFIG_DIR}`：配置文件所在目录。
- `${ECM_ROOT}`：`service.ecm_root` 的值。
- `${PYTHON}`：`service.python_path` 的值。
- `${DATA_DIR}`：`data.data_dir` 的值。
- `${COUCHDB_DATABASE}`：`couchdb.database` 的值。
- `${COUCHDB_USERNAME}`：`couchdb.username` 的值。
- `${COUCHDB_PASSWORD}`：`couchdb.password` 的值。
- `${COUCHDB_URL}`：`couchdb.url` 的值。
- `${COUCHDB_PORT}`：`couchdb.port` 的值。

### 2.2 基本用法

1. 复制示例配置并填入真实路径。
2. 先执行 `init` 恢复测试数据库。
3. 再执行 `run` 启动远程服务。
4. 测试结束后执行 `stop`。

命令示例：

```bash
python tools/remote_env.py --config tools/remote_env.example.yaml init
python tools/remote_env.py --config tools/remote_env.example.yaml run
python tools/remote_env.py --config tools/remote_env.example.yaml stop
python tools/remote_env.py --config tools/remote_env.example.yaml clear
```

### 2.3 命令行覆盖

如果只想临时覆盖少数字段，可以直接传命令行参数：

```bash
python tools/remote_env.py \
  --config tools/remote_env.example.yaml \
  --db-name test_push_auto_tmp \
  --ecm-root /data/ecm_app \
  --python-path /data/venv/bin/python \
  init
```

支持覆盖的字段：
- `--db-name`
- `--db-user`
- `--db-password`
- `--couchdb-base-url`
- `--couchdb-url`
- `--couchdb-port`
- `--ecm-root`
- `--python-path`
- `--data-dir`
- `--ready-url`

### 2.4 行为约束

- `clear` 和 `init` 会先执行停服，避免服务仍占用 CouchDB 连接。
- `init` 不会自动执行 `run`；数据库恢复和服务启动分开控制。
- `run` 如果检测到远端仓库下已有 `run.py` 进程在运行，会直接返回 `already_running`。
- `.env` 只覆盖 `env_file.overrides` 中声明的键，未声明的键会原样保留。
- 第一次覆盖 `.env` 时会自动备份，`stop` 和 `clear` 默认会恢复原始 `.env`。
- 进程状态写到 `runtime/remote_env/state.json`，不需要手工配置 pid 文件。

## 3. 推荐测试流程

### 3.1 本地开发回归

1. 先跑 `pytest tests/unit -q`。
2. 如果改动影响 pipeline，再跑目标集成测试。
3. 如果改动影响真实接口流程，再使用 `tools/remote_env.py` 管理远程环境。
4. 如果修改了配置语义或 pipeline 控制流，优先补一条直接覆盖设计语义的 pytest，而不是只补装配/转发测试。

### 3.2 真实接口联调

1. 用 `init` 恢复 ECM 测试库。
2. 用 `run` 拉起 ECM 服务。
3. 执行同步脚本或联调脚本。
4. 查看 `runtime/remote_env/` 下的状态文件与日志。
5. 完成后执行 `stop`。

### 3.3 复杂 domain 回归

1. 用 `scripts/auto_test_domains/` 中的 case 脚本准备多轮数据。
2. 先缩小到单个 domain 复现。
3. 确认差异与日志后，再扩大到聚合回归。

## 4. 相关文件

- `tests/README.md`
- `tools/remote_env.py`
- `tools/remote_env.example.yaml`
- `docs/operations.md`
- `docs/auto_test_spec/agent_auto_test_method.md`