Files
cpa-plugin/docs/modules/test-plan.md
T

23 KiB
Raw Blame History

测试与发布门禁

1. 目标

本测试计划证明 cpa-ext 不仅“能加载”,还满足三项核心结果:

  1. 用户 Key 的认证、金额额度、上游绑定和实际 Usage 结算形成完整闭环;
  2. 取消、重试、乱序、崩溃和宿主 fail-open 限制不会造成重复扣费或免费旁路;
  3. native 插件不会把 CPA 的稳定性和流式性能降到不可接受水平。

测试以可观察结果为准,不以代码覆盖率或“接口返回 200”替代业务证明。

2. 固定基线

当前设计基线:

项目 版本
CLIProxyAPI v7.2.130 / f43aad7637ad813745bf7d341acb5663617570c5
ABI 1
RPC schema 3
key-billing 参考 v0.3.1 / 25b534ae386f830f537cca9215cff5586e630b3a
usage-keeper 参考 v1.14.4 / d62cad3f345ae574089a14a4ac75cca023c7ead6

测试报告必须记录:

  • cpa-ext commit/version
  • CPA commit/tag、artifact checksum
  • OS/arch、Go/C compiler、SQLite driver
  • artifact checksum/export
  • topologycallback/sidecar/worker);
  • plugin negotiated schema/capabilities
  • 测试配置 hash
  • 未执行项及原因。

“使用最新 CPA”不是可复现的测试标识。

3. 门禁分层

Gate 内容 PR Release
G0 格式、静态检查、依赖/文档一致性 必须 必须
G1 纯领域/dispatcher 单元测试 必须 必须
G2 SQLite、并发、恢复和 race 集成测试 必须 必须
G3 真正 c-shared ABI/export/load 测试 必须 必须
G4 目标 CPA 端到端业务闭环 关键变更 必须
G5 安全与故障注入 关键变更 必须
G6 流式性能、容量和查询基准 性能相关 必须
G7 24h soak、备份恢复、升级回滚和平台矩阵 可选 nightly 必须

任一 Required gate 失败都不能发布。不得以“已知问题”豁免认证旁路、账本不一致、秘密泄露、数据库损坏或结构性流式放大。

4. 测试环境

4.1 纯 Go

  • cmd/cpa-ext/main_stub.go 让普通 go test ./... 不依赖 C ABI
  • 每个测试使用独立 temp SQLite
  • 注入 fixed clock、ID generator、CSPRNG facade、price snapshot 和 fake host adapter
  • 禁止依赖测试执行顺序、真实当前时间或共享全局 DB;
  • fixture 中金额使用 micros、倍率使用 Ratio
  • 所有 callback 默认视为并发、乱序、重复和可能缺失。

4.2 ABI 宿主

使用真实构建产物,不用纯 Go dispatcher 代替:

  • WSL/Linux .so
  • Windows .dll
  • 对应平台 CPA plugin-capable binary
  • 独立 config/auth/data/plugins 目录;
  • 随机端口和临时 sentinel/Management/downstream Key
  • 测试完成明确停止进程并保存日志/结果。

4.3 Provider

测试分三层:

  1. deterministic fake provider:覆盖错误、stream、Usage、迟到和断连;
  2. CPA 内部兼容 provider/test server:验证完整翻译/重试;
  3. 受控真实 Codex OAuth 测试账户:只在 release/nightly 验证真实协议和费用,使用最小请求预算。

真实 secret 只由 CI secret store 注入,不出现在命令行回显、fixture、artifact 或日志。普通 PR 不依赖真实 OAuth。

5. G0:静态与契约检查

最低命令:

test -z "$(gofmt -l cmd internal)"
go vet ./...
go test ./...
git diff --check

检查:

  • ABI version 和 RPC schema 未混用;
  • register/reconfigure capability shape 相同;
  • 只声明已实现 method
  • wire JSON tag/casing 对照目标 CPA
  • API exact route 无 :, *, .. 和宿主保留冲突;
  • config/API/error enums 有唯一实现来源;
  • migration 连续且不可重复编号;
  • SQL 查询使用固定列/allowlist
  • 日志调用不接收 raw request/config/auth DTO
  • os.Exitlog.Fatal、未保护 panic 不存在;
  • 文档中的 CPA revision、ABI/schema、capability 与代码一致;
  • NOTICE/THIRD_PARTY 记录移植代码来源和许可证。

