chore: 更新共享测试基础设施

- conftest.py: operator_id fixture 支持从 DINGTALK_USER_ID 自动转换
- conftest.py: 新增 _userid_to_unionid 工具函数
- .env.example: 补充 dingtalk-todo 测试所需配置
- pyproject.toml/uv.lock: 新增 dotenv 依赖
- .gitignore/skills-lock.json: 同步更新
This commit is contained in:
breath57
2026-03-12 23:06:32 +08:00
parent bae298448f
commit 4f13af2420
6 changed files with 111 additions and 19 deletions
+4
View File
@@ -5,3 +5,7 @@ __pycache__/
*.pyc
*.pyo
.pytest_cache/
# local drafts & temp files (not for repo)
docs/
temp/
+5
View File
@@ -1,6 +1,11 @@
{
"version": 1,
"skills": {
"agent-browser": {
"source": "vercel-labs/agent-browser",
"sourceType": "github",
"computedHash": "57bfa07d34f966bccf791d49c9b6ce507fbba08f7dbf9e2ad15809e2dd29041e"
},
"skill-creator": {
"source": "anthropics/skills",
"sourceType": "github",
+50 -17
View File
@@ -1,31 +1,64 @@
# ── 钉钉应用凭证 ─────────────────────────────────────────────────
# ╔══════════════════════════════════════════════════════════════════╗
# ║ 公共配置(所有技能共用) ║
# ╚══════════════════════════════════════════════════════════════════╝
# 钉钉应用凭证(开放平台 → 应用管理 → 凭证信息)
DINGTALK_APP_KEY=your_app_key_here
DINGTALK_APP_SECRET=your_app_secret_here
# ── 企业 corpId ───────────────────────────────────────────────────
# 企业 corpId(开放平台 → 应用管理 → 基本信息)
DINGTALK_CORP_ID=your_corp_id_here
# ── 测试数据 ──────────────────────────────────────────────────────
# 测试用文档节点 ID从 alidocs URL 末尾提取)
# 操作者 unionId开发者后台 → 调试工具 → 获取 unionId
OPERATOR_ID=your_union_id_here
# 写入测试开关(默认关闭,设置为 1 启用覆写文档/表格等破坏性测试)
ENABLE_WRITE_TESTS=0
# ╔══════════════════════════════════════════════════════════════════╗
# ║ dingtalk-document 知识库 & 文档 ║
# ╚══════════════════════════════════════════════════════════════════╝
# 测试用文档节点 ID从 alidocs URL 提取:/nodes/<nodeId>
TEST_NODE_ID=your_node_id_here
# 测试用文档 URL
# 测试用文档完整 URL
TEST_NODE_URL=https://alidocs.dingtalk.com/i/nodes/your_node_id_here
# 创建/删除临时文档时使用的知识库 workspaceId
TEST_WORKSPACE_ID=your_workspace_id_here
# ── 操作者 unionId ────────────────────────────────────────────────
# 在钉钉开发者后台 -> 调试工具 -> 获取 unionId
OPERATOR_ID=your_union_id_here
# ╔══════════════════════════════════════════════════════════════════╗
# ║ dingtalk-ai-table AI 表格 ║
# ╚══════════════════════════════════════════════════════════════════╝
# ── dingtalk-ai-table 测试数据 ────────────────────────────────────
# 测试工作簿 ID普通电子表格即 .xlsx / SHEET 类型,非 .able AI表格
# 若留空,测试框架会在 TEST_WORKSPACE_ID 下自动创建临时工作簿并在结束后删除。
# 手动指定时从 alidocs 链接中提取 /nodes/<workbookId>
# https://alidocs.dingtalk.com/i/nodes/<workbookId>
# AI 表格(.able 文件)的 nodeId
# 从分享链接提取https://alidocs.dingtalk.com/i/nodes/<nodeId>
TEST_NOTABLE_ID=your_notable_id_here
# 普通电子表格 workbookId.xlsx / SHEET 类型,非 .able
# 若留空,测试框架会在 TEST_WORKSPACE_ID 下自动创建临时工作簿并在结束后删除
TEST_WORKBOOK_ID=
# 用于自动创建测试工作簿的知识库 IDTEST_WORKBOOK_ID 为空时生效)
TEST_WORKSPACE_ID=QXvd5SLN2AxOQz0Z
# ╔══════════════════════════════════════════════════════════════════╗
# ║ dingtalk-message 消息发送 ║
# ╚══════════════════════════════════════════════════════════════════╝
# ── 写入测试开关(默认关闭,设置为 1 启用覆写文档/表格测试)────────
ENABLE_WRITE_TESTS=0
# ── Webhook 机器人(群设置 → 智能群助手 → 自定义机器人)──────────
# 完整 Webhook URL含 access_token
TEST_WEBHOOK_URL=https://oapi.dingtalk.com/robot/send?access_token=your_token_here
# 加签密钥(选填,不配置则跳过加签测试)
TEST_WEBHOOK_SECRET=
# ── 企业内部应用机器人 ──────────────────────────────────────────
# 机器人 robotCode通常等于 DINGTALK_APP_KEY
TEST_ROBOT_CODE=your_robot_code_here
# 测试目标用户 userId机器人单聊消息的接收人
TEST_USER_ID=your_user_id_here
# 测试群的 openConversationId机器人群聊消息用
# 可通过机器人收到群消息回调时获得,或调用 IM 接口创建群时返回
TEST_OPEN_CONVERSATION_ID=your_open_conversation_id_here
# ── 工作通知 ────────────────────────────────────────────────────
# 应用 agentId开放平台 → 应用管理 → 基本信息)
TEST_AGENT_ID=your_agent_id_here
+28 -1
View File
@@ -26,9 +26,30 @@ def _require_env(key: str) -> str:
APP_KEY = os.environ.get("DINGTALK_APP_KEY", "")
APP_SECRET = os.environ.get("DINGTALK_APP_SECRET", "")
OPERATOR_ID = os.environ.get("OPERATOR_ID", "")
USER_ID = os.environ.get("DINGTALK_USER_ID", "")
BASE = "https://api.dingtalk.com"
def _userid_to_unionid(app_key: str, app_secret: str, user_id: str) -> str:
"""通过旧版 API 将 userId 转换为 unionId。"""
r = requests.get(
"https://oapi.dingtalk.com/gettoken",
params={"appkey": app_key, "appsecret": app_secret},
timeout=15,
)
r.raise_for_status()
old_token = r.json()["access_token"]
r2 = requests.post(
f"https://oapi.dingtalk.com/topapi/v2/user/get?access_token={old_token}",
json={"userid": user_id},
timeout=15,
)
r2.raise_for_status()
data = r2.json()
assert data.get("errcode") == 0, f"userId→unionId 转换失败:{data}"
return data["result"]["unionid"] # 注意:无下划线
@pytest.fixture(scope="session")
def token() -> str:
app_key = _require_env("DINGTALK_APP_KEY")
@@ -46,7 +67,13 @@ def token() -> str:
@pytest.fixture(scope="session")
def operator_id() -> str:
return _require_env("OPERATOR_ID")
"""返回操作者 unionId。优先读 OPERATOR_ID否则从 DINGTALK_USER_ID 自动转换。"""
if OPERATOR_ID:
return OPERATOR_ID
user_id = _require_env("DINGTALK_USER_ID")
app_key = _require_env("DINGTALK_APP_KEY")
app_secret = _require_env("DINGTALK_APP_SECRET")
return _userid_to_unionid(app_key, app_secret, user_id)
@pytest.fixture(scope="session")
+1
View File
@@ -6,6 +6,7 @@ readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"alibabacloud-dingtalk>=2.2.38",
"dotenv>=0.9.9",
"requests>=2.32.5",
]
Generated
+22
View File
@@ -387,6 +387,17 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/66/d3/a7daaee544c904548e665829b51a9fa2572acb82c73ad787a8ff90273002/darabonba_core-1.0.5-py3-none-any.whl", hash = "sha256:671ab8dbc4edc2a8f88013da71646839bb8914f1259efc069353243ef52ea27c", size = 24580, upload-time = "2025-12-12T07:53:59.494Z" },
]
[[package]]
name = "dotenv"
version = "0.9.9"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "python-dotenv" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/b2/b7/545d2c10c1fc15e48653c91efde329a790f2eecfbbf2bd16003b5db2bab0/dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9", size = 1892, upload-time = "2025-02-19T22:15:01.647Z" },
]
[[package]]
name = "frozenlist"
version = "1.8.0"
@@ -680,6 +691,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
]
[[package]]
name = "python-dotenv"
version = "1.2.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" },
]
[[package]]
name = "requests"
version = "2.32.5"
@@ -701,6 +721,7 @@ version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "alibabacloud-dingtalk" },
{ name = "dotenv" },
{ name = "requests" },
]
@@ -712,6 +733,7 @@ dev = [
[package.metadata]
requires-dist = [
{ name = "alibabacloud-dingtalk", specifier = ">=2.2.38" },
{ name = "dotenv", specifier = ">=0.9.9" },
{ name = "requests", specifier = ">=2.32.5" },
]