配套文档:
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)。<真实SN> / <真实GICCID> 为占位,需替换为实际测试样本。initializeprotocolVersion 用 client 默认,clientInfo={name:"mcp-test",version:"1.0"}serverInfo.name=="fwh-tools";serverInfo.version=="2.0.0";protocolVersion 非空。tools/list(循环翻页至 nextCursor 为空,聚合全部)tools_snapshot.json。service_,data_,storage_,util_,plane_};无其它前缀。description、inputSchema.type=="object";inputSchema.required 中的字段都存在于 properties。tools/call,name="__no_such_tool__",arguments={}tools_snapshot.json diff;新增/删除/改名的工具需人工确认(防止误删注册)。util_parse_sn{"sn":"10060006A03X25310091"}productCode=="1006";productName 含 "aGate";hwVersion=="0006";year=="2025";week=="31";serialNumber=="0091";summary 非空。util_parse_sn,分别传 1005.../1002.../1003.../9999...productName 分别为 aPower / aHub·PowerLink / MPPT / 未知产品。{}isError=true,消息含「参数 sn 必填」。{"sn":"1006"}isError=true,消息含「SN 长度不足」。service_site_query_info,入参 {}isError=true,「参数 sn 必填」。service_site_power_flow{"sn":"ANY","date":"2025/01/01"}isError=true,「date 格式错误 ... yyyy-MM-dd」(在调用后端前返回)。service_sim_monthly_data,入参 {"giccid":"x"}isError=true,「参数 month 必填」。data_mongo_find{"collection":"UserInfo"}isError=true,「不允许查询此集合」(UserInfo 已从白名单移除)。data_mongo_find{"collection":"SiteInfoManageData","filter":"{bad json"}isError=true,「filter ... JSON 解析失败」。data_redis_exists{"key":"foo*"}isError=true,「不支持通配符」。data_redis_get{"key":"any","type":"zset"}isError=true,「可选值: string, hash, list, set」。storage_minio_list_objects{"bucket":"secret-bucket"}isError=true,「不允许访问此 bucket」。data_emspara_vid_info{"id":"abc"}isError=true,「id ... 必须是数字」。data_pgsql_query_snapshot_data{"sn":"ANY","date":"20250101"}isError=true,「date ... yyyy-MM-dd」。storage_file_read{"path":"/uploads/../../etc/passwd"}(或 storage_file_read_to_local 同理)/uploads/ 外文件。service_feedback_create{"title":"t","type":"invalid"}isError=true,「type ... 必须是 bug 或 feature」(在调用 Plane 前返回,属参数校验,不产生写入)。每个用例通用期望:无 panic;返回合法 CallToolResult(成功 JSON / 业务空结果 / 结构化错误)。 连接类失败 → SKIP。业务空数据(如「查询结果为空」)算 PASS。
| 编号 | 工具 | 入参 | 期望 |
|---|---|---|---|
| L2-R1 | data_redis_exists |
{"key":"nonexistent_test_key"} |
返回「key 不存在」或 exists 信息,结构合法 |
| L2-R2 | data_redis_get |
{"key":"nonexistent_test_key"} |
返回空/不存在提示,结构合法 |
| 编号 | 工具 | 入参 | 期望 |
|---|---|---|---|
| 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}} |
结构合法 |
| 编号 | 工具 | 入参 | 期望 |
|---|---|---|---|
| L2-P1 | data_pgsql_query_snapshot_data |
{"sn":"<真实SN>","date":"<近期日期>"} |
返回数据或「查询结果为空」 |
| L2-P2 | data_pgsql_query_snapshot_file_record |
{"file_name":"<已知文件名>"} |
返回记录或「未找到文件记录」 |
| 编号 | 工具 | 入参 | 期望 |
|---|---|---|---|
| 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 |
{} |
返回文件列表或空 |
| 编号 | 工具 | 入参 | 期望 |
|---|---|---|---|
| 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 标签历史或空 |
| 编号 | 工具 | 入参 | 期望 |
|---|---|---|---|
| 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} |
返回分析任务列表 |
| 编号 | 工具 | 入参 | 期望 |
|---|---|---|---|
| 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-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 必填」 |
(可选)隔离环境真实写入用例(需你确认后启用):
service_site_monitor_config_create → _get 校验 → _delete 清理(闭环)storage_file_write → storage_file_list/storage_minio_list_objects 校验 → 删除go test ./cmd/mcp/tools/helper/...明文检测正则(对返回文本整体扫描,命中即 FAIL):
\b1\d{10}\b\b\d{14,15}\b([0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}\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 亦已脱敏 |
TestMaskJSONBytes_InvalidJSONFailsClosed)。report.md 每行:用例编号 | 工具 | 结果(PASS/FAIL/SKIP) | 耗时ms | 原因/备注
pii_report.md 每条 L4 用例附:命中的负向断言(应为空)、正向断言结果、脱敏后返回样本片段。
/var/www/fwh-toolbox/server,config.env 可读./fwh-mcp-server 存在且可执行(或已重建)/var/log/fwh-mcp-server/ 可写(否则 logger fallback)<真实SN> / <真实GICCID> / 已知 MinIO 文本对象 / 已知快照文件名go test ./cmd/mcp/tools/helper/... 通过(L4-0)