Files
knowledge-governance/scripts/test_dws_error_map.py
T
姚章浩 6496ee3571
build / build (push) Successful in 25s
fix(artifact): 在线文档大小预检只对普通文件生效,压测暴露的取件器错误映射补齐
- 大小预检此前对 adoc/axls 也用目录大小,而在线文档目录大小是容器大小
  (实测有 1.40GB 的 adoc,导出 docx 仅 49.5MB)→ 3 个可导出文档被误判 over_limit
- axls 转换器的 dws 失败改为抛 DwsCallError,取件器映射成平台错误码
  (此前进程无结果退出,平台只看到 artifact_fetch_no_output)
- export_failed 改判 retryable:8 路混合时巨型文档服务端导出偶发失败,1/2/4 路与串行必成
- 新增 2 个回归测试 + dws_error_map 1 个断言;文档补并发实测表与回归清单
2026-10-09 11:51:26 +08:00

115 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""dws_error_map 单测:全部使用本轮实测捕获的真实 dws 错误体(不改写、不简化)。"""
import json
import os
import sys
# 测的是仓库里的实现(脚本目录),bridge/ 只是本轮评估期的开发副本
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
if not os.path.exists(os.path.join(os.path.dirname(os.path.abspath(__file__)), "dws_error_map.py")):
sys.path.insert(0, "/home/zheromiku/dws_test/bridge")
from dws_error_map import classify
# —— 真实样本(逐字来自本轮会话的工具输出)——
CASES = [
("TLS 缺 CA(debian-slim 实测)",
{"category": "api",
"cause": 'Post "https://mcp-gw.dingtalk.com/server/91e17caf44f6ca1ed9c6ce614221a518ac93300ece63ca8d7e9b133f912e0607": tls: failed to verify certificate: x509: certificate signed by unknown authority',
"code": 1, "hint": "请检查网络连通性和 MCP 服务状态后重试。", "retryable": True,
"message": "提交导出任务失败: request to https://mcp-gw.dingtalk.com failed"},
("tls_ca", False, False)),
("登录态被顶掉(容器认证包轮换实测)",
{"type": "internal", "exit_code": 5, "upstream_code": "UNCLASSIFIED",
"message": "[UNCLASSIFIED] resolve access token: 旧版登录态已无法由当前认证服务刷新;本地 profile 已保留,请重新运行 dws auth login 完成一次重新授权: MCP token exchange failed: invalidParameter.authCode.notFound - 不合法的临时授权码"},
("credentials", False, False)),
("无权限(技术诊断部某 axls)",
{"error": {"actions": ["持续失败时保留 Trace ID 和 Server Code 联系服务端排查"], "category": "api", "code": 1,
"hint": "The API returned a business-level error.", "message": "Access Denied",
"operation": "tools/call", "reason": "business_error",
"server_error_code": "forbidden.accessDenied", "server_key": "sheet"}},
("forbidden", False, False)),
("严格版 512MB 上限(838MB xz 实测)",
{"type": "internal", "exit_code": 5, "message": "LOCAL_DOWNLOAD_TOO_LARGE: 响应大小 878540264 超过上限 536870912 字节"},
("over_limit", False, False)),
("本地文件已存在(探针重跑实测)",
{"type": "internal", "exit_code": 5, "message": "LOCAL_FILE_EXISTS: 目标文件已存在;请选择新的输出路径"},
("file_exists", False, False)),
("able 节点走 doc 入口(实测拒收)",
{"error": {"category": "api", "code": 1,
"message": "提交导出任务失败: 当前节点是钉钉AI表格(多维表,扩展名=able),不是钉钉在线文档(扩展名=adoc),无法使用文档相关工具操作。请改用钉钉AI表格相关 MCP Tool 或相关 CLI 命令进行操作。",
"server_error_code": "invalidRequest.inputArgs.invalid", "server_key": "doc"}},
("unsupported_format", False, False)),
("axls 不支持直下(实测拒收)",
{"type": "api", "subtype": "business_error", "exit_code": 1,
"upstream_code": "invalidRequest.inputArgs.invalid",
"message": "nodeId 指向的节点是钉钉表格(extension=axls),在线表格不支持直接下载。请使用 getRange 工具获取表格数据。"},
("unsupported_format", False, False)),
("节点不存在(dlink 节点实测)",
{"ok": False, "outcome": "failure",
"error": {"type": "api", "subtype": "business_error", "exit_code": 1,
"upstream_code": "internalError", "message": "workspace node not found"}},
("missing", False, True)),
("正文为空(平台历史缺陷文案)",
{"category": "api", "code": 1, "message": "DingTalk online document content is empty"},
("empty_document", False, False)),
("限流(构造,用于锁定 429 语义)",
{"category": "api", "code": 1, "message": "HTTP 429 Too Many Requests"},
("http_429", True, False)),
("连接层失败(构造,锁定可重试语义)",
{"category": "api", "code": 1, "cause": "dial tcp: lookup alidocs.dingtalk.com: no such host"},
("source_unavailable", True, False)),
("导出任务超时(构造,锁定可重试语义)",
{"category": "api", "code": 1, "message": "DingTalk export job timed out after 120s"},
("export_timeout", True, False)),
("导出任务失败(并发压测实测:同一节点 8 路偶发失败、1/2/4 路及串行必成)",
{"error": {"category": "api", "code": 1, "message": "提交导出任务失败: 钉钉导出任务失败",
"server_error_code": "internalError"}},
("export_failed", True, False)),
("未分类错误 → 不得默认可重试",
{"category": "internal", "code": 9, "message": "something nobody has seen before"},
("unknown", False, False)),
]
SUCCESS_CASES = [
("严格版成功体", {"ok": True, "outcome": "success", "data": {"count": 1}}),
("普通子命令成功体", {"success": True, "sizeBytes": 305078, "status": "SUCCESS"}),
("drive 成功体", {"ok": True, "outcome": "success", "data": {"savedPath": "ref.xlsx", "sizeBytes": 3583939, "success": True}}),
]
fails = 0
print("=== 失败体映射 ===")
for label, payload, expect in CASES:
r = classify(payload)
got = (r["code"], r["retryable"], r["missing"])
mark = "✅" if got == expect else "❌"
if got != expect:
fails += 1
print(f" {mark} {label:34s} → code={r['code']:<18} retryable={str(r['retryable']):<5} missing={str(r['missing']):<5} 期望={expect}")
print("\n=== 成功体映射 ===")
for label, payload in SUCCESS_CASES:
r = classify(payload)
ok = r["ok"] is True and r["code"] == ""
if not ok:
fails += 1
print(f" {'✅' if ok else '❌'} {label:34s} → ok={r['ok']} code={r['code']!r}")
print("\n=== 文本输入(stderr 兜底)===")
r = classify(None, stderr="Error: x509: certificate signed by unknown authority")
print(f" {'✅' if r['code']=='tls_ca' else '❌'} stderr-only 文本 → code={r['code']}")
fails += 0 if r["code"] == "tls_ca" else 1
print(f"\n结果: {'全部通过' if fails==0 else str(fails)+' 项失败'}({len(CASES)+len(SUCCESS_CASES)+1} 个断言)")
sys.exit(1 if fails else 0)