建议增加 secret scanner、dependency vulnerability scan、SBOM 和 artifact provenance。

6. G1:领域与 dispatcher 单元测试

6.1 数据和 Token 归一化

  • OpenAI/Codex 普通输入、cache read、cache write、output 四段互斥;
  • Reasoning 已包含 output 时不重复;
  • total 与分项一致/不一致;
  • missing、partial、unclassified、inconsistent
  • 相同 Usage 多来源择优,不相加;
  • response ID / RequestID / ExecutionID 关联;
  • nested host model callback 不重复采集;
  • raw provider 值脱敏和质量字段保留。

6.2 Pricing Golden Tests

每个 PriceVersion fixture 至少覆盖:

场景 期望
四段各 1M Token 每段精确使用自己的 rate
cache 已包含在 input 普通输入扣除 cache,不重复
request priority 总基础金额 × 5/2 一次
response priority 总基础金额 × 5/2 一次
request + response 都 priority 仍然 × 5/2,不是 × 25/4
response 明确 standard 按有效 tier 政策处理并记录来源
长上下文 threshold-1/equal/+1 符合显式 gt/gte
长上下文 + priority 先阶梯价,再 × 5/2
GPT-5.6 Sol/Terra/Luna 分别 exact match,不互相继承
alias 只按显式 alias,冲突失败
unknown model unavailable,不是 0
explicit free available + 0,带确认标记
最大 Token/rate/multiplier 不溢出、不出现 NaN/Inf
Usage revision 新总额减已入账总额,舍入确定

发布价格版本时在事务提交前运行同一组 compiled golden casesAPI preview 和 runtime resolver 必须共享算法。

6.3 Billing

  • plan 周期首次准入激活、到期、never/custom
  • BillingAccount 多 Credential 共享金额;
  • secret rotate 不新建余额/周期;
  • 本地拒绝且无 Execution 金额 0
  • 成功/失败/取消有可靠 Usage 时按事实收费;
  • 取消无 Usage 为 unmeasured,不猜费;
  • 迟到 Usage 创建差额账本;
  • 重复 callback/EventID/response ID 幂等;
  • 更小/矛盾 cumulative vector 不自动退款;
  • last request 可形成有限负余额,之后拒绝;
  • 默认 account concurrency=1
  • adjustment/refund 使用新账本项;
  • 余额从 ledger 重建与投影一致。

6.4 Access/Route

  • unknown/disabled/expired/revoked Key
  • unbound account/plan/price 默认拒绝;
  • endpoint/model/tier allowlist
  • strict target 在 candidates 中/不在 candidates
  • preferred fallback 与原因;
  • pool round-robin/fill-first
  • CPA delegate response
  • retry 排除已 tried Auth
  • lower priority account priority_shadowed
  • scheduler error/panic/invalid/unhandled
  • after-auth actual Auth mismatch 必须 Terminate
  • config snapshot 原子替换。

6.5 Upstream accounts

  • AuthID/AuthIndex/internal ID 映射;
  • 新增、rename、missing、return、replacement
  • 同 email/label 不自动 merge
  • host status/disabled/unavailable/next retry
  • candidate visibility 按 provider/model/priority
  • quota fresh/stale/partial/error/unknown
  • Codex 主/次窗口、additional limits、subscription/reset credits
  • refresh 去重、批量、冷却和超时;
  • raw Auth JSON 不越过 adapter。

6.6 RPC dispatcher

  • malformed JSON、nil/empty bytes、unknown method
  • register/reconfigure schema negotiation
  • future host/低 schema
  • invalid initial config 返回错误;
  • invalid reconfigure 返回 LKG success registration
  • 每个 declared method 可分派;
  • method boundary panic recover
  • error envelope 脱敏;
  • concurrent register/reconfigure/call/shutdown
  • shutdown 多次调用、调用后请求;
  • response buffer ownership 的普通 helper 测试。

