新增完善测试模板

This commit is contained in:
2026-05-03 17:52:52 +08:00
parent 07fa0b0038
commit dd6236866e
26 changed files with 1319 additions and 5 deletions

32
tests/desktop/README.md Normal file
View File

@@ -0,0 +1,32 @@
# 可选桌面 E2Epytest
本目录用于**匠厂桌面 + jiangchang_desktop_sdk** 的端到端冒烟,**不是** `python tests/run_tests.py` 默认路径的一部分。
## 依赖
- `pytest`
- `jiangchang_desktop_sdk`(由 `conftest.py` 按多级路径尝试加入 `sys.path`,见该文件说明)
- 匠厂桌面已安装或开发态可启动,且 `jiangchang://` 协议可用
## 为什么不默认跑
- 需要真实宿主环境,无法在纯 CI/无头环境稳定复现。
- `unittest``tests/run_tests.py` **只发现** `tests/` 根目录下 `test_*.py`**不递归**本目录。
- 示例文件为 **`test_desktop_smoke.py.sample`**,避免被误收集。
## 如何启用
1.`test_desktop_smoke.py.sample` **复制或重命名**为 `test_desktop_smoke.py`
2. 按你的技能名称、路由关键词、期望输出修改 `ask` 与断言。
3. 运行:
```powershell
pytest tests/desktop/test_desktop_smoke.py -v -s
```
失败时,`conftest.py` 中的 `failure_snapshot_dir` fixture 会将现场目录指向 `tests/desktop/artifacts/`(需自行避免将大文件提交进仓库)。
## 与后端测试的关系
- **后端 / CLI / DB**:请使用仓库根目录下 `python tests/run_tests.py`(见 `tests/README.md`)。
- **桌面 E2E**:仅在本目录用 **pytest** 维护,二者互补、互不替代。

76
tests/desktop/conftest.py Normal file
View File

@@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
"""
可选桌面 E2Epytest 收集时自动加载,负责把 jiangchang_desktop_sdk 加入 sys.path。
查找顺序(与 content-manager 对齐思路,去掉业务仓专属路径假设):
1. 已 pip install 则直接 import
2. 环境变量 JIANGCHANG_KIT_ROOT → sdk/jiangchang-desktop-sdk/src
3. 自本文件上溯skill 根 → workspace 根 → OpenClaw 根,拼 jiangchang-platform-kit/sdk/.../src
4. 都找不到则 pytest.exit 给出可操作的排障说明。
"""
from __future__ import annotations
import importlib.util
import os
import sys
import pytest
def _bootstrap_jiangchang_desktop_sdk() -> None:
if importlib.util.find_spec("jiangchang_desktop_sdk") is not None:
return
env_kit_root = (os.environ.get("JIANGCHANG_KIT_ROOT") or "").strip()
here = os.path.dirname(os.path.abspath(__file__))
skill_root = os.path.abspath(os.path.join(here, "..", ".."))
workspace_root = os.path.abspath(os.path.join(skill_root, ".."))
open_claw_root = os.path.abspath(os.path.join(workspace_root, ".."))
candidates = []
if env_kit_root:
candidates.append(os.path.join(env_kit_root, "sdk", "jiangchang-desktop-sdk", "src"))
candidates.append(
os.path.join(workspace_root, "jiangchang-platform-kit", "sdk", "jiangchang-desktop-sdk", "src")
)
candidates.append(
os.path.join(open_claw_root, "jiangchang-platform-kit", "sdk", "jiangchang-desktop-sdk", "src")
)
tried = []
for cand in candidates:
cand_abs = os.path.abspath(cand)
tried.append(cand_abs)
if os.path.isdir(cand_abs) and os.path.isdir(os.path.join(cand_abs, "jiangchang_desktop_sdk")):
if cand_abs not in sys.path:
sys.path.insert(0, cand_abs)
return
pytest.exit(
"无法定位 jiangchang_desktop_sdk。请任选一种方式解决\n"
" 1) pip install -e <path-to>/jiangchang-platform-kit/sdk/jiangchang-desktop-sdk\n"
" 2) 设置环境变量 JIANGCHANG_KIT_ROOT=<path-to>/jiangchang-platform-kit\n"
" 3) 将 jiangchang-platform-kit 与本仓库放在同一 workspace 父级可推断的位置,例如:\n"
f" {workspace_root}/jiangchang-platform-kit/ 或 {open_claw_root}/jiangchang-platform-kit/\n"
"已尝试的候选路径:\n - " + "\n - ".join(tried),
returncode=4,
)
_bootstrap_jiangchang_desktop_sdk()
_ARTIFACT_DIR = os.path.join(os.path.dirname(__file__), "artifacts")
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
rep = outcome.get_result()
setattr(item, f"rep_{rep.when}", rep)
@pytest.fixture
def failure_snapshot_dir() -> str:
os.makedirs(_ARTIFACT_DIR, exist_ok=True)
return _ARTIFACT_DIR

View File

@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
"""
可选桌面 E2E 示例(默认不执行:文件名后缀为 .sample
启用方式:
复制本文件为 test_desktop_smoke.py按目标技能修改提问文案与断言。
运行pytest tests/desktop/test_desktop_smoke.py -v -s
依赖pytest、jiangchang_desktop_sdk、匠厂桌面宿主已就绪。
"""
from __future__ import annotations
import logging
import os
import pytest
from jiangchang_desktop_sdk import AskOptions, JiangchangDesktopClient
_logger = logging.getLogger("skill-template-desktop-e2e-sample")
if not _logger.handlers:
_logger.setLevel(logging.INFO)
_h = logging.StreamHandler()
_h.setFormatter(logging.Formatter("[E2E-sample] %(message)s"))
_logger.addHandler(_h)
_ARTIFACT_DIR = os.path.join(os.path.dirname(__file__), "artifacts")
class TestDesktopSmokeSample:
"""最小 smoke拉起桌面、等待网关、发一条与技能路由相关的问句并做宽松断言。"""
@pytest.fixture(autouse=True)
def connect_and_teardown(self, request):
client = JiangchangDesktopClient()
_logger.info("ensure_app_running")
client.ensure_app_running(wait_timeout_s=30)
client.bring_to_front()
client.wait_gateway_ready(timeout_ms=30000)
self.client = client
yield
try:
rep = getattr(request.node, "rep_call", None)
if rep is not None and rep.failed:
os.makedirs(_ARTIFACT_DIR, exist_ok=True)
paths = client.snapshot(_ARTIFACT_DIR, tag=request.node.name)
_logger.error("snapshot: %s", paths)
finally:
client.disconnect()
def test_gateway_and_simple_ask(self) -> None:
"""请将 SKILL 名与路由关键词改成你复制后的真实技能。"""
answer = self.client.ask(
"请使用当前已安装技能执行一次 health 检查并返回 OK 或等价结果。",
AskOptions(timeout=120000, new_task=True),
)
assert answer
assert "OK" in answer or "ok" in answer.lower() or "健康" in answer