23 KiB
测试与发布门禁
1. 目标
本测试计划证明 cpa-ext 不仅“能加载”,还满足三项核心结果:
- 用户 Key 的认证、金额额度、上游绑定和实际 Usage 结算形成完整闭环;
- 取消、重试、乱序、崩溃和宿主 fail-open 限制不会造成重复扣费或免费旁路;
- 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;
- topology(callback/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
测试分三层:
- deterministic fake provider:覆盖错误、stream、Usage、迟到和断连;
- CPA 内部兼容 provider/test server:验证完整翻译/重试;
- 受控真实 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.Exit、log.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 cases;API 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. G2:SQLite、并发与恢复
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. G3:C 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 启动验证
- 目标 CPA 启动;
- plugin global/instance enabled;
- 日志显示正确 ID/version/path;
- negotiated schema=3;
- capability shape 与阶段一致;
- Management exact routes 注册且无冲突;
- frontend exclusive active;
- Home 关闭;
- 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 SSE;Codex 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 中途断开永远无 Usage:unmeasured,不猜费;
- 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;
- SSRF:localhost、私网、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、请求体和网络路径,只改变插件阶段:
- 无 cpa-ext;
- schema 3 + 空 capability/hook;
- collection-only;
- 完整 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. G7:24h 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.go:dispatcher、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.go:SQLite、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 真机加载;
- 发布报告完整记录未执行项,不以口头确认替代证据。