7. G2SQLite、并发与恢复

7.1 Migration

  • 空库创建当前 schema
  • 每个历史 schema 逐级迁移;
  • 重复启动不重复 migration
  • migration 中途失败不开放 readiness
  • 破坏性 migration 前 backup gate
  • 新版本 DB 回滚兼容声明有自动测试;
  • schema/data contract version 分离。

7.2 事务和幂等

  • provision 原子写账户、Credential、plan/route、audit
  • Request→Execution→Usage→Billing→Ledger→ProjectionEvent 原子边界;
  • DB error 在每个 statement/commit 点注入;
  • 同一 EventID 并发 2/10/100 次只结算一次;
  • revision CAS 冲突重试/返回冲突;
  • outbox/checkpoint 与事实一致;
  • 账本总和、余额、统计 spend reconciliation。

7.3 Crash recovery

在以下时点强制终止进程并重启:

  • Request pending 已写、尚未选 Auth
  • Execution 已写、Usage 未到;
  • Usage 已 durable、账本未写;
  • 账本已写、projection/outbox 未发布;
  • completion 丢失;
  • backup/checkpoint/rebuild 中间;
  • WAL 有未 checkpoint 内容。

恢复后不能永久占用并发、重复扣费或把可靠 Usage 丢成 0。

7.4 并发/race

go test -race ./...

覆盖:

  • 同 BillingAccount 并发准入/结算;
  • Key rotate/revoke 与在途请求;
  • price publish 与在途 Usage
  • route/account snapshot replace 与 scheduler pick
  • quota refresh dedupe
  • shutdown/reconfigure 与 callback
  • SQLite busy/locked、reader/writer pool
  • callback-driven maintenance budget。

8. G3C ABI 与动态库

WSL/Linux 示例:

CGO_ENABLED=1 go build -tags cshared -buildmode=c-shared -o bin/cpa-ext.so ./cmd/cpa-ext
file bin/cpa-ext.so
nm -D bin/cpa-ext.so | grep cliproxy_plugin_init

验证:

  • 目标架构/动态库格式;
  • export cliproxy_plugin_init
  • ABI version=1
  • call/free_buffer/shutdown 均非空且签名正确;
  • request bytes 在保留前复制;
  • empty/error response ptr/len 初始化;
  • plugin 分配的内存只由 plugin free
  • host callback buffer 只由 host free
  • 大小 0、畸形长度、并发调用;
  • reentrant host callback
  • shutdown 等待/超时和 active call
  • Windows shadow copy/changed content path
  • Linux/macOS loader 错误和缺失 symbol。

普通 Go 测试通过不能替代此 Gate。

9. G4:真实 CPA 端到端

9.1 启动验证

  1. 目标 CPA 启动;
  2. plugin global/instance enabled
  3. 日志显示正确 ID/version/path
  4. negotiated schema=3
  5. capability shape 与阶段一致;
  6. Management exact routes 注册且无冲突;
  7. frontend exclusive active
  8. Home 关闭;
  9. self readiness + gateway gate 通过。

9.2 MVP 价值闭环

管理员发布价格
→ 同步 Codex Auth / 确认 bindable
→ provision BillingAccount + Key + strict route
→ 用户调用 /v1/responses
→ CPA 选择指定 OAuth Auth
→ provider 返回 Usage
→ cpa-ext durable Usage + 金额账本
→ 用户 API 显示金额余额/请求
→ 额度耗尽后新请求 429

验证数据库事实、管理 API、用户 API、CPA log 和 provider fake recorder 五方一致。

9.3 请求矩阵

维度
transport HTTP SSECodex WebSocket 若发布支持
response streaming / non-streaming
outcome success / upstream 4xx / upstream 5xx / timeout / canceled / local reject
Usage complete / partial / missing / duplicate / late / inconsistent
retry 0 / 1 / 多 Auth attempts
tier standard / priority
context normal / long threshold 边界
route strict / preferred / pool / delegate
state active / quota exhausted / key revoked / price unavailable

当前 MVP allowlist 外的 WebSocket Alpha Search、Live、Realtime/client secret、chat/messages/image/video 等路径必须证明用户 Key 请求失败,不能只测试支持路径。

