Files
sap-cli-skill/docs/dev/test-report.md
T
吴让宇 c5905a5b1e refactor: 本仓升为唯一源(原 sap-cli 源码仓归档)
方向反转:此前 SKILL.md 是「模板渲染产物」、sap-cli 是源;现 sap-cli 归档,
sap-cli-skill 承接开发与分发,SKILL.md 回归手工维护的正本。

迁移(来自 sap-cli,共 104 文件):
- tests/           692 例测试(15 个文件的内联 sys.path 改指 assets/)
- openspec/        SDD 规格与归档变更(42 文件)
- docs/            开发文档与 ADT 原理(含 dev/CLAUDE.md、AGENTS.md)
- .claude/         rules 副本 + settings.json(供 Claude Code)
- .github/ .hermes/ .pre-commit-config.yaml .editorconfig CLAUDE.md
- scripts/ 保持仅 setup.py(pack_skill.py 已随旧仓归档,不迁)

修复(迁移暴露的真实缺陷):
- assets/pyproject.toml 的 build-backend 写作 `setuptools.backends._legacy:_Backend`,
  该模块在 setuptools 中不存在 → `pip install -e` 从来装不上。改为 build_meta。
  实测:临时 venv 安装成功,sap-cli --help 正常列出 31 个命令
- pyproject readme 指向不存在的 assets/README.md(editable 安装会失败)→ 改内联文本
- pyproject urls 改指 sap-cli-skill

机制调整:
- .github/workflows/ci.yml 适配 assets/ 布局;顶部注明该工作流仅 GitHub 执行,
  本仓在 Gitee 不会自动跑
- pre-commit 增本地测试门禁(Gitee 上真正生效的那道)
- .gitignore 合并旧仓完整规则(保留 log/ 下 md 知识库入库,只忽略运行日志)
- 大文件上限 100KB→1MB(架构图 512KB)

守卫测试 tests/unit/test_repo_guards.py(10 → 18 例):
- SKILL.md 须记录 parser 全部 CLI 命令 / 铁律 1-5 须为真实小节标题 / 示例不得违反铁律 5
- references/ 规则齐备;.claude/rules 与 references 必须一致(实测抓到一次真实漂移)
- VERSION == sapcli.__version__ == README 版本
- 仓内不得再出现 pack_skill.py / skill-src(防废弃流程回潮)

698 tests OK;editable 安装与 CLI 入口经临时 venv 实测通过。
docs/RELEASING.md 重写为单源开发流程。
2026-09-11 00:40:15 +08:00

13 KiB
Raw Blame History

ADT 工具集测试报告

测试目标:验证 main.py 的 create / info / download / sync / delete 五大功能在各种场景下的正确性。

测试日期:2026-05-20 | 测试脚本:test_main.py

前置条件

  • SAP 系统可连接(配置文件 config.ini 正确)
  • Python 3.10+ 和 requests 库已安装
  • 系统中存在至少一个 Modifiable 状态的传输请求
  • 系统中存在以下已有对象用于查询测试:
    • class: ZCL_SAP_MCP_HANDLER
    • report: ZIDTR_IMPORT_BOM
    • domain: ZSAPILOT_OBJECT_TYPE
    • dataelement: ZSAPILOT_OBJECT_NAME
    • table: ZSAPILOT_OBJ

测试覆盖矩阵

操作(行) × 对象类型(列) 的交叉覆盖情况。 = 已测试通过 | = 不适用 | = 未覆盖(系统限制)

操作 \ 对象类型 report class function functiongroup interface domain dataelement table tabletype
create SC1 SC2 SC3 SC3 SC4 🔒 🔒 🔒
info SC1 SC2 SC3 SC4 SC9 SC9 SC9 SC9
download SC1 SC2 SC3 SC13 SC4 🔒 🔒 🔒
sync SC1,SC5 SC2 SC3 SC13 SC4 🔒 🔒 🔒
delete SC1 SC2 SC3 SC3 SC4 🔒 🔒 🔒

图例:

  • 已测试通过
  • 不适用(VIT 端点不支持源码读写和锁定操作)
  • 🔒 SAP 系统限制(当前目标系统 NW 7.40 的 ADT API 不支持 DDIC 对象的源码读写和创建/删除)

