# 测试与发布门禁 ## 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; - 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 测试分三层: 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:静态与契约检查 最低命令: ```bash 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 ```bash 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 示例: ```bash 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 价值闭环 ```text 管理员发布价格 → 同步 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、请求体和网络路径,只改变插件阶段: 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. 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](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 ` 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 基础: ```bash ./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. 发布报告模板 ```text 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 真机加载; - 发布报告完整记录未执行项,不以口头确认替代证据。