9.4 取消测试

  • client 在连接上游前取消:不收费;
  • first byte 前取消但 provider 报 Usage:收费;
  • stream 中途断开,provider 有最终/迟到 Usage:按实际差额收费;
  • stream 中途断开永远无 Usageunmeasured,不猜费;
  • completion 在 Usage 前/后/重复;
  • 取消后并发槽及时释放;
  • 高频 canceled+unmeasured 触发风险计数,不直接伪造金额。

可参考 key-billing scripts/e2e_cpa_billing.sh 的多协议、stream/non-stream、账单比对和客户端断开测试;需要扩展金额定点、Fast、strict route、数据库恢复和旁路门禁。

10. G5:安全与故障注入

10.1 认证旁路矩阵

逐项验证普通用户 Key 无法访问上游:

  • plugin binary 缺失;
  • global plugins disabled
  • cpa-ext instance disabled
  • register invalid
  • reconfigure invalid
  • frontend auth panic/error
  • request interceptor panic/error
  • scheduler panic/error/invalid response
  • plugin fused
  • Home enabled
  • database/keyring/price unavailable
  • native CPA 只剩 sentinel
  • gateway readiness 失联。

同时验证 sentinel 未出现在 UI/config response/log/test report,且不被日常应用持有。

10.2 依赖故障期望

故障 当前请求 后续请求 内部状态
credential DB lookup 失败 401 no_credentials(宿主限制) fail closed high severity auth dependency
admission DB/ledger/price 失败 success RPC + Terminate 503 fail closed readiness unhealthy
scheduler error/panic 宿主可能 fallback after-auth mismatch Terminate fuse/diagnostic
Usage durable write 失败 无法撤销已产生上游成本 后续 fail closed persistence_gap + reconciliation
completion 丢失 响应可能已完成 recovery 扫描 abandoned/settle pending
price candidate refresh 失败 不影响 active price 继续 LKG candidate stale
statistics projection 失败 计费继续 UI 显示 lag checkpoint error
quota refresh 失败 基础路由按 host candidate snapshot stale quota error

10.3 Web/API 安全

  • Management route 无 key/错误 key/remote deny
  • resource 枚举、非 GET、敏感 route 404
  • 用户 cross-account ID/cursor/cache
  • brute force/rate limit
  • SQL/filter/order injection
  • JSON duplicate/unknown/oversize/trailing document
  • XSS labels、Management entity encoding、CSP/nosniff
  • CSV formula injection
  • CORS/Origin/iframe/message
  • SSRFlocalhost、私网、redirect、DNS rebinding 模拟、非 HTTPS、超大 body
  • request log/download token 过期和审计;
  • backup/path traversal/symlink
  • error/log secret redaction。

10.4 Secret canary

为 downstream Key、Management Key、HMAC key、OAuth token、cookie、Prompt 各生成唯一 canary,测试结束扫描:

  • SQLite/WAL/backup(按预期字段例外审查);
  • stdout/file logs
  • API/HTML/CSV
  • panic/error strings
  • test artifacts
  • release archives。

任何未批准位置命中即失败。

11. G6:性能与容量

11.1 流式 A/B

保持完全相同的 CPA binary、OAuth/Auth、provider、model、service tier、transport、请求体和网络路径,只改变插件阶段:

  1. 无 cpa-ext
  2. schema 3 + 空 capability/hook
  3. collection-only
  4. 完整 cpa-ext。

矩阵:

  • SSE / WebSocket
  • prompt 1 KiB / 128 KiB / 1 MiB
  • 32 / 256 / 1024 chunks
  • concurrency 1 / 8 / 32
  • standard / Fast
  • success / failure / cancel。

采集:

  • TTFT P50/P95/P99
  • 总时长、tokens/s、chunks/s、chunk gap
  • CPU、RSS、alloc、GC
  • 每 method RPC count/bytes/max payload
  • SQLite/lock latency
  • canonical Usage/amount 一致性。

