TEST_CASES.md 14 KB

FWH MCP Server 详细测试用例

配套文档:TEST_PLAN.md(总体方案)。本文件是可执行的用例清单。 运行路径:/var/www/fwh-toolbox/server(cwd,加载 config.env) 传输:stdio JSON-RPC;被测二进制:./fwh-mcp-server 用例编号规则:层级-序号;三态结果:PASS / FAIL / SKIP(原因)

约定:

  • 「入参」为 tools/call 的 arguments 对象。
  • 「期望」描述断言点;isError=true 指 CallToolResult 的错误结果(mcp.NewToolResultError)。
  • 依赖不可用(连接失败)→ 记 SKIP;进程 panic / 结构非法 / 超时 → FAIL。
  • <真实SN> / <真实GICCID> 为占位,需替换为实际测试样本。

L0 协议与工具清单

L0-1 初始化握手

  • 方法:initialize
  • 入参:protocolVersion 用 client 默认,clientInfo={name:"mcp-test",version:"1.0"}
  • 期望:返回成功;serverInfo.name=="fwh-tools";serverInfo.version=="2.0.0";protocolVersion 非空。

L0-2 工具清单可枚举

  • 方法:tools/list(循环翻页至 nextCursor 为空,聚合全部)
  • 期望:工具总数 > 0;无重复 name;导出到 tools_snapshot.json。

L0-3 命名规范

  • 数据来源:L0-2 聚合结果
  • 期望:每个 name 前缀 ∈ {service_,data_,storage_,util_,plane_};无其它前缀。

L0-4 Schema 完整性

  • 数据来源:L0-2
  • 期望:每个工具含非空 description、inputSchema.type=="object";inputSchema.required 中的字段都存在于 properties。

L0-5 未知工具调用

  • 方法:tools/call,name="__no_such_tool__",arguments={}
  • 期望:返回错误(协议层 error 或 isError),进程不崩溃。

L0-6 工具清单快照回归(可选)

  • 期望:与上次 tools_snapshot.json diff;新增/删除/改名的工具需人工确认(防止误删注册)。

L1 纯函数与参数校验(不依赖后端)

L1-1 SN 解析正确性

  • 工具:util_parse_sn
  • 入参:{"sn":"10060006A03X25310091"}
  • 期望:productCode=="1006";productName 含 "aGate";hwVersion=="0006";year=="2025";week=="31";serialNumber=="0091";summary 非空。

L1-2 SN 各产品码

  • 工具:util_parse_sn,分别传 1005.../1002.../1003.../9999...
  • 期望:productName 分别为 aPower / aHub·PowerLink / MPPT / 未知产品。

L1-3 SN 缺参

  • 入参:{}
  • 期望:isError=true,消息含「参数 sn 必填」。

L1-4 SN 长度不足

  • 入参:{"sn":"1006"}
  • 期望:isError=true,消息含「SN 长度不足」。

L1-5 站点查询缺 sn

  • 工具:service_site_query_info,入参 {}
  • 期望:isError=true,「参数 sn 必填」。

L1-6 功率流日期格式错误

  • 工具:service_site_power_flow
  • 入参:{"sn":"ANY","date":"2025/01/01"}
  • 期望:isError=true,「date 格式错误 ... yyyy-MM-dd」(在调用后端前返回)。

L1-7 SIM 月数据缺 month

  • 工具:service_sim_monthly_data,入参 {"giccid":"x"}
  • 期望:isError=true,「参数 month 必填」。

L1-8 mongo 非白名单 collection

  • 工具:data_mongo_find
  • 入参:{"collection":"UserInfo"}
  • 期望:isError=true,「不允许查询此集合」(UserInfo 已从白名单移除)。

L1-9 mongo filter 非法 JSON

  • 工具:data_mongo_find
  • 入参:{"collection":"SiteInfoManageData","filter":"{bad json"}
  • 期望:isError=true,「filter ... JSON 解析失败」。

L1-10 redis 通配符拦截

  • 工具:data_redis_exists
  • 入参:{"key":"foo*"}
  • 期望:isError=true,「不支持通配符」。

L1-11 redis 非法 type

  • 工具:data_redis_get
  • 入参:{"key":"any","type":"zset"}
  • 期望:isError=true,「可选值: string, hash, list, set」。

L1-12 minio 非白名单 bucket

  • 工具:storage_minio_list_objects
  • 入参:{"bucket":"secret-bucket"}
  • 期望:isError=true,「不允许访问此 bucket」。

L1-13 emspara id 非数字

  • 工具:data_emspara_vid_info
  • 入参:{"id":"abc"}
  • 期望:isError=true,「id ... 必须是数字」。

L1-14 pgsql 日期格式错误

  • 工具:data_pgsql_query_snapshot_data
  • 入参:{"sn":"ANY","date":"20250101"}
  • 期望:isError=true,「date ... yyyy-MM-dd」。

L1-15 storage_file 路径穿越拦截

  • 工具:storage_file_read
  • 入参:{"path":"/uploads/../../etc/passwd"}(或 storage_file_read_to_local 同理)
  • 期望:安全拒绝(不存在/非法路径),不读到 /uploads/ 外文件。

