# TDD 实施计划（MVP）

日期：2026-05-13

## 设计结论

首版采用：

- `packs/openai-cn-pack/`：真实可校验模型包
- `internal/pack`：pack 装载、checksum 校验、provider schema 校验
- `internal/provision`：导入编排服务
- `internal/host/sub2api`：宿主 admin/gateway 适配
- `cmd/cli import-provider`：一键导入入口

## TDD 顺序

1. 先写 `internal/pack/loader_test.go`
   - 成功装载 pack
   - checksum mismatch 失败
   - provider schema 非法失败
2. 再写 `internal/provision/import_service_test.go`
   - subscription 模式成功导入
   - strict 模式探测失败直接失败
   - 参数非法拒绝
3. 再补宿主适配器集成测试
   - `CheckGatewayAccess()` 能校验 `/v1/models`
4. 最后补 CLI 测试
   - `import-provider` 参数解析
   - 输出状态摘要

## 当前确认后的宿主契约

基于 `sub2api-official-fresh` 宿主源码二次核对，当前 CRM 需要明确依赖以下宿主实现逻辑：

1. channel payload 不是扁平 `model_mapping`
   - 宿主管理接口实际接收：`map[string]map[string]string`
   - CRM 仍可在内部保留 `map[string]string`，但序列化时必须按 platform 包成嵌套结构
2. channel `model_pricing[].platform` 不能留空
   - 宿主 create/update handler 会把空 platform 回退为 `anthropic`
   - 对 OpenAI-compatible provider，CRM 必须显式写入 provider platform；若调用方未给值，至少回退到 `openai`
3. channel 完整收口字段必须同时存在
   - `model_mapping`
   - `model_pricing`
   - `restrict_models=true`
   - `billing_model_source=channel_mapped`
4. subscription access 的最终 probe key 是 managed key
   - 宿主 user `/api/v1/keys` 创建 key 时不要求携带 `group_id`
   - group 绑定是后续管理员 `PUT /api/v1/admin/api-keys/:id` 动作
   - gateway `/v1/models` 鉴权/订阅校验走绑定后的 managed key，而不是外部传入原始 key
5. `/v1/models` 与 `/v1/chat/completions` 不能混为同一层验收
   - 宿主 API key middleware 会先做 key/user/group/subscription/balance 校验
   - `/v1/models` 通过只证明普通用户访问链路与 group/subscription 绑定成立，不自动证明 completion 可用

## 当前验证缺口

1. 回滚删除链路尚未接入真实宿主 HTTP 路径，当前仅在服务层保留失败状态，不宣称真实宿主回滚已闭环
2. completion 兼容性仍是独立层，不能由 account/channel 落库测试替代
3. 需要把上面的宿主契约固化进单测，避免 future drift

## 追加测试设计

1. `internal/host/sub2api/sub2api_test.go`
   - `TestCreateChannelWithMock`
     - 断言创建 channel 时下发完整 `model_mapping/model_pricing/restrict_models/billing_model_source`
   - `TestUpdateChannelWithMock`
     - 断言既有 channel 纠偏走 `PUT /api/v1/admin/channels/:id`
   - `TestCreateChannelRequestMarshalJSONDefaultsPricingPlatform`
     - 断言 pricing platform 空值时优先回退 `request.Platform`，再回退 `openai`
   - `TestEnsureSubscriptionAccessWithMock`
     - 断言 managed key 创建与 admin bind group 分两步完成
2. `internal/access/closure_test.go`
   - 断言 subscription closure 最终 probe 使用 managed key，而不是显式传入的 caller probe key
3. `internal/provision/import_service_test.go`
   - `TestImportCreatesChannelWithManifestModelMapping`
   - `TestImportReconcilesExistingChannelConfiguration`
   - 断言 buildChannelRequest 生成的 channel 配置能直接满足宿主 channel handler 的最小契约

## 完成标准

- `go test ./internal/host/sub2api ./internal/access ./internal/provision -count=1` 通过
- 关键宿主契约均有单测覆盖
- CLI 能从真实 pack 读取 provider
- 导入报告明确输出 batch/provider/access 三种状态