门槛:

  • 常见场景完整插件相对无插件中位退化不超过 5%;
  • 压力场景不超过 10%
  • P95 TTFT 增量不超过 max(20ms, 5%)
  • 超门槛需要明确审批和产品理由,不能隐藏;
  • 结构性断言失败无条件失败。

结构性断言:

  • schema 3 payload chunk 的 OriginalRequest/RequestBody 为空,header-init 除外;
  • 单独统计 response-before 仍重复携带的 request bodies
  • 未使用 HistoryChunks 时不能默认传 64 chunks/1 MiB;若宿主暂不能关闭,必须量化并推动契约修改;
  • 正常 chunk 的插件成本近似 O(chunk),不能是 O(prompt + history)
  • 计费正确性与无插件 provider Usage 一致。

11.2 SQLite/查询容量

参考 usage-keeper capacity-v1,构造 canonical 数据集:

  • 3M+ events
  • 50 Key、500 upstream identities、50 models 的基础档,并增加更大档;
  • 90 天 hot data
  • 1% failure、取消、late usage、retries
  • 完整 ledger/projection/checkpoint。

验证 ingestion 最大稳定点、Dashboard 核心 API p95/p99、RSS/cgroup peak、DB/WAL size、backup/rebuild 时间和查询计划。每个 probe 使用独立 clone,避免缓存/WAL 污染。

12. G724h Soak 与生命周期

P0 runtime spike 至少比较:

  • 零后台 goroutine + 内存状态;
  • callback-driven + 目标 SQLite driver
  • sidecar
  • 仅在候选时验证的 bounded worker。

24h workload

  • 持续 stream/non-stream 请求;
  • concurrency 波动;
  • 周期性管理查询/price/account snapshot
  • callback-driven maintenance
  • DB WAL/checkpoint/backup
  • config valid/invalid reconfigure
  • cancel/retry/provider failures
  • quota refresh(适用拓扑)。

每轮生命周期注入:

  • plugin config disable/enable
  • binary version change
  • CPA graceful shutdown/restart
  • blocked plugin call 时 shutdown timeout
  • Windows shadow copy/retired library
  • Linux loader/unload 行为。

通过条件:

  • bad flushGen、runtime fatal、panic、deadlock、use-after-free
  • RSS/handle/goroutine/DB connection 无持续无界增长;
  • retired runtime 不继续写同一 DB
  • shutdown 有界且事实/账本一致;
  • 无重复 settlement/checkpoint
  • TTFT/吞吐无随时间恶化;
  • backup/restore 和 integrity 通过。

若 DLL worker 方案失败,结论不是“再调参数”,而是把长期任务永久移到 sidecar/callback 拓扑并重跑门禁。

13. API 与 UI 测试

13.1 API contract

  • 每个 api.md route 的 method/path/auth/status/envelope
  • Money JSON、UTC time、empty collection、opaque ID
  • revision/If stale、Idempotency-Key
  • Credential secret first response/replay
  • cursor 并发插入、scope binding、过期/篡改;
  • pagination/time/filter max
  • stable error codes 与脱敏 500
  • resource JSON plain string 与 Management entity-v1 差异。

13.2 UI

  • desktop 与窄屏;
  • admin/user view 数据隔离;
  • Key/Management secret 仅内存;
  • negative/large/zero amount formatting
  • quota unknown/stale/partial
  • unpriced/unmeasured/persistence gap
  • loading/error/empty states
  • A & B <x> entity decode 后仍以 text node 渲染;
  • CSP 下无 inline/eval 违规;
  • no service worker/cache sensitive data
  • Home/ready failure 显示阻断,不伪装空数据;
  • destructive actions 有确认、reason、revision conflict 和恢复提示。

若修改 UI,按 key-billing AGENTS 的实践使用真实浏览器检查桌面/窄屏;静态快照不能替代交互验证。

14. 备份、升级和灾难恢复测试

  • 在线 backup 与并发写;
  • 打开 backup、integrity、schema/ledger check
  • SQLite + matching keyring 成对恢复;
  • 缺失/错误 keyring fail closed
  • 从事实重建全部 projections
  • 升级前 backup → migration → 新版流量;
  • backward-compatible binary rollback
  • incompatible migration 使用 backup rollback
  • 升级窗口 Usage reconciliation
  • price rollback 创建新版本;
  • 操作均有审计和 runbook 时间记录。

