Pangolin 功能 × 测试覆盖清单

逐功能盘点:现有能力 → 已覆盖测试(怎么覆盖)→ 欠缺 → 只能人工。作为「验证五个终端是否正确」的对照工作表。

怎么用:本清单分两部分——A 共享逻辑 + 后端(控制面 + 数据面),跑一次自动化套件即对全平台生效;B 平台隧道层,五端各自不同,须逐端按底部验收清单打勾。每行的「覆盖」标签直接告诉你这条是已自动化部分/契约级零覆盖 还是本质上只能人工
↔ 配套:统一测试框架·架构说明(分层方法论 + ⑩ 已知缺口) · 开发规范·可测试性五支柱 · VPN 测试计划(站点矩阵)
自动 CI 里有断言守门,回归即红 部分 只到契约/单元,未覆盖端到端或边界 当前无任何测试 人工 本质上只能人工/真设备验证

「覆盖」列给的是当前状态,不是目标;标 /部分 的「欠缺」列写清下一步补什么。标 人工 的不是缺陷,是真设备/真出网/真链路这类自动化跑不动、必须人测的部分——集中收在 §A.4 / §B 验收清单

A · 共享逻辑 + 后端(控制面 + 数据面)

这部分代码四端共用(Flutter 业务逻辑 / Go 控制面 / 数据面渲染),自动化跑一次即对五个终端同时生效。验证策略:能自动化的全压到 CI,逐端不重复测