DDIC 对象(🔒)说明:

  • info 可用(使用 Accept: */* 头),已在 SC9 验证
  • create/download/sync/delete 不可用 — 经实际探测,SAP NetWeaver 7.40 的 ADT 框架对 DDIC 对象(domain/dataelement/table)不提供源码读写端点(返回 404/406)
  • 在 SAP S/4HANA 或更高版本 NetWeaver 系统上,这些操作可能可用
  • 代码层面已实现完整支持(URI 映射、创建模板、argparse choices 均已配置),仅受目标系统 API 限制

tabletype)说明:

  • info 已可用 — 通过 VIT 端点(/sap/bc/adt/vit/wb/object_type/ttypda/object_name/{name})查询元数据,已在 SC9 验证
  • download/sync/delete 不可用 — VIT 端点为只读元数据接口,不提供源码读写和锁定操作
  • create 不可用 — 无可用的集合端点用于创建新对象

场景测试详情

场景组 happy — 正向完整流程

核心流程:创建 → 查询 → 下载 → 修改 → 同步激活 → 验证修改 → 删除 → 验证不存在

场景 1: 正向完整流程 — report

编号 步骤 操作 验证条件 结果
SC1-1 创建 report create --name ZTEST_SC1_RPT_xxx --type report rc == 0
SC1-2 查询验证 info --name ZTEST_SC1_RPT_xxx --type report rc == 0 且名称匹配
SC1-3 下载源码 download --name ZTEST_SC1_RPT_xxx --type report rc == 0 且文件存在
SC1-4 修改后同步激活 修改源码 → sync 语法检查通过 + 激活成功
SC1-5 验证修改持久化 再次 download 源码包含修改标记
SC1-6 删除对象 delete --name ZTEST_SC1_RPT_xxx 删除成功
SC1-7 验证已不存在 info rc != 0

场景 2: 正向完整流程 — class

编号 步骤 操作 验证条件 结果
SC2-1 创建 class create --name ZTEST_SC2_CLS_xxx --type class rc == 0
SC2-2 查询验证 info --name ZTEST_SC2_CLS_xxx --type class rc == 0 且名称匹配
SC2-3 下载源码 download --name ZTEST_SC2_CLS_xxx --type class rc == 0 且文件存在
SC2-4 修改后同步激活 修改源码 → sync 语法检查通过 + 激活成功
SC2-5 验证修改持久化 再次 download 源码包含修改标记
SC2-6 删除对象 delete --name ZTEST_SC2_CLS_xxx 删除成功
SC2-7 验证已不存在 info rc != 0

场景 3: 正向完整流程 — function + functiongroup

编号 步骤 操作 验证条件 结果
SC3-1 创建函数组 create --name ZTEST_SC3_FG_xxx --type functiongroup rc == 0
SC3-2 info 验证函数组存在 info --name ZTEST_SC3_FG_xxx --type functiongroup rc == 0 且名称匹配
SC3-3 创建函数 create --name ZTEST_SC3_FG_xxx/ZTEST_SC3_FM_xxx --type function rc == 0
SC3-4 info 验证函数存在 info rc == 0 且名称匹配
SC3-5 下载函数源码 download rc == 0 且文件存在
SC3-6 首次修改 → 同步激活 修改源码 → sync 语法检查通过 + 激活成功
SC3-7 再次下载验证修改持久化 download rc == 0 且文件包含修改标记
SC3-7b 验证修改内容已持久化 读取下载文件 包含 "SC3 TEST MARKER"
SC3-8 二次修改 → 同步激活 替换标记 → sync 语法检查通过 + 激活成功
SC3-9 info 验证函数仍存在 info --name ... --type function rc == 0 且名称匹配
SC3-10 info 验证函数组仍存在 info --name ... --type functiongroup rc == 0 且名称匹配
SC3-11 删除函数 delete 函数模块 删除成功
SC3-12 info 验证函数组在函数删除后仍存在 info --name ... --type functiongroup rc == 0(函数组独立于函数)
SC3-13 删除函数组 delete 函数组 删除成功
SC3-14 info 验证函数组已不存在 info rc != 0

场景 4: 正向完整流程 — interface

编号 步骤 操作 验证条件 结果
SC4-1 创建 interface create --name ZTEST_SC4_INT_xxx --type interface rc == 0
SC4-2 查询验证 info --name ZTEST_SC4_INT_xxx --type interface rc == 0 且名称匹配
SC4-3 下载源码 download --name ZTEST_SC4_INT_xxx --type interface rc == 0 且文件存在
SC4-4 修改后同步激活 修改源码 → sync 语法检查通过 + 激活成功
SC4-5 验证修改持久化 再次 download 源码包含修改标记
SC4-6 删除对象 delete --name ZTEST_SC4_INT_xxx 删除成功
SC4-7 验证已不存在 info rc != 0

场景组 error — 逆向错误处理

场景 5: 逆向 — 语法错误

编号 步骤 操作 验证条件 结果
SC5-1 创建 report create rc == 0
SC5-2 下载源码 download rc == 0 且文件存在
SC5-3 注入无效语法 → 同步 添加 INVALID_SYNTAX_HERE.sync rc != 0 且包含错误信息
SC5-4 恢复源码 → 重新同步 还原源码 → sync rc == 0 + 激活成功
SC5-5 清理删除 delete 删除成功

场景 6: 逆向 — 对象不存在

编号 步骤 操作 验证条件 结果
SC6-1 查询不存在的 class info --name Z_NOT_EXIST_99999 --type class rc != 0 且包含"不存在"
SC6-2 下载不存在的 report download --name Z_NOT_EXIST_99999 rc != 0
SC6-3 删除不存在的 report delete --name Z_NOT_EXIST_99999 rc != 0

场景 7: 逆向 — 重复创建

编号 步骤 操作 验证条件 结果
SC7-1 首次创建 create --name ZTEST_SC7_RPT_xxx rc == 0
SC7-2 重复创建 → 报错 再次 create 同名对象 rc != 0
SC7-3 清理删除 delete 删除成功

场景组 edge — 边界场景

场景 8: 边界 — 同步自动创建

编号 步骤 操作 验证条件 结果
SC8-1 准备本地源码 创建 .abap 文件 文件存在
SC8-2 sync 自动创建 sync 不存在的对象 rc == 0(自动创建)
SC8-3 info 验证存在 info rc == 0 且名称匹配
SC8-4 清理删除 delete 删除成功

场景 9: 边界 — DDIC 对象查询

编号 步骤 操作 验证条件 结果
SC9-1 info domain info --name ZSAPILOT_OBJECT_TYPE --type domain rc == 0 且名称匹配
SC9-2 info dataelement info --name ZSAPILOT_OBJECT_NAME --type dataelement rc == 0
SC9-3 info table info --name ZSAPILOT_OBJ --type table rc == 0
SC9-4 info tabletype info --name ZSAPILOT_OBJECT_TT --type tabletype rc == 0 且名称匹配

场景 10: 边界 — 删除安全确认

编号 步骤 操作 验证条件 结果
SC10-1 创建 report create rc == 0
SC10-2 delete 输入 no → 取消 delete stdin="no" 输出包含"已取消"
SC10-3 info 验证仍存在 info rc == 0 且名称匹配
SC10-4 delete 输入 yes → 成功 delete stdin="yes" 删除成功
SC10-5 info 验证已不存在 info rc != 0

场景 13: 边界 — functiongroup 不支持源码操作

编号 步骤 操作 验证条件 结果
SC13-1 download functiongroup → 报错 download --name ZIDT_MCP_TOOL --type functiongroup rc != 0 且包含"不支持"
SC13-2 sync functiongroup → 报错 sync --name ZIDT_MCP_TOOL --type functiongroup rc != 0 且包含"不支持"

场景组 existing — 已有对象操作

场景 11: 已有对象查询

编号 步骤 操作 验证条件 结果
SC11-1 info 已有 class info --name ZCL_SAP_MCP_HANDLER --type class rc == 0 且名称匹配
SC11-2 下载已有 class download --name ZCL_SAP_MCP_HANDLER rc == 0 且文件存在
SC11-3 同步无修改 → 成功 sync(源码未修改) 语法检查通过 + 激活成功

场景组 config — 配置与环境

场景 12: 配置与环境

编号 步骤 操作 验证条件 结果
SC12-1 默认配置运行 download(使用 config.ini rc == 0
SC12-2 无效 SAP_HOST 设置 SAP_HOST=http://invalid:9999 rc != 0

测试统计

按场景组统计

场景组 场景数 步骤总数 通过 失败 通过率
happy(正向完整流程) 4 36 36 0 100%
error(逆向错误处理) 3 11 11 0 100%
edge(边界场景) 4 14 14 0 100%
existing(已有对象) 1 3 3 0 100%
config(配置环境) 1 2 2 0 100%
合计 13 66 66 0 100%

失败项分析

本次测试全部通过,无失败项。

测试过程中发现并修复的问题

# 问题 修复内容
1 下载文件含 \r\r\n 双重回车 保存前统一换行符 source.replace("\r\n", "\n")
2 lock() 未传 corrNr 导致写入冲突 lock() 增加 corr_nr 参数
3 activate() 误判 inactiveObjects 为成功 新增 Content-Type 检测,正确处理 inactiveCtsObjects 响应
4 activate() 使用 preauditRequested=true 导致激活失败 移除该参数
5 _headers() 硬编码 Accept-Language: EN 移除硬编码,使用 SAP 系统登录语言
6 cmd_sync 交互式等待用户输入 改为非交互式,报错直接退出
7 cmd_sync 缺少语法检查步骤 新增 syntax_check() 方法,激活前先做语法检查
8 cmd_sync 对象不存在时直接退出 改为自动创建空对象后继续同步流程
9 create 缺少 interface 默认模板 新增 interface 模板到 DEFAULT_TEMPLATES
10 parse_object_name 对 functiongroup 崩溃(src_uri 为 None 增加 if config["src_uri_template"] else None
11 cmd_info 缺少名称校验 新增查询结果名称与请求名称的对比校验
12 cmd_info function 名称校验失败 split("/")[-1] 取实际对象名进行校验
13 DDIC 对象 info 查询 Accept 头不正确 改用 */* Accept 头