RPO/RTO 必须用演练结果制定,不能只写目标数字。

15. 平台与兼容矩阵

每个 release

CPA Linux amd64 Linux arm64 Windows amd64 macOS(若发布)
exact minimum/tested baseline 全 Gate G0-G5 + smoke/soak G0-G5 + Windows lifecycle 对应 artifact gates
拟升级 CPA 全 Gate smoke + ABI smoke + ABI smoke + ABI

当前正式 baseline 固定 v7.2.130。当支持范围扩展时,最低版本和最高验证版本都必须有 E2E;中间版本不能仅凭 semver 假设兼容。no-plugin artifact 必须启动拒绝/部署前置失败,不能被误判为兼容。

16. CI/Release 命令清单

WSL/Linux 基础:

./scripts/check-env.sh
test -z "$(gofmt -l cmd internal)"
go vet ./...
go test ./...
go test -race ./...
CGO_ENABLED=1 go build -tags cshared -buildmode=c-shared -o bin/cpa-ext.so ./cmd/cpa-ext
file bin/cpa-ext.so
nm -D bin/cpa-ext.so | grep cliproxy_plugin_init

随后执行:

  • CPA test-host load/register/capability tests
  • deterministic provider E2E
  • security/fault suite
  • stream A/B
  • migration/backup/restore
  • release OS/arch builds + checksums
  • nightly/release soak。

脚本必须使用临时明确目录并在退出时停止子进程;不得递归删除未验证路径。测试日志先脱敏再上传。

17. 参考现有项目

17.1 CLIProxyAPI

重点复用 internal/pluginhost/*_test.go 对以下行为的断言:

  • schema negotiation、future schema
  • reconfigure、fuse、blocked load/shutdown
  • frontend exclusive priority
  • scheduler error/panic/invalid fallback
  • request termination 和 async completion
  • Management exact/resource route
  • host callback buffer/context
  • Windows shadow copy
  • stream bridge cancel/full buffer。

17.2 key-billing

  • scripts/e2e_cpa_billing.sh:真实 CPA release、协议/stream、账单和 cancel
  • internal/billing/*_test.go:Key、plan、四段价格、长上下文、store;
  • internal/plugin/*_test.godispatcher、usage correlation、Management UI。

不能继承其固定 schema 2 性能形状、JSON store、缺少 Fast 和只验证最终 Usage 的覆盖边界。

17.3 usage-keeper

  • internal/pricing/test/Snapshot/rule/resolver
  • internal/quota/test/Codex quota、refresh、subscription
  • internal/repository/*_test.goSQLite、migration、aggregation
  • internal/api/test/auth、scope、CSP、no-store、redaction
  • internal/benchmark/capacity-v1:真实容量方法。

Keeper 是 sidecar 服务,其 worker/session/Gin 路由测试不能直接证明 c-shared 安全,必须在真实 CPA ABI 中重测。

18. 发布报告模板

cpa-ext version/commit:
CPA version/commit/checksum:
ABI / negotiated schema:
Capabilities:
OS/arch/toolchains:
Runtime topology:
Artifact path/checksum/export:
G0:
G1:
G2:
G3:
G4:
G5:
G6:
G7:
Performance delta:
Soak duration/result:
Backup/restore result:
Known limitations:
Checks not run:
Approver/date:

19. 最终完成标准

  • 所有模块验收项映射到自动测试或明确人工证据;
  • MVP 纵向闭环、取消、失败、迟到 Usage 和重试全部通过;
  • Fast/priority 2.5× golden cases 无双乘;
  • plugin 缺失/fuse/reconfigure/Home 等旁路测试全部 fail closed
  • secret canary 扫描无非预期命中;
  • SQLite crash/recovery、backup/restore、ledger reconciliation 通过;
  • stream 结构断言和性能阈值通过;
  • 24h soak 无 runtime crash/leak/deadlock
  • 每个发布 artifact 在目标 CPA/OS/arch 真机加载;
  • 发布报告完整记录未执行项,不以口头确认替代证据。