测试与变更验证
Sub2API 的风险集中在协议兼容、跨实例状态、计费幂等和长连接生命周期。只跑受影响文件的单元测试不足以证明一次改动可以上线。
仓库现有测试层次
| 层次 | 工具/入口 | 主要验证 |
|---|---|---|
| Go 单元测试 | make -C backend test-unit | Service、Handler、middleware、协议和错误策略 |
| Go 集成测试 | make -C backend test-integration | PostgreSQL、Redis、migration、Repository、缓存一致性 |
| 前端静态检查 | make test-frontend | ESLint、vue-tsc、关键 Vitest |
| 前端完整测试 | pnpm --dir frontend test:run | 所有 Vitest |
| 构建 | make build | Go 二进制与 Vue 生产产物 |
| 真实环境 E2E | go test -tags=e2e ... | 已运行实例与真实/测试上游协议 |
根目录 make test 会运行后端和前端检查。后端 make test 还执行 golangci-lint。
单元与集成标签
后端 Makefile 使用 build tags:
make -C backend test-unit
make -C backend test-integration集成测试的 Repository harness 使用 Testcontainers 启动 PostgreSQL 18 和 Redis 8,自动执行 migration。开发机没有 Docker 时会跳过,CI 中 Docker 不可用则直接失败。
这类测试适合发现:
- PostgreSQL 三值逻辑、约束和事务问题。
- Redis Lua、TTL、并发和缓存失效问题。
- Ent schema 与手写 SQL 不一致。
- migration 在空库上的最终结构错误。
- scheduler outbox 与快照不同步。
如果改动包含 SQL、schema、缓存或事务,不能只跑 unit tag。
CI 实际门槛
.github/workflows/backend-ci.yml 当前执行:
- 固定 Go 版本校验。
- 后端 unit tests。
- 后端 integration tests。
- 前端 lint、typecheck 和关键 Vitest。
- golangci-lint。
发布前还应本地或独立流水线补生产 build、镜像启动和 smoke test。CI 通过表示代码级门槛通过,不表示真实上游和部署配置已经验证。
按改动选择验证范围
| 改动 | 最低测试 | 额外验证 |
|---|---|---|
| Handler/DTO | 对应 unit + API contract | 非法输入、权限、错误 envelope |
| PostgreSQL schema/SQL | unit + integration + migration | 旧数据升级、锁表、回滚兼容 |
| Redis/cache | unit + integration | 多实例失效、TTL、Redis 故障 |
| 调度/账号状态 | service + repository integration | outbox、粘性、全部账号不可用 |
| 协议转换 | transform unit | 非流式、SSE、工具、thinking、图片 |
| WebSocket | WS unit/integration | 重连、帧边界、取消、连接池回收 |
| 计费 | billing unit + DB integration | dedup、余额、订阅、Key quota、并发 |
| 支付 | lifecycle/fulfillment/refund tests | 重复 Webhook、主动查询、Provider 历史配置 |
| 前端页面 | lint/typecheck/Vitest | 三种身份、feature flag、浏览器刷新 |
| 启动配置 | config/setup tests + build | 空库、旧库、丢 data volume、Secret 固定 |
协议测试矩阵
一个新模型或上游不能只测 200 OK。至少覆盖:
/v1/messages、/v1/responses、/v1/chat/completions或目标原生入口。- 普通 JSON 与流式 SSE。
- tool use、thinking/reasoning、system prompt 和多轮上下文。
- 客户端取消、上游超时和流中错误事件。
- requested model、upstream model、billing model 的记录。
- 400、401、403、404、429、5xx 对 Account 状态和 failover 的影响。
- 最大 body、最大 SSE line 和大响应读取限制。
协议测试最好使用录制/构造的确定性上游响应;真实上游 E2E 用于发现供应商行为变化,不能替代可重复单元测试。
计费不变量
每个计费变更都要证明:
- 同一个 billing dedup key 只扣一次。
- 用户费用和 Account 成本分别正确。
- 余额、订阅、API Key quota 和平台 quota 更新一致。
- 流式完成、客户端中断和上游错误有明确结算规则。
- usage worker 队列满时的 sync/sample/drop 策略符合配置。
- 价格缺失不会静默记 0。
- 缓存失败不会造成无限放行或重复扣费。
推荐用已知 Token 和固定价格 fixture 断言金额,不用真实模型输出做精确金额测试。
多实例不变量
单进程测试通过后,涉及共享状态的改动还要验证:
- 两个副本同时获取用户/Account 并发槽位。
- 一个副本修改 Account/Group,另一个副本何时看到。
- OAuth refresh lock 只有一个 owner。
- leader task 不重复执行,锁丢失后可接管。
- scheduler outbox 消费延迟与全量重建恢复。
- Settings/Provider Registry 在各副本的生效方式。
- 滚动升级期间新旧版本能够共享 DB/Redis 数据。
如果测试环境只能运行单副本,必须把多实例风险写入发布检查并安排灰度观察。
Migration 验证
Migration 是向前执行的 SQL 文件,不应只在空数据库上测试:
- 从生产前一版本的脱敏 schema/数据快照启动。
- 执行 migration 并检查 checksum 记录。
- 验证大表 DDL 锁时间和回填速度。
- 新旧应用短时并存时,旧版本是否能读新 schema。
- migration 失败重试是否安全。
- 备份恢复后能否重新启动应用。
涉及列重命名/删除时优先使用 expand → migrate/backfill → contract,多版本分步发布。
前端验证策略
关键 Vitest 当前覆盖 OAuth callback、支付视图、Profile 和 Settings 等高风险页面,但修改其他核心页面时应补进完整 test suite,而不是只依赖现有 critical list。
浏览器验收至少包含:
- 首次直接打开目标 URL。
- 登录过期和多请求同时 401。
- 普通用户尝试访问管理员页面。
- backend/simple/payment/risk-control 各开关组合。
- 表单重复提交、网络超时和部分保存失败。
- 生产 build 后 lazy chunk 加载。
当前 E2E 入口注意事项
后端 Makefile 的 test-e2e 目标引用 ./scripts/e2e-test.sh,但当前仓库没有这个脚本。不要把该目标显示成功当作 E2E 已覆盖;应先补齐脚本或直接运行:
cd backend
BASE_URL=http://localhost:8080 \
CLAUDE_API_KEY='<sub2api-test-key>' \
GEMINI_API_KEY='<sub2api-test-key>' \
go test -tags=e2e -v -timeout=300s ./internal/integration/...这里的变量是 Sub2API 测试 Key,不应把上游凭证提交到仓库。未设置对应 Key 的用例会跳过,需检查测试输出中的 skip,而不是只看退出码。
发布前最小闭环
受影响 unit
→ integration(涉及 DB/Redis 时)
→ 前端 lint/typecheck/test
→ 生产 build
→ migration 演练
→ 单副本 smoke
→ 多实例/真实上游关键链路
→ 灰度指标与回滚验证最终至少记录:构建版本、配置差异、migration、已运行测试、被跳过测试、smoke Request ID、已知风险和回滚触发条件。
开发流程见开发与定制入口,生产变更流程见变更、升级、备份与恢复。