联机测试方案
1. 系统概述
本方案设计基于 go-libp2p 的分布式工业配置与控制权同步系统的完整联机测试流程,覆盖编译构建、远程部署、功能测试、异常场景验证等全部环节。
核心定位: 这是一个”分布式配置 + 控制权系统”,而不是”数据同步系统”
- Config → CRDT-like,最终一致性
- Ownership → Lease,强约束
- Runtime → Owner Only,单点主控
2. 测试环境准备
2.1 双节点拓扑
┌──────────────────────────┐ libp2p:4001 ┌────────────────────────────┐
│ 本机 (开发机) │◄─────────────────────────►│ 远程 (root@192.168.3.230) │
│ │ │ │
│ OS: Windows/Linux │ 节点发现 + 配置同步 │ OS: Linux ARM64 │
│ 启动: go run cmd/main.go │ │ 启动: systemctl edgex │
│ 节点: NODE-1 │ │ 节点: NODE-REMOTE │
│ API: http://localhost:8082 │ │ API: http://192.168.3.230:8082│
└──────────────────────────┘ └────────────────────────────┘
2.2 环境要求
| 项目 | 本机 (开发机) | 远程 (192.168.3.230) |
|---|---|---|
| 操作系统 | Windows/Linux/macOS | Linux ARM64 |
| Go 版本 | 1.21+ | 无需(已编译为二进制) |
| Node.js | 16+ (构建前端用) | 无需 |
| goreleaser | 已安装 (goreleaser --version) |
无需 |
| SSH 密钥 | 可免密登录 root@192.168.3.230 |
接受 SSH 连接 |
| 防火墙 | 放行 4001/tcp 4001/udp |
放行 4001/tcp 4001/udp 8082/tcp |
| 依赖 | 无特殊要求 | systemd, dpkg, curl |
3. 编译与构建
3.1 方式一:本地开发运行(go run)
适用于快速开发调试,版本信息为默认值 dev/unknown/unknown。
# 1. 安装前端依赖并构建(首次或前端有变更时执行)
cd ui && npm install && npm run build && cd ..
# 2. 直接运行(开发模式,版本=dev)
go run cmd/main.go
# 3. 或指定 ldflags 注入真实版本信息
go run -ldflags="-X github.com/anviod/edgex/internal/model.Version=0.0.4 \
-X 'github.com/anviod/edgex/internal/model.BuildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)' \
-X github.com/anviod/edgex/internal/model.CommitID=$(git rev-parse --short HEAD)" \
cmd/main.go
验证本地启动成功:
# 检查服务端口
curl -s http://localhost:8082/api/auth/system-info
# 预期返回:{"code":"0","data":{"name":"NODE-1","softVer":"dev","buildTime":"unknown","commitID":"unknown"}}
# 检查 libp2p 监听
netstat -an | grep 4001
3.2 方式二:goreleaser 构建多平台安装包
适用于正式版本构建,自动注入版本信息并生成所有平台的 .deb / .tar.gz。
# 完整构建(构建前端 + 编译 Go + 打包所有平台)
goreleaser release --snapshot --clean --config .goreleaser.yml
构建流程详解:
步骤 1: go mod tidy # 整理依赖
步骤 2: npm run build (./ui) # 构建 Vue 前端 → ui/dist/
步骤 3: GOOS/GOARCH 矩阵编译 # CGO_ENABLED=0,静态链接
├── linux/amd64
├── linux/arm64
├── linux/arm (ARMv7)
├── windows/amd64
└── windows/arm64
步骤 4: ldflags 注入版本信息 # Version / BuildTime / CommitID
步骤 5: 打包 tar.gz # 归档包(含 conf/ scripts/ ui/dist/)
步骤 6: 打包 .deb # dpkg 包(含 preinstall/postinstall/edgex.service)
步骤 7: SHA256SUMS # 校验和文件
产物列表(在 dist/ 目录下):
dist/
├── edgex-v0.0.4-amd64.deb # AMD64 deb 安装包
├── edgex-v0.0.4-arm64.deb # ARM64 deb 安装包(远程节点使用)
├── edgex-v0.0.4-arm.deb # ARMv7 deb 安装包
├── edgex-0.0.4-linux-amd64.tar.gz # AMD64 归档
├── edgex-0.0.4-linux-arm64.tar.gz # ARM64 归档
├── edgex-0.0.4-linux-arm.tar.gz # ARMv7 归档
├── edgex-0.0.4-windows-amd64.tar.gz
├── edgex-0.0.4-windows-arm64.tar.gz
└── SHA256SUMS
构建后验证版本注入:
# 解压本地对应架构的包,验证版本信息写入正确
tar -xzf dist/edgex-0.0.4-linux-amd64.tar.gz -C /tmp/edgex-test
/tmp/edgex-test/edgex --version 2>/dev/null || \
strings /tmp/edgex-test/edgex | grep -E "^(0\.0\.[0-9]+|$(git rev-parse --short HEAD))"
4. 远程部署(deb 方式)
4.1 一键部署
# 语法: bash scripts/deploy-remote.sh <远程主机> <节点名称>
bash scripts/deploy-remote.sh root@192.168.3.230 NODE-REMOTE
4.2 部署流程详解
[步骤 1] 检查 SSH 连接
├── ssh -q root@192.168.3.230 "echo 'SSH连接正常'"
└── 失败 → 终止部署,提示检查 SSH 密钥
[步骤 2] 检测远程架构
├── ssh root@192.168.3.230 "uname -m"
├── aarch64 → arm64
├── armv7l → arm
└── x86_64 → amd64
[步骤 3] 查找匹配的 .deb 包
├── ls dist/edgex-v*-arm64.deb | sort -V | tail -1
└── 未找到 → 终止部署,提示先执行 goreleaser 构建
[步骤 4] SCP 传输安装包
└── scp dist/edgex-v0.0.4-arm64.deb root@192.168.3.230:/tmp/
[步骤 5] 备份现有配置
├── cp -rf /usr/local/bin/edgex/data → /tmp/edgex_backup/
└── cp -rf /usr/local/bin/edgex/config → /tmp/edgex_backup/
[步骤 6] 停止旧服务
└── systemctl stop edgex
[步骤 7] 卸载旧版本
└── apt remove -y edgex
[步骤 8] 安装新版本
├── dpkg -i /tmp/edgex-v0.0.4-arm64.deb
└── 依赖缺失 → apt-get install -f -y 自动修复
[步骤 9] 配置节点名称
└── sed -i "s/node_name:.*/node_name: NODE-REMOTE/" /usr/local/bin/edgex/config/sync.yaml
[步骤 10] 启动服务
├── systemctl daemon-reload
├── systemctl enable edgex
└── systemctl start edgex
[步骤 11] 验证服务状态
└── sleep 5 && systemctl is-active edgex → active
4.3 部署后验证
# 1. 服务状态
ssh root@192.168.3.230 "systemctl status edgex --no-pager"
# 2. API 可达性(含版本信息验证)
ssh root@192.168.3.230 "curl -s http://localhost:8082/api/auth/system-info | python3 -m json.tool"
# 预期返回:
# {
# "code": "0",
# "data": {
# "name": "NODE-REMOTE",
# "softVer": "0.0.4",
# "buildTime": "2026-06-05T...",
# "commitID": "efa219a"
# }
# }
# 3. libp2p 端口监听
ssh root@192.168.3.230 "ss -tlnp | grep 4001"
# 4. 服务日志(检查是否有 libp2p 相关输出)
ssh root@192.168.3.230 "journalctl -u edgex -n 50 --no-pager"
4.4 手动部署(备用方式)
当脚本不可用时的手动部署流程:
# 1. 手动 SCP 传输
scp dist/edgex-v0.0.4-arm64.deb root@192.168.3.230:/tmp/
# 2. SSH 进去执行
ssh root@192.168.3.230
# 3. 备份配置
cp -rf /usr/local/bin/edgex/data /tmp/edgex_backup/ 2>/dev/null || true
cp -rf /usr/local/bin/edgex/config /tmp/edgex_backup/ 2>/dev/null || true
# 4. 停止并卸载
systemctl stop edgex 2>/dev/null || true
apt remove -y edgex 2>/dev/null || true
# 5. 安装
dpkg -i /tmp/edgex-v0.0.4-arm64.deb || apt-get install -f -y
# 6. 配置节点名
sed -i 's/node_name:.*/node_name: NODE-REMOTE/' /usr/local/bin/edgex/config/sync.yaml
# 7. 启用并启动
systemctl daemon-reload && systemctl enable edgex && systemctl start edgex
# 8. 退出 SSH
exit
5. 功能测试
测试原则:每个测试用例必须严格按照文档设计验证。测试执行顺序遵循依赖关系(先基础后高级)。
5.1 测试前置条件
执行所有测试前,确保:
# 本机确认
curl -s http://localhost:8082/api/auth/system-info # NODE-1 在线
# 远程确认
curl -s http://192.168.3.230:8082/api/auth/system-info # NODE-REMOTE 在线
5.2 测试用例:节点发现与加入(P0)
TC-01:mDNS 局域网自动发现
| 项目 | 内容 |
|---|---|
| 目标 | 验证两个节点通过 mDNS 互相发现 |
| 前置条件 | NODE-1 (本机) 和 NODE-REMOTE (远程) 均已启动,同处 192.168.3.0/24 网段 |
| 测试步骤 | 1. 启动 NODE-1 的 go run cmd/main.go2. 确认 NODE-REMOTE 已通过 systemctl 启动 3. 等待 30s(discovery_interval) 4. 调用 NODE-1 的集群 API |
| API 调用 | GET http://localhost:8082/api/sync/peers |
| 预期结果 | 返回的 peers 列表包含 NODE-REMOTE 的 PeerID,状态为 online |
| 通过标准 | peers 数量 ≥ 2,NODE-REMOTE 在线 |
| 失败排查 | 检查防火墙是否放行 4001/udp(mDNS);检查两台机器是否同一子网 |
TC-02:Bootstrap 静态发现
| 项目 | 内容 |
|---|---|
| 目标 | 验证通过 bootstrap_peers 配置的静态节点发现 |
| 前置条件 | conf/sync-config.yaml 已配置 bootstrap_peers(含远程节点 PeerID) |
| 测试步骤 | 1. 在远程节点临时禁用 mDNS 2. 重启 NODE-1 3. 等待连接建立 |
| API 调用 | GET http://localhost:8082/api/sync/peers |
| 预期结果 | NODE-1 通过静态种子发现并连接到 NODE-REMOTE |
| 通过标准 | NODE-REMOTE 出现在 peers 列表中 |
TC-03:新节点加入网络(全量同步)
| 项目 | 内容 |
|---|---|
| 目标 | 验证新节点加入后自动触发全量配置同步 |
| 前置条件 | NODE-REMOTE 已有 3 个 channel、5 个 device、120 个 point 的配置 |
| 测试步骤 | 1. 启动 NODE-1(新节点) 2. NODE-1 发现 NODE-REMOTE 3. NODE-1 自动发起全量同步 |
| API 调用 | GET http://localhost:8082/api/sync/node/NODE-REMOTE/tree |
| 预期结果 | NODE-1 的配置树包含与 NODE-REMOTE 完全相同的 channel、device、point |
| 通过标准 | 配置树节点数量和结构与 NODE-REMOTE 一致,所有 hash 匹配 |
| 一致性验证 | GET http://localhost:8082/api/sync/consistency → overall_status: "consistent" |
5.3 测试用例:配置同步(P0)
TC-04:全量配置同步
| 项目 | 内容 |
|---|---|
| 目标 | 验证全量同步模式正确传输所有配置 |
| 前置条件 | 双节点在线,NODE-1 为新节点(配置为空或版本旧) |
| 测试步骤 | 1. 触发 NODE-1 → NODE-REMOTE 的全量同步 |
| API 调用 | POST http://localhost:8082/api/sync/trigger {"mode":"full"} |
| 预期结果 | sync_status 返回 mode: "full", status: "completed" |
| 一致性检查 | 两节点 channels.yaml 内容一致 |
TC-05:增量同步(Delta Sync)
| 项目 | 内容 |
|---|---|
| 目标 | 验证修改单个配置项后仅同步变更部分 |
| 前置条件 | 双节点同步已完成(一致状态) |
| 测试步骤 | 1. 在 NODE-1 修改 channel modbus-1 的 port 从 502 → 5032. 等待 Announce 广播 3. NODE-REMOTE 检测 hash 不一致后拉取 delta |
| API 调用 | NODE-REMOTE: GET /api/sync/node/NODE-1/diff |
| 预期结果 | diff 仅包含 channel.modbus-1.port: 502→503;不包含其他未变更的配置 |
| 通过标准 | diff 列表精确对应变更项,无多余项,无遗漏 |
| 验证 | NODE-REMOTE 的 channel modbus-1 port 变为 503 |
TC-06:点位级别同步
| 项目 | 内容 |
|---|---|
| 目标 | 验证最小粒度(单个 point)的同步能力 |
| 前置条件 | 双节点同步已完成,设备 PLC-01 含 point temp |
| 测试步骤 | 1. 在 NODE-1 修改 PLC-01 的 point temp.scale 从 0.1 → 1.02. 等待自动增量同步 |
| API 调用 | GET /api/sync/node/NODE-1/device/PLC-01/points |
| 预期结果 | NODE-REMOTE 上的 temp.scale 更新为 1.0;其他 point 不受影响 |
| 通过标准 | 只有一个 point 发生变更,其他 point 不变 |
5.4 测试用例:控制权管理(P0)
TC-07:Exclusive 设备 - 禁止接管
| 项目 | 内容 |
|---|---|
| 目标 | 验证 exclusive 模式设备不允许被其他节点接管 |
| 前置条件 | NODE-1 拥有一个 Modbus RTU 设备(access_mode: exclusive),正在采集 |
| 测试步骤 | 1. NODE-REMOTE 尝试接管该设备 |
| API 调用 | POST http://192.168.3.230:8082/api/ownership/takeover {"device_id":"RTU-01","force":false} |
| 预期结果 | 返回 {"success": false, "message": "独占设备不允许接管"} |
| 通过标准 | HTTP 200 但 success=false;NODE-1 设备采集不受影响 |
TC-08:Shared 设备 - 直接接管
| 项目 | 内容 |
|---|---|
| 目标 | 验证 shared 模式设备(如 OPC UA)可不经确认直接接管 |
| 前置条件 | NODE-1 拥有 OPC UA 设备(access_mode: shared),owner=NODE-1 |
| 测试步骤 | 1. NODE-REMOTE 发起接管 |
| API 调用 | POST http://192.168.3.230:8082/api/ownership/takeover {"device_id":"OPCUA-01"} |
| 预期结果 | 返回 {"success": true, "new_owner": "NODE-REMOTE"};OPCUA-01 的 owner 变更为 NODE-REMOTE |
| 通过标准 | takeover 成功;NODE-1 上的 owner 同步更新为 NODE-REMOTE;OWNER_ANNOUNCE 消息广播 |
TC-09:Lease 设备 - 租约抢占
| 项目 | 内容 |
|---|---|
| 目标 | 验证 lease 模式设备(如 Modbus TCP)需等待租约过期或抢占 |
| 前置条件 | NODE-1 持有 Modbus TCP 设备 PLC-01 的 lease,lease_ttl=30s,renew_interval=15s |
| 测试步骤 | 1. NODE-REMOTE 发起接管 2. 此时 lease 未过期 |
| API 调用 | POST http://192.168.3.230:8082/api/ownership/takeover {"device_id":"PLC-01","force":true} |
| 预期结果 | NODE-REMOTE 的请求等待 lease 过期,或 force=true 时立即抢占takeover_result.lease_acquired=true |
| 通过标准 | 接管成功后 NODE-1 调度器暂停 PLC-01 采集;NODE-REMOTE 开始采集 |
| 调度器验证 | 检查 NODE-1 日志含 pause collect [PLC-01]、NODE-REMOTE 日志含 start collect [PLC-01] |
TC-10:Lease 续约与过期
| 项目 | 内容 |
|---|---|
| 目标 | 验证租约续约机制和过期自动释放 |
| 前置条件 | NODE-1 持有 PLC-01 的 lease |
| 测试步骤 | 1. 停止 NODE-1(模拟故障) 2. 等待 lease_ttl(30s)过期 3. NODE-REMOTE 尝试获取 lease |
| API 调用 | POST http://192.168.3.230:8082/api/lease/acquire {"device_id":"PLC-01"} |
| 预期结果 | 30s 后 lease 自动过期,NODE-REMOTE 成功获取 lease |
| 通过标准 | NODE-REMOTE 成为新 owner,开始采集 |
5.5 测试用例:设备更换与配置迁移(P0)
TC-11:设备编码验证
| 项目 | 内容 |
|---|---|
| 目标 | 验证设备编码体系正确解析和校验 |
| 前置条件 | 已有设备的编码体系中注册了一台 S7-1200 |
| 测试步骤 | 传入有效和无效的设备编码 |
| API 调用 | POST /api/device/code/validate {"device_code":"modbus-siemens-s71200-SN123456789-ABC123"} |
| 预期结果 | 有效的返回 valid:true 含 device_info;无效的返回 valid:false + 错误信息 |
| 通过标准 | 解析结果正确:protocol=modbus-tcp, vendor=Siemens, model=S7-1200, access_mode=lease |
TC-12:设备配置迁移(含接管)
| 项目 | 内容 |
|---|---|
| 目标 | 验证通过设备编码实现完整配置迁移 |
| 前置条件 | NODE-1 拥有完整配置的设备 PLC-01;新设备编码与 PLC-01 匹配 |
| 测试步骤 | 1. 在 NODE-REMOTE 发起迁移请求 2. 系统验证设备编码 → 查找历史配置 → 检查 AccessMode → 处理 Lease |
| API 调用 | POST /api/device/migrate{"device_code":"modbus-siemens-s71200-SN123456789-ABC123", "target_device_id":"new-plc-01", "migration_scope":["channel","device","northbound"], "preserve_history":true} |
| 预期结果 | success:true,migrated_items 含 channel/device/northbound 配置 |
| 通过标准 | NODE-REMOTE 拥有与 NODE-1 的 PLC-01 完全相同的通道、设备、北向配置 |
TC-13:迁移回滚
| 项目 | 内容 |
|---|---|
| 目标 | 验证配置迁移失败后的回滚机制 |
| 前置条件 | 已完成一次迁移,持有 rollback_key |
| 测试步骤 | 使用 rollback_key 回滚 |
| API 调用 | POST /api/device/migrate/rollback {"rollback_key":"<上一步返回的key>"} |
| 预期结果 | 配置恢复到迁移前状态 |
| 通过标准 | 所有迁移项被回滚;设备配置与迁移前一致 |
5.6 测试用例:配置版本管理(P1)
TC-14:Vector Clock 版本比较
| 项目 | 内容 |
|---|---|
| 目标 | 验证 Vector Clock 正确识别版本新旧 |
| 前置条件 | 版本向量:NODE-1={NODE-1:12, NODE-2:9},NODE-2={NODE-1:10, NODE-2:9} |
| 测试步骤 | 触发一致性检查 |
| API 调用 | GET /api/sync/consistency |
| 预期结果 | 报告显示 NODE-1 的版本更新(因为在所有维度上都 >= NODE-2) |
| 通过标准 | NODE-1 的配置不会被 NODE-2 覆盖;NODE-2 的版本被标记为 older |
TC-15:Vector Clock 冲突检测
| 项目 | 内容 |
|---|---|
| 目标 | 验证并发修改产生 Vector Clock 冲突时正确检测 |
| 前置条件 | NODE-1 和 NODE-2 同时修改同一个 point(产生并发版本) |
| 测试步骤 | 1. 同时修改 NODE-1 和 NODE-REMOTE 的同一个 point 2. 触发一致性检查 |
| API 调用 | GET /api/sync/consistency |
| 预期结果 | 该 point 被标记为 CONFLICT,overall_status 为 inconsistent |
| 通过标准 | 冲突被正确识别,UI 显示 ⚠️ CONFLICT 标记 |
TC-16:配置版本回滚
| 项目 | 内容 |
|---|---|
| 目标 | 验证配置可回滚到指定历史版本 |
| 前置条件 | NODE-1 有至少 3 个版本的配置历史 |
| 测试步骤 | 1. 查询历史版本列表 2. 回滚到 v1 |
| API 调用 | GET /api/sync/node/NODE-1/history → POST /api/sync/node/NODE-1/rollback {"version":1} |
| 预期结果 | 配置恢复为 v1 版本;version 字段更新为 v1 |
| 通过标准 | 回滚后配置内容与历史 v1 完全一致 |
5.7 测试用例:数据一致性(P0)
TC-17:一致性检查
| 项目 | 内容 |
|---|---|
| 目标 | 验证一致性检查正确识别不一致项 |
| 前置条件 | NODE-1 和 NODE-REMOTE 存在已知差异 |
| 测试步骤 | 调用全量一致性检查 |
| API 调用 | GET /api/sync/consistency |
| 预期结果 | 返回 overall_status + inconsistent_items 详细列表 + repair_suggestions |
| 通过标准 | 所有已知差异项出现在 inconsistent_items 中 |
| 状态码 | consistent(一致)/ inconsistent(不一致)/ degraded(部分降级) |
5.8 测试用例:Diff 引擎(P1)
TC-18:结构 Diff(新增/删除)
| 项目 | 内容 |
|---|---|
| 目标 | 验证增加新设备和删除点位被正确识别 |
| 前置条件 | NODE-1 比 NODE-REMOTE 多一个 device,少一个 point |
| 测试步骤 | 对比两节点差异 |
| API 调用 | GET /api/sync/node/NODE-1/diff |
| 预期结果 | diff 列表包含 + 新设备 PLC-03(add)、- 删除点位 temp(remove) |
| 通过标准 | add/remove 类型 diff 准确 |
TC-19:属性 Diff(字段变更)
| 项目 | 内容 |
|---|---|
| 目标 | 验证字段值变更被正确识别 |
| 前置条件 | point temp.scale: NODE-1=0.1, NODE-REMOTE=1.0 |
| 测试步骤 | 对比差异 |
| API 调用 | GET /api/sync/node/NODE-1/diff |
| 预期结果 | type: "update", path: "point.temp.scale", before: 0.1, after: 1.0, level: "info" |
| 通过标准 | update 类型 diff 准确 |
TC-20:语义 Diff(破坏性变更)
| 项目 | 内容 |
|---|---|
| 目标 | 验证类型变更被标记为危险级别 |
| 前置条件 | point val: NODE-1=Integer, NODE-REMOTE=Float |
| 测试步骤 | 对比差异 |
| API 调用 | GET /api/sync/node/NODE-1/diff |
| 预期结果 | type: "conflict", level: "critical",UI 显示 ⚠️ 类型变更(破坏性) |
| 通过标准 | 语义冲突被正确标记为 critical 级别 |
5.9 测试用例:错误处理与故障恢复(P1)
TC-21:节点离线检测
| 项目 | 内容 |
|---|---|
| 目标 | 验证节点离线被正确检测并标记 |
| 前置条件 | 双节点在线 |
| 测试步骤 | 1. 停止 NODE-REMOTE(systemctl stop edgex)2. 等待心跳超时(30s) |
| API 调用 | NODE-1: GET /api/sync/peers |
| 预期结果 | NODE-REMOTE 状态变为 offline |
| 通过标准 | 离线检测时间 ≤ 45s(peer_timeout 30s + 检测周期) |
TC-22:节点恢复与断点续传
| 项目 | 内容 |
|---|---|
| 目标 | 验证节点恢复后从断点继续同步 |
| 前置条件 | TC-21 后的状态 |
| 测试步骤 | 1. 重新启动 NODE-REMOTE 2. 在 NODE-1 修改一项配置(离线期间产生差异) |
| API 调用 | 观察 sync session 恢复 |
| 预期结果 | NODE-REMOTE 上线后自动检测差异,发起增量同步,从断点继续 |
| 通过标准 | NODE-REMOTE 恢复后 60s 内达到一致状态;不会触发全量同步 |
TC-23:网络超时重试
| 项目 | 内容 |
|---|---|
| 目标 | 验证指数退避重试机制 |
| 前置条件 | 双节点在线 |
| 测试步骤 | 1. 临时阻断 NODE-1 → NODE-REMOTE 网络(iptables drop 4001) 2. 触发同步 3. 观察重试行为 |
| 预期结果 | 重试间隔呈指数增长:1s → 2s → 4s(最多 3 次) |
| 通过标准 | 重试 3 次后标记失败;恢复网络后不丢数据 |
TC-24:数据冲突自动解决
| 项目 | 内容 |
|---|---|
| 目标 | 验证非并发冲突自动以最新版本为准 |
| 前置条件 | NODE-1 版本 > NODE-REMOTE 版本(非并发) |
| 测试步骤 | 触发同步 |
| API 调用 | POST /api/sync/trigger {"mode":"delta"} |
| 预期结果 | NODE-REMOTE 的旧版本被更新为新版本(自动解决) |
| 通过标准 | 不需要人工介入;同步后一致 |
5.10 测试用例:所有权管理 API(P0)
TC-25:所有权状态查询
| 项目 | 内容 |
|---|---|
| 目标 | 验证所有权状态 API 返回正确信息 |
| API 调用 | GET /api/ownership/status |
| 预期结果 | 返回所有设备的 owner、access_mode、lease 状态 |
| 通过标准 | exclusive 设备不可接管;shared/lease 设备状态正确 |
TC-26:所有权转移
| 项目 | 内容 |
|---|---|
| 目标 | 验证主动转移设备控制权 |
| 前置条件 | NODE-1 持有 shared 设备 |
| API 调用 | POST /api/ownership/transfer {"device_id":"OPCUA-01","target_node":"NODE-REMOTE"} |
| 预期结果 | OPCUA-01 的 owner 变为 NODE-REMOTE;OWNER_ANNOUNCE 广播 |
| 通过标准 | 两个节点 owner 信息一致 |
5.11 测试用例:UI 界面验证(P1)
TC-27:集群总览页面
| 项目 | 内容 |
|---|---|
| 目标 | 验证 UI 集群总览正确展示节点拓扑 |
| 步骤 | 1. 浏览器打开 http://localhost:80822. 导航到「集群总览」 |
| 预期结果 | 显示 NODE-1 和 NODE-REMOTE 两个节点,状态指示灯正确 |
| 通过标准 | 节点数量、在线状态、IP 地址、版本号与实际一致 |
TC-28:配置树导航
| 项目 | 内容 |
|---|---|
| 目标 | 验证 GATEWAY → Channel → Device → Point 四级树结构 |
| 步骤 | 1. 点击 NODE-REMOTE 节点 2. 逐级展开 channel → device → points |
| 预期结果 | 懒加载正常,点 channel 加载 device,点 device 加载 point |
| 通过标准 | 树结构与 conf/ 目录配置一致;展开/折叠/懒加载正常 |
TC-29:Diff UI 差异展示
| 项目 | 内容 |
|---|---|
| 目标 | 验证差异对比 UI 正确显示 |
| 前置条件 | 两节点存在已知差异 |
| 步骤 | 进入「配置差异」tab |
| 预期结果 | 显示本地 vs 远程对比,标注 ⚠️ 差异项,提供 [同步到本地] [推送远程] 按钮 |
| 通过标准 | diff 列表与 API /sync/node/{id}/diff 返回一致 |
TC-30:侧边栏版本信息展示
| 项目 | 内容 |
|---|---|
| 目标 | 验证侧边栏底部正确显示构建版本信息 |
| 步骤 | 1. 浏览器打开 UI 2. 查看左下角 sidebar-footer |
| 预期结果 | 显示 ● v0.0.4、Build 2026-06-05 14:23、Commit efa219a |
| 通过标准 | 版本信息与 GET /api/auth/system-info 返回一致 |
| goreleaser 构建后验证 | 版本号、构建时间、CommitID 均为真实注入值,而非 dev/unknown |
TC-31:接管 UI(AccessMode 分叉)
| 项目 | 内容 |
|---|---|
| 目标 | 验证接管按钮按 AccessMode 分叉显示 |
| 前置条件 | 系统中存在 exclusive / shared / lease 三种设备 |
| 步骤 | 1. 逐个查看设备详情 2. 检查接管按钮状态 |
| 预期结果 | - exclusive: 按钮禁用,显示 🔒 - shared: 按钮正常,显示 🌐,点击直接接管 - lease: 按钮显示 🔁,点击弹确认框”会踢掉对方” |
| 通过标准 | 三种模式按钮行为与 6.6 节 AccessMode 接管规则一致 |
5.12 测试用例:配置树结构与懒加载(P1)
TC-32:Channel 层级懒加载
| 项目 | 内容 |
|---|---|
| 目标 | 验证展开 Channel 节点时不立即加载所有设备 |
| API 调用 | GET /api/sync/node/NODE-1/channel/modbus-1/devices |
| 预期结果 | 仅返回 channel 下的设备列表,不含 point 详情 |
| 通过标准 | 响应体不含 point,deviceCount 与实际一致 |
TC-33:Device 层级懒加载
| 项目 | 内容 |
|---|---|
| 目标 | 验证展开 Device 节点时才加载该设备的 point 列表 |
| API 调用 | GET /api/sync/node/NODE-1/device/PLC-01/points |
| 预期结果 | 返回 PLC-01 的 point 列表 |
| 通过标准 | 仅返回 PLC-01 的 points;不影响其他 device |
5.13 测试用例:并发安全(P1)
TC-34:多节点同时修改不同配置
| 项目 | 内容 |
|---|---|
| 目标 | 验证非冲突的并发修改正确合并 |
| 前置条件 | 双节点在线,状态一致 |
| 测试步骤 | 1. NODE-1 修改 device A 的配置 2. 同时 NODE-REMOTE 修改 device B 的配置 3. 两节点互相广播 Announce |
| 预期结果 | 两项修改都被合并,无冲突 |
| 通过标准 | 最终两节点 device A 和 device B 的配置都正确更新;一致性检查通过 |
TC-35:同设备多节点并发接管
| 项目 | 内容 |
|---|---|
| 目标 | 验证多节点同时接管同一个 lease 设备时不会双主 |
| 前置条件 | lease 设备 PLC-01 的 lease 已过期 |
| 测试步骤 | 1. NODE-1 和 NODE-REMOTE 同时发起接管 2. 观察冲突解决机制 |
| 预期结果 | 只有一个节点获得 lease(通过 VectorClock 比较或先到先得) |
| 通过标准 | 同一时刻 PLC-01 只有一个 owner;另一个节点收到 REJECT |
6. 异常与边界测试
6.1 异常场景
| 编号 | 场景 | 操作 | 预期行为 |
|---|---|---|---|
| E-01 | 远程节点断电 | 直接断开 192.168.3.230 电源 | 本机检测到 offline,WAL 持久化未同步数据 |
| E-02 | 网络分区 | iptables 阻断两节点间通信 | 两节点各自正常运行;网络恢复后自动合并 |
| E-03 | 远程服务崩溃 | kill -9 edgex 进程 |
systemd 自动重启(Restart=always);恢复后增量同步 |
| E-04 | 磁盘满 | 远程节点磁盘写满 | 同步失败,返回错误信息;不损坏已有数据 |
| E-05 | 配置格式错误 | 手动写入非法 YAML | 解析失败,不传播到其他节点;记录错误日志 |
| E-06 | 超大配置文件 | 注入 200 设备 × 500 点的配置 | 分层 hash 增量同步;不触发全量传输 |
| E-07 | 版本号溢出 | 快速连续修改 > 2^32 次 | VectorClock 正确处理 uint64 溢出 |
| E-08 | 重复节点 ID | 两节点配置相同 node_id | 后启动节点检测冲突,自动重新生成 ID |
6.2 边界条件
| 编号 | 条件 | 操作 | 预期行为 |
|---|---|---|---|
| B-01 | 空配置节点加入 | NODE-REMOTE 首次部署,无任何配置 | 全量同步正常完成 |
| B-02 | 单节点运行 | 只有 NODE-1 在线,无其他 peers | 正常运行,无异常日志 |
| B-03 | 设备数为 0 | channel 下无 device | 树结构正常展示空状态 |
| B-04 | 点位数为 0 | device 下无 point | 树结构正常展示空状态 |
| B-05 | 极长设备名(256+字符) | 创建长名称设备 | 正常创建和同步(或正确拒绝) |
| B-06 | 特殊字符设备编码 | 编码含 / \ : 等 |
正确解析或合理拒绝 |
7. 性能测试
7.1 同步性能基准
| 指标 | 条件 | 预期值 | 测试方法 |
|---|---|---|---|
| 全量同步耗时 | 3 channel / 10 device / 200 point | < 5s | 计时 sync trigger 到 completed |
| 增量同步耗时 | 修改 1 个 point | < 500ms | 计时 point 修改到对端更新 |
| Announce 延迟 | 局域网内单次广播 | < 100ms | 测量 hash 变更到对端感知 |
| 节点发现时间 | 新节点加入到被发现 | < 10s (mDNS) | 计时节点启动到 peers 列表出现 |
| 一致性检查耗时 | 200 point 级检查 | < 3s | 计时 consistency API 响应 |
| 内存占用 | 空闲运行 | < 100MB | top / htop |
| CPU 占用 | 空闲运行 | < 5% | top / htop |
| 启动时间 | 冷启动到 API 可用 | < 3s | 计时启动命令到 API 响应 |
7.2 压力测试
| 指标 | 条件 | 验证目标 |
|---|---|---|
| 高频同步 | 每秒修改 10 个 point,持续 60s | 不丢数据、不重复、无崩溃 |
| 大配置包 | 1000 个 point 的 delta 包 | 正常传输(Snappy 压缩后 < 1MB) |
| 网络延迟 | 模拟 200ms 延迟 | 同步正常完成(超时配置需相应调整) |
8. 测试执行清单
8.1 执行顺序
Phase 1: 环境准备
└── [ ] 2.2 环境检查
└── [ ] 3.1 go run 本地启动
└── [ ] 3.2 goreleaser 构建
└── [ ] 4.1 远程部署
└── [ ] 4.3 部署后验证
Phase 2: 基础功能 (P0)
└── [ ] TC-01 mDNS 自动发现
└── [ ] TC-02 Bootstrap 静态发现
└── [ ] TC-03 新节点加入全量同步
└── [ ] TC-04 全量配置同步
└── [ ] TC-05 增量同步
└── [ ] TC-06 点位级同步
└── [ ] TC-17 一致性检查
Phase 3: 控制权管理 (P0)
└── [ ] TC-07 Exclusive 禁止接管
└── [ ] TC-08 Shared 直接接管
└── [ ] TC-09 Lease 租约抢占
└── [ ] TC-10 Lease 续约与过期
└── [ ] TC-25 所有权状态查询
└── [ ] TC-26 所有权转移
Phase 4: 设备迁移 (P0)
└── [ ] TC-11 设备编码验证
└── [ ] TC-12 设备配置迁移
└── [ ] TC-13 迁移回滚
Phase 5: 版本与Diff (P1)
└── [ ] TC-14 Vector Clock 版本比较
└── [ ] TC-15 Vector Clock 冲突检测
└── [ ] TC-16 配置版本回滚
└── [ ] TC-18 结构 Diff
└── [ ] TC-19 属性 Diff
└── [ ] TC-20 语义 Diff
Phase 6: 错误处理 (P1)
└── [ ] TC-21 节点离线检测
└── [ ] TC-22 节点恢复与断点续传
└── [ ] TC-23 网络超时重试
└── [ ] TC-24 数据冲突自动解决
Phase 7: UI验证 (P1)
└── [ ] TC-27 集群总览页面
└── [ ] TC-28 配置树导航
└── [ ] TC-29 Diff UI 差异展示
└── [ ] TC-30 侧边栏版本信息
└── [ ] TC-31 接管 UI 分叉
Phase 8: 懒加载 (P1)
└── [ ] TC-32 Channel 层级懒加载
└── [ ] TC-33 Device 层级懒加载
Phase 9: 并发安全 (P1)
└── [ ] TC-34 多节点同时修改不同配置
└── [ ] TC-35 同设备多节点并发接管
Phase 10: 异常测试
└── [ ] E-01 ~ E-08 异常场景
└── [ ] B-01 ~ B-06 边界条件
Phase 11: 性能测试
└── [ ] 7.1 性能基准
└── [ ] 7.2 压力测试
8.2 通过标准
| 级别 | 标准 |
|---|---|
| P0 全部通过 | 最小可发布标准 |
| P0 + P1 全部通过 | 推荐发布标准 |
| 异常测试 80% 通过 | 生产就绪 |
| 性能达标 | 所有指标在预期范围内 |
9. 快速测试命令集
9.1 本机开发(最常用)
# 构建前端 + 编译运行
cd ui && npm run build && cd ..
go run cmd/main.go
# 验证
curl -s http://localhost:8082/api/auth/system-info | python3 -m json.tool
9.2 远程部署(最常用)
# 一键构建 + 部署
goreleaser release --snapshot --clean --config .goreleaser.yml && \
bash scripts/deploy-remote.sh root@192.168.3.230 NODE-REMOTE
# 部署后验证
ssh root@192.168.3.230 "systemctl status edgex --no-pager"
ssh root@192.168.3.230 "curl -s http://localhost:8082/api/auth/system-info"
9.3 同步测试
# 获取节点列表
curl -s http://localhost:8082/api/sync/peers | python3 -m json.tool
# 获取远程节点配置树
curl -s http://localhost:8082/api/sync/node/NODE-REMOTE/tree | python3 -m json.tool
# 触发全量同步
curl -s -X POST http://localhost:8082/api/sync/trigger \
-H 'Content-Type: application/json' \
-d '{"mode":"full"}' | python3 -m json.tool
# 一致性检查
curl -s http://localhost:8082/api/sync/consistency | python3 -m json.tool
# 所有权状态
curl -s http://localhost:8082/api/ownership/status | python3 -m json.tool
9.4 远程排错
# 查看最近日志
ssh root@192.168.3.230 "journalctl -u edgex -n 100 --no-pager"
# 实时跟踪
ssh root@192.168.3.230 "journalctl -u edgex -f"
# 重启服务
ssh root@192.168.3.230 "systemctl restart edgex"
10. 综合测试检查(10项必须严格验证)
在每次联调测试中,按以下顺序执行,全部通过方可认为系统就绪:
| # | 检查项 | 验证命令/方法 | 通过标准 |
|---|---|---|---|
| 1 | 本机启动 | go run cmd/main.go → curl sys-info |
HTTP 200,API 可达 |
| 2 | goreleaser 构建 | goreleaser release --snapshot --clean |
所有平台产物生成,无错误 |
| 3 | 远程部署 | bash scripts/deploy-remote.sh root@192.168.3.230 NODE-REMOTE |
systemctl active,API 可达 |
| 4 | 节点发现 | curl peers API | 双节点互相可见,状态 online |
| 5 | 全量同步 | 新节点加入触发或手动 trigger | 配置树完全一致 |
| 6 | 增量同步 | 修改 1 个配置项 → 观察对端更新 | 仅变更项传递 |
| 7 | 控制权接管 | exclusive→拒绝 / shared→直接接管 / lease→抢占 | 三种模式行为正确 |
| 8 | 一致性检查 | curl consistency API | overall_status = consistent |
| 9 | UI 展示 | 浏览器确认集群/配置树/Diff/版本信息 | 数据与 API 一致 |
| 10 | 故障恢复 | 停止远程节点 → 恢复 | offline 检测 + 断点续传正常 |
文档版本: v3.0(联机测试专用版 + 编译部署 + 35个测试用例)
创建日期: 2026-06-05
适用项目: Industrial Edge Gateway
技术栈: Go 1.25+, go-libp2p, goreleaser
测试节点: 本机 (go run) + 远程 root@192.168.3.230 (deb)
测试覆盖: 节点发现 / 配置同步 / 控制权 / 设备迁移 / 版本管理 / Diff / 错误处理 / UI / 性能