A.1 控制面(Go 控制面 HTTP API + gRPC,server/internal/*

账号 / 鉴权

功能现有能力已覆盖 + 怎么覆盖欠缺 / 只能人工
注册 / 登录 / 刷新 自动 邮箱注册、密码登录、refresh token 轮换、登出;/v1/auth/{register,login,refresh,code} auth/service_test · handler_test · token_test · integration_test:真库(SQLite)跑完整 register→login→refresh 链;token 签发/校验/过期纯函数单测;password_test 哈希;helpers_test 真邮件投递(SMTP)只 mock;账号枚举/暴力破解的真实速率压测仅 ratelimit_test 逻辑级
邮箱验证码 自动 发码、校验、节流;emailcheck auth/emailcheck_test · ratelimit_test:码生成/校验/节流窗口逻辑断言 真 SMTP 通道、到达率、垃圾箱判定 → 人工
两步验证 TOTP 自动 TOTP 绑定/校验、备份码 totp/totp_test(算法)+ auth/totp_user_test(绑定/校验流程) 真 Authenticator app 互操作 → 人工(一次性)
管理后台 自动 admin 登录/会话、IP 白名单、配置、加密 admin/{auth,session,mw_ipallow,config,crypto,handlers,services_real}_test:会话签发、IP allowlist 中间件、handler 行为 后台前端页面交互 → 人工

业务 / 计费

功能现有能力已覆盖 + 怎么覆盖欠缺 / 只能人工
兑换码 / 套餐 自动 码生成、兑换、套餐授予;/v1/redeem · /v1/plans codes/{generator,service}_test:码生成唯一性、兑换幂等、过期/已用拒绝
支付 webhook 部分 第三方支付回调 → 充值/开通 codes/webhook_test:回调签名校验 + 入账逻辑(mock 上游) 真支付渠道端到端(下单→回调→开通)仅 mock;真渠道沙箱 → 人工
激励解锁配额 自动 看广告解锁额度;/v1/ads/unlock usage/ads_test:解锁额度计算/上限 真广告 SDK 回调 → 人工
设备管理 自动 设备注册/列举/解绑、上限;/v1/me/devices devices/service_test · devices_integration_test:真库注册→列举→解绑;注册回填 last_seen(修过的真 bug)

节点 / 调度 / 供给

功能现有能力已覆盖 + 怎么覆盖欠缺 / 只能人工
节点注册表 + 连接 自动 列节点、连接/断开、ping;/v1/nodes · /connect · /disconnect nodes/{grpc,hub,lifecycle}_test:gRPC hub 收发、节点生命周期状态机 真节点延迟/可用性探测准确度 → 见数据面 §A.2
Agent mTLS 接入 自动 CA 签发、CRL 吊销、节点身份、bootstrap token mtls/{ca,crl,identity,bootstrap}_test + nodectl/bootstrap_token_test:证书链签发/校验/吊销 真 mTLS 握手在 §A.2 e2e 覆盖一段
自动调度 / 故障替换 自动 探测引擎、熔断、容量、自动替换编排、探针接入 scheduler/detect/engine_test · orchestrate/{breaker,capacity,config,replacer}_test · probe/{ingest,prober_agent}_test · wiring_{lifecycle,provision}_test:健康判定→熔断→替换决策全链逻辑 真云厂商触发的端到端替换 → 人工(贵/慢)
云供给 provision 部分 cloud-init 渲染、provider 注册表、节点替换 provision/{cloudinit,replace,service}_test · providers/registry_test(fakes 注入) 真厂商 API 开机/销毁 → 人工(按需,烧钱)
告警 自动 Telegram 通知、runbook alert/{notifier,runbook}_test:触发条件 + 去抖逻辑 真 Telegram 投递 → 人工

持久层 / 可移植性

功能现有能力已覆盖 + 怎么覆盖欠缺 / 只能人工
双数据库 SQLite/MySQL 自动 裸 SQL + 方言层;迁移分两套;upsert/行锁中性记法 db/dialect_test · sqlite_smoke_teststore/{sqlite_stores,sqlite_migrate,sqlite_per_device,mysql,mysql_integration}_test:SQLite 实库 + MySQL testcontainers 真库跑同一套断言;ci/scan-portable-sql.sh 扫禁用 MySQL 专属构造 真 512MB VPS 上的并发/锁竞争压测 → 人工

A.2 数据面(sing-box 配置渲染 + per-user 记账,httpapi/clientconfig · agentd/*

「连上了能不能真出网、记账准不准」的源头逻辑。渲染/解析有测试,真链路靠 e2e 一段 + 人工兜底。

功能现有能力已覆盖 + 怎么覆盖欠缺 / 只能人工
客户端配置渲染 自动 BuildClientConfig 服务端渲染原样下发:REALITY 出站、TUN 入站(strict_route 杀开关)、DNS 劫持首条规则、国内分流 httpapi/clientconfig_test:断言 REALITY 公钥/short-id/端口、TUN auto_route/strict_route、hijack-dns 规则排在 LAN 前、split_cn 开关 渲染对 ≠ 内核吃得下:真 sing-box 加载该配置并连通 → §B 人工
节点配置渲染(agent 侧)自动 agent 渲染 sing-box 服务端配置 + 凭证增删/轮换/吊销 agentd/singbox_test:Upsert/Revoke/Rotate/ApplyConfig 状态机;derive_test 数据口凭证派生;hy2cert_test 证书;command_test systemctl restart sing-box 后端口真监听 → 人工
per-user 流量采集 部分 V2RayUsageSource:agent 读 sing-box v2ray_api StatsService per-user 计数器 → 聚合 → ReportUsage 新增 agentd/usage_v2ray_test:表驱动 parseUserStat + loopback 假 StatsService 跑真 Collect(),验聚合/方向不串/全0丢弃/reset=true 窗口语义 假 StatsService ≠ 真 sing-box:真出网流量经真 v2ray_api 的计数准确度 → 人工
记账全链 e2e 部分 enroll → ReportUsage → 统计入库 → API 读出 server/test/e2e/smoke_test:进程级 gRPC 全链路(agent enroll→上报→控制面统计真入库真读出),HTTP 段 + miniredis /v1/usage/devices 分设备断言(stats-overhaul 合并后补回);真节点真流量不在 e2e
REALITY 数据口连通 人工 客户端经 REALITY 连节点 443 真出网 —(握手是真协议真节点,自动化跑不动) 真握手成功 + 出网 → §B 每端验收清单第 1–2 步

A.3 客户端共享逻辑(Flutter client/lib/*,四端共用 Dart)

功能现有能力已覆盖 + 怎么覆盖欠缺 / 只能人工
API 客户端 + 契约 自动 ApiClient 取数、错误映射;/v1/me · usage · usage/devices unit/api_client_testcontract/{api_contract,stats_contract}_test 冻结 wire 字段形状;Go 侧 httpapi/contract_test · usage/contract_test · pb/agentv1/contract_test 两侧对齐
登录 / 续登 / 登出流程 自动 AuthNotifier 状态机 + token 存储 unit/flow_auth_test:真控制器 + MockClient 注入,跑登录→重启续登→登出 flutter_secure_storage keychain 行为 → 人工(一次性)
连接状态机 自动 connectionProvider connecting→connected→error;ping 探测 unit/{connection_controller,flow_connect}_test:真控制器 + 假桥,验状态流转;connect_passthrough_test 真隧道回调时序(连上才启动 app 等触发条件)→ §B
统计计算 + 上屏 自动 月 GB/时长聚合、周柱、分设备归因;stats_page unit/{device_usage,format}_test 解析/格式化;新增 widget/stats_page_test:真 wire 形态喂 StatsPage 断言指标卡/周柱/分设备数值真上屏 —(解析对 + 上屏对都已守)
配额 / 节点列表 自动 quotaProvider · nodesProvider unit/{quota_controller,nodes_provider}_test
UI 视觉一致性 自动 各页面/组件像素还原(亮/暗 × 中/英 × 手机/平板/桌面) golden/{components,auth_redesign,desktop_pages,tablet_pages}_test Linux 权威基线;widget/{cards,connect_button}_testresponsive/form_factor_testfonts_test 字体锁 真机不同 DPI/字号/深色模式实机观感 → 人工(抽查)
桥接口契约 部分 VpnBridge 抽象 + 桌面子进程实现 + mock bridge/{vpn_bridge_mock,kernel_process,desktop_vpn_bridge_m4m5}_test:内核进程查找/启停、mock 桥行为 原生 MethodChannel 两侧真实编解码 → §B(各端原生层)
交互组件(本轮)部分 切换节点确认框(连接态防误触,节点行 + 智能选择卡同款);节点页搜索框配色(PangolinFieldBox 修双色缝);showPangolinToast(主显示区内、自适应宽、上限 80%、超长多行居中) Windows 人工已验 2026-06-29:确认框两处弹出 + 防误触正常;搜索框配色一致无缝;toast 常规文案显示正常(位置/明暗/动画)。node_connect_confirm_test 守确认框逻辑;toast 走真相源规格 design/CONTRACT.md §2 toast 超长文本多行居中 + ≤80% 上限边界未验 待测;其余四端(mac/Android/iOS/iPad)同源未逐端抽查 → 人工

设计契约 · 五端 UI 同源

五端跑同一个 Flutter 工程client/lib 共享 Dart),UI 不按平台分叉——颜色/样式/组件不是「约定一致」,是物理上同一份代码。下表是「五端同源」各维度的单源 + 守门状态。两处关键盲区:① token 有漂移闸,logo/app-icon 资产没有;② 「生成物不漂移」有闸,但「screen 是否真用真相源」(依从性) 零闸、且所有闸无反向 case 自检

维度单源(真相源 → 生成/消费)守门机制欠缺 / 风险
颜色/间距/圆角/字体 自动 design/colors_and_type.css(clay/sand 色板)→ gen_flutter_tokens.mjspangolin_tokens.gen.dart(勿手改)→ pangolin_theme.dart 语义层;业务禁硬编码 hex CI codegen-driftci/check-codegen-drift.sh):重生成与提交版不一致即红,防「改 CSS 没重生成 / 手改生成物」 —(单源 + 自动闸,最规范的一块)
组件 自动 client/lib/widgets/ 唯一实现;design/ 禁放 Dart 副本(会漂移);规格在 design/CONTRACT.md + design/preview/ golden 测试:组件 + 各页面 × 明/暗 × 中/英 × 手机/平板/桌面,像素回归即红
图标库 自动 lucide_icons(细线条)全端同一套字体图标 随 golden 像素守
品牌 logo(应用内)部分 design/assets/*.svg(mark / wordmark / app-icon)→ 拷贝一份到 client/assets/flutter_svg 加载,四端同一份矢量 无自动闸——design↔client 双拷贝靠手动同步(当前 4 个 SVG 已 diff 确认一致) design/assets 那份忘同步 client/assets,CI 静默放过 → 待补 drift 闸
app 启动图标 部分 assets/app-icon.svgapp-icon-ios-1024.pngflutter_launcher_icons 一键铺五端全尺寸(iOS appiconset / Android mipmap / macOS / Windows .ico) 无 drift 闸——生成物(各端 PNG/ico,如 iOS 22 张)入库,靠手动 dart run flutter_launcher_icons app-icon.svg 忘重跑生成,五端图标与源图分叉,CI 静默放过 → 待补 drift 闸
设计契约文档 人工 design/CONTRACT.md(design-distill 蒸馏):token 映射 / 组件映射 / 逐屏像素规格 / 逐屏验收清单(明×暗×中×英) 人读契约;像素侧由 golden 兜底 「还原对不对」的逐屏验收是人工抽查(截图 diff 可半自动)
screen 依从性
(源自真相源)
要求 screen/widget 的颜色/字号/间距必须源自 PangolinColors/PangolinText/PangolinSpacing,不得硬编码 hex/数值或绕过 widgets/ 自拼组件 无任何检测codegen-drift 只守「生成物=CSS 源」不守消费端是否用它analysis_options.yaml 无禁硬编码规则;design/_adherence.oxlintrc.json 能抓 raw hex/px 但只对 JSX 原型、warn 级、未接 CI 实测 lib/screens 已有 62+ 处硬编码 fontSize:/EdgeInsets 数值无人拦(如 account_page.dart:187 fontSize: 16)→ 待补依从性扫描闸(第①档:正则扫 raw 字面量,仿 scan-portable-sql.sh
闸的反向 case 自检 每个闸应有「注入违规 → 断言闸变红」的负向自检,证明闸非永真(怎么改都绿 = 形同虚设) 无任何反向 case(全仓 grep 零结果)——含 codegen-drift 在内的所有闸都没验证过「真能拦住违规」 待补 fixture 自检(备一行违规样本,CI 跑「扫它必须非零退出」;新依从性闸与 codegen-drift 都该配)

关键区别:codegen-drift(生成物不漂移) 依从性(消费端真用真相源)。前者保证真相源本身没被改歪,后者保证 screen 真的去用了真相源——当前只有前者有闸,后者零守门、且所有闸都缺反向 case 自检。规划见 plan:UI 真相源依从性收口(本轮仅登记,补闸后续做)。

待补(性价比高):仿 ci/check-codegen-drift.shlogo SVG 双拷贝app-icon 生成物 各加一个 drift 闸——重新生成/比对 design↔client,不一致即红,把「图标 logo 五端同源」从靠纪律升成靠闸。这是 token 已有、资产尚缺的一行。

A.4 后端「只能人工」汇总

B · 平台隧道层(五端逐一)

真正逐端不同的只有原生隧道实现——这是测试架构 ⑩ 标注的零自动化盲区。三种策略:

各端隧道状态矩阵

终端隧道实现已覆盖欠缺 / 验证重点
Android PangolinVpnService.kt(VpnService) + libbox.aar;DefaultNetworkMonitor.kt 网络监听;VpnEventBus.kt 事件桥 原生层无自动化测试 VpnService 授权弹窗、TUN fd 建立、libbox 启动、断网重连、统计回传桥;真机连通 + 出网
iOS VpnManager.swift(NEPacketTunnelProvider) + 内嵌 libbox;StatsClient.swift 统计 原生层无自动化测试 NE 内存 ≤50MB 闸、NE profile 安装授权、libbox 后台队列启动、TestFlight 分发;真机连通 + 出网
iPad 同 iOS 二进制;额外横屏侧栏布局 部分 布局走 golden(tablet_pages),隧道同 iOS 同 iOS + 横屏/分屏多任务下隧道与 UI;真机连通 + 出网
macOS 默认子进程(DesktopVpnBridge+sing-box);可选 PacketTunnel 系统扩展(站外 Developer ID + 公证) 部分 子进程查找/启停有 kernel_process_test · desktop_vpn_bridge_m4m5_test;sysext realize sysext 能否被 sysextd realize(见踩坑复盘)、CFBundleVersion 递增、公证/staple、三方死锁规避;真机连通 + 出网
Windows sing-box.exe 子进程 + wintun.dll TUN;kernel_process.dart 管理 部分 进程管理逻辑同 kernel_process_test(跨平台共用);wintun/打包 wintun.dll 随 exe 落位、TUN 适配器创建、UAC 提权、安装包;真机连通 + 出网

每端真连通验收清单(同一张表,逐端打勾)

第 1–7 步对五端是同一份判据,差异只在第 8 步平台专项。用客观信号代替「看着像连上了」。当前节点:107.172.55.251(REALITY 443)。

半自动化建议:第 1–3 步(出口 IP / 站点可达 / 分流判定)可写成一个连通探针脚本自动跑,把客观判据固化下来,只留第 4–8 步手测——这是把 §B 盲区往自动化推进的最小一步。

下一步「功能更完善」可补的自动化(从盲区往里推)

维护:新功能上线时同步更新本表对应行的「覆盖」标签与「欠缺」列;与 test-architecture.html ⑩ 已知缺口 互为索引——本表是「逐功能」视角,那里是「分层方法论 + 盲区跟踪」视角。