L1-16 反馈类型校验

  • 工具:service_feedback_create
  • 入参:{"title":"t","type":"invalid"}
  • 期望:isError=true,「type ... 必须是 bug 或 feature」(在调用 Plane 前返回,属参数校验,不产生写入)。

L2 只读工具连通性冒烟(依赖外部服务)

每个用例通用期望:无 panic;返回合法 CallToolResult(成功 JSON / 业务空结果 / 结构化错误)。 连接类失败 → SKIP。业务空数据(如「查询结果为空」)算 PASS。

Redis 组

编号 工具 入参 期望
L2-R1 data_redis_exists {"key":"nonexistent_test_key"} 返回「key 不存在」或 exists 信息,结构合法
L2-R2 data_redis_get {"key":"nonexistent_test_key"} 返回空/不存在提示,结构合法

Mongo 组(白名单 collection)

编号 工具 入参 期望
L2-M1 data_mongo_count {"collection":"SiteInfoManageData"} 返回 {collection,count},count≥0
L2-M2 data_mongo_find {"collection":"SiteInfoManageData","limit":1} 返回数组或「查询结果为空」;若含 PII 须已脱敏(见 L4-5)
L2-M3 data_mongo_find {"collection":"StatisticsAlarm","limit":1,"sort":{"createdAt":-1}} 结构合法

PgSQL 组

编号 工具 入参 期望
L2-P1 data_pgsql_query_snapshot_data {"sn":"<真实SN>","date":"<近期日期>"} 返回数据或「查询结果为空」
L2-P2 data_pgsql_query_snapshot_file_record {"file_name":"<已知文件名>"} 返回记录或「未找到文件记录」

MinIO / 文件组

编号 工具 入参 期望
L2-S1 storage_minio_list_objects {"bucket":"fwh-toolbox","limit":5} 返回对象列表或「无匹配对象」
L2-S2 storage_minio_read_object {"bucket":"fwh-toolbox","key":"<已知文本对象>"} 返回文本内容;非文本/超1MB 返回相应校验错误
L2-S3 storage_file_list {} 返回文件列表或空

Plane 组(真实凭据)

编号 工具 入参 期望
L2-PL1 plane_list_workspaces {} 返回 workspace 列表;登录失败→SKIP
L2-PL2 plane_list_projects {} 返回项目列表
L2-PL3 plane_list_issues {}(或带 project) 返回 issue 列表
L2-PL4 plane_list_labels {} 返回标签列表
L2-PL5 plane_list_states {} 返回状态列表
L2-PL6 service_feedback_history {} 返回 Feedback 标签历史或空

业务层(依赖远程 MySQL)

编号 工具 入参 期望
L2-B1 service_site_search {"page":1,"size":5} 返回分页站点列表;PII 须脱敏(L4-2)
L2-B2 service_sim_search {"page":1,"size":5} 返回分页 SIM 列表;脱敏(L4-4)
L2-B3 service_monitor_alarm_stats {} 返回最新告警统计或空
L2-B4 service_monitor_shutdown_stats {} 返回最新停机统计或空
L2-B5 service_dashboard_shutdown_date {} 返回日期字符串或「暂无数据」
L2-B6 service_dashboard_shutdown_detail {} 结构合法
L2-B7 service_dashboard_alarm_detail {} 结构合法
L2-B8 service_tou_template_list {"page":1,"size":5} 返回模版列表
L2-B9 service_background_task_list {} 返回任务列表或「当前无后台任务」
L2-B10 service_site_monitor_config_list {} 返回配置列表
L2-B11 service_site_monitor_name_list {} 返回名称列表
L2-B12 service_site_info_update_status {} 返回更新状态
L2-B13 service_site_info_tags_list {"page":1,"size":5} 返回标签列表
L2-B14 service_auto_update_get_options {} 返回升级包/版本选项
L2-B15 service_auto_update_get_apower_version {} 返回 aPower 版本列表
L2-B16 service_auto_update_file_list {"page":1,"size":5} 返回文件列表
L2-B17 service_batch_download_list {"page":1,"size":5} 返回任务列表
L2-B18 service_site_analysis_list {"page":1,"size":5} 返回分析任务列表

FWH 远程 API 组(凭据/网络需实测,失败→SKIP)

编号 工具 入参 期望
L2-F1 service_site_query_info {"sn":"<真实SN>"} 返回站点详情;PII 脱敏(L4-1)
L2-F2 service_site_power_flow {"sn":"<真实SN>","date":"<近期日期>"} 返回功率流数据
L2-F3 service_sim_query {"giccid":"<真实GICCID>"} 返回 SIM 详情;脱敏(L4-3)
L2-F4 service_sim_monthly_data {"giccid":"<真实GICCID>","month":"<yyyy-MM>"} 返回月用量
L2-F5 data_fwh_query_site_base_info_list 见工具描述最小必填 结构合法或结构化错误
L2-F6 其余 data_fwh_*(约 20 个) 各取最小必填参数 逐一冒烟:结构合法 / SKIP
L2-F7 data_sim_*(SmartHub/Acceleronix) 最小必填 结构合法 / SKIP
L2-F8 data_emspara_vid_info / pid_info {"id":"1"} 返回宏名/描述或「未找到」

