# ADT 工具集测试报告 > 测试目标:验证 `main.py` 的 create / info / download / sync / delete 五大功能在各种场景下的正确性。 > > 测试日期:2026-05-20 | 测试脚本:[test_main.py](file:///d:/codeSpace/Gitee/Projects2026/sap-cli/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 头 |