L2-F6/F7 需按 data/fwh_api.go、data/sim_api.go 的每个工具展开为逐条用例(driver 中据 tools/list 自动生成最小入参)。


L3 写操作(默认只测参数校验,不真实写入)

除非确认隔离环境,L3 仅验证「必填缺失被拦截」,不构造合法写入。

编号 工具 入参 期望
L3-1 service_feedback_create {"title":"t"}(缺 type) isError,「参数 type 必填」
L3-2 service_feedback_create {"title":"t","type":"x"} isError,「必须是 bug 或 feature」
L3-3 service_site_info_tag_update {} isError,「参数 tag 必填」
L3-4 service_auto_update_create_package {"package_type":"system"}(缺 info) isError,「参数 info 必填」
L3-5 service_auto_update_update_config {"agate_version":"v"}(缺 config) isError,「参数 config 必填」
L3-6 service_site_monitor_config_create {"name":"n"}(缺 site_list) isError,「参数 site_list 必填」
L3-7 service_site_monitor_config_delete {} isError,「参数 name 必填」
L3-8 service_batch_download_create 缺必填 isError
L3-9 service_site_analysis_create 缺必填 isError
L3-10 storage_file_write {"path":"/uploads/x"}(缺 content) isError,「参数 content 必填」
L3-11 storage_file_upload {}(缺 localPath) isError,「参数 localPath 必填」

(可选)隔离环境真实写入用例(需你确认后启用):

  • L3-W1 service_site_monitor_config_create → _get 校验 → _delete 清理(闭环)
  • L3-W2 storage_file_write → storage_file_list/storage_minio_list_objects 校验 → 删除
  • 每条真实写入用例必须带清理步骤,避免污染数据。

L4 敏感数据脱敏验证(安全门禁)

L4-0 脱敏单元测试

  • 命令:go test ./cmd/mcp/tools/helper/...
  • 期望:全部 PASS(掩码函数、流式脱敏、强类型脱敏、A/B 一致性、fail-closed、null 保留)。

端到端脱敏(负向断言:明文 PII 不得出现;正向断言:保留字段原值)

明文检测正则(对返回文本整体扫描,命中即 FAIL):

  • 手机号:\b1\d{10}\b
  • IMEI/IMSI:\b\d{14,15}\b
  • MAC:([0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}
  • IP:\b(?!0\.0\.0\.0)\d{1,3}(\.\d{1,3}){3}\b
  • 邮箱本地部分明文:[\w.+-]{2,}@(脱敏后应为 x***@)
编号 工具 入参 负向断言(不得出现) 正向断言(应保留/应呈现)
L4-1 service_site_query_info {"sn":"<真实SN>"} 明文手机号/邮箱/地址/经纬度/IMEI/MAC/IP sn/iccId/city 原值;contactNumber/address/imei==******;userName~X***;email~x***@域名
L4-2 service_site_search {"page":1,"size":5} 同上(对 Data[] 每项) 每项均脱敏;分页字段 total/page 正常
L4-3 service_sim_query {"giccid":"<真实GICCID>"} 明文 imei/imsi/msisdn imei/imsi/msisdn==******;giccid 原值保留
L4-4 service_sim_search {"page":1,"size":5} 同上(对 Data[]) 每项脱敏,giccid 保留
L4-5 data_mongo_find {"collection":"SiteInfoManageData","limit":1} 明文 PII(走 B 路径流式脱敏) 敏感 key 的 value 已脱敏;sn/iccid 等保留
L4-6 data_mongo_find {"collection":"SimCardManage","limit":1} 明文 imei/imsi/msisdn giccid 保留
L4-7 触发大结果落盘的查询(如 service_site_search 大 size 或 data_fwh_* 大结果) — 读取返回的 localPath 文件,文件内同样无明文 PII spilled==true;preview 亦已脱敏

L4-8 fail-closed(设计验证)

  • 主要由 L4-0 单测保证(TestMaskJSONBytes_InvalidJSONFailsClosed)。
  • 端到端标注为「设计验证」:脱敏解析失败时返回 error,不返回原始数据。

报告字段建议

report.md 每行:用例编号 | 工具 | 结果(PASS/FAIL/SKIP) | 耗时ms | 原因/备注 pii_report.md 每条 L4 用例附:命中的负向断言(应为空)、正向断言结果、脱敏后返回样本片段。

依赖前置检查清单(执行前跑一遍)

  • cwd == /var/www/fwh-toolbox/server,config.env 可读
  • ./fwh-mcp-server 存在且可执行(或已重建)
  • Redis:6379 / Mongo:27017 / MinIO:9198 / PgSQL:5432 / Plane:8600 可连通
  • /var/log/fwh-mcp-server/ 可写(否则 logger fallback)
  • 已准备 <真实SN> / <真实GICCID> / 已知 MinIO 文本对象 / 已知快照文件名
  • go test ./cmd/mcp/tools/helper/... 通过(L4-0)