Files
BlueArchiveToolkit/docs/reports/BAT_API_CONTRACT_FIXTURE_HANDOFF.md
T
nyaKazuha 7f465523e1
bat-rust / Build and test Go API (push) Canceled after 0s
bat-rust / Build and test Rust (push) Canceled after 0s
fix(bat-api): 完成 issue #19 同机 live 联调
2026-08-29 22:58:01 +08:00

8.4 KiB
Raw Blame History

bat-api / Rust bat Contract Fixture Handoff

更新时间:2026-08-29

本文用于两个 Codex 窗口之间间接联调 bat-api 与 Rust bat 的跨语言 contract fixture。 仓库内归一化 fixture 已交付;本文保留生成、审核和后续扩展的协作协议。

2026-07-31 更新:已审核归一化 fixture 已落入 internal/api/testdata/contract/Go 侧通过 internal/api/contract_fixture_test.go 固化 mirror struct 验证。本文件保留为 后续重新生成或扩展 contract fixture 时的协作协议。

最小上下文包

另一个窗口不需要知道本窗口的完整对话,只需要遵守以下上下文:

  • 本次联调对象是 Rust bat RPC / snapshot JSON 与 Go bat-api mirror struct 的 contract fixture。
  • 联调不要求本地运行全量长期服务端 bat;允许 Rust 侧使用 fixture root 或临时目录走真实代码路径导出 JSON。
  • fixture 审核前只能放在 /tmp/bat-contract-fixture/,不能直接提交到仓库。
  • Go 侧已经实现 player-facing HTTP 鉴权、限流、访问日志、反代适配、OpenAPI 和 /admin/ 控制入口;仓库内 contract fixture 和同机 live daemon socket / 完整 fixture release 切换联调均已完成,命令为 make bat-api-local-live-smoke
  • Go 侧当前相关代码入口:
    • internal/api/rpc_release.go
    • internal/api/release_index.go
    • internal/api/responses.go
    • internal/backendrpc/

背景

  • Rust bat 是资源同步、状态发布和 bat.sock RPC 的权威实现。
  • Go bat-api 是只读 HTTP bootstrap / 分发服务,消费 Rust RPC 输出和已发布资源目录。
  • contract fixture 不能由任一侧手写猜测;必须由 Rust 侧真实输出,经归一化和用户审核后,再由 Go 侧固化测试。

非目标

  • 不引入真实玩家账号、登录、网关、鉴权绕过或游戏业务 API fixture。
  • 不写入开发机绝对资源路径,例如 /home/wanye/D/BlueArchive
  • 不把当前某个真实版本号、日期、远程目录或本地目录写成长期契约。
  • 不让 Go fixture 反向约束 Rust 内部实现;只约束对外 JSON contract。

建议共享目录

联调前使用临时目录交换未审核产物:

/tmp/bat-contract-fixture/
  rust/
    catalog-status.available.raw.json
    catalog-status.unavailable.raw.json
    resource-manifest.page0.raw.json
    official-sync-snapshot.raw.json
  normalized/
    catalog-status.available.json
    catalog-status.unavailable.json
    resource-manifest.page0.json
    official-sync-snapshot.json
  notes.md

只有用户审核通过后,才允许把归一化 fixture 落入仓库,例如:

internal/api/testdata/contract/

Rust 侧需要产出

Rust 窗口请基于当前真实代码生成或导出以下 JSON:

  1. catalog.status available=true 响应。
  2. catalog.status available=false 响应。
  3. resource.manifest 第一页响应,至少包含 1 到 2 个 entries。
  4. 对应 release 的 official-sync-snapshot.json

输出应来自 Rust 代码路径,而不是手写 JSON。允许使用 fixture resource root 或临时目录,但不能依赖开发机真实资源目录。

归一化规则

归一化只允许处理环境相关值,不改变 schema:

  • 绝对路径归一化为 ${RESOURCE_ROOT}${STATE_DIR}
  • 版本 id 归一化为 ${VERSION_ID}
  • 时间戳可归一化为固定小整数或 ${COMPLETED_UNIX_SECONDS}
  • 真实 URL host 保留;路径中若含具体 release token,可归一化为 {addressables-root} / {manifest-path}
  • 字段名、字段类型、字段层级、null / missing / array / number 语义不得修改。

Go 侧验证范围

Go 窗口读取归一化后的 JSON,验证:

  1. parseCatalogStatus 能解析 available=true,并正确映射:
    • app_version
    • bundle_version
    • connection_group_name
    • addressables_root
    • version.id
    • version.completed_unix_seconds
    • version.resource_root
    • launcher_metadata
    • game_main_config_bootstrap
  2. parseCatalogStatusavailable=false 返回不可用而不是错误。
  3. resource.manifest entry 字段能映射为 Go ResourceManifestEntry
    • url
    • destination
    • bytes
    • blake3
  4. 本地 snapshot fixture 与 RPC catalog.status 均使用 game_main_config_bootstrap
  5. bat-api bootstrap 和 launcher bootstrap 不泄露归一化前的开发机路径。

Go 侧审核通过后的落地建议:

  • internal/api/testdata/contract/catalog-status.available.json
  • internal/api/testdata/contract/catalog-status.unavailable.json
  • internal/api/testdata/contract/resource-manifest.page0.json
  • internal/api/testdata/contract/official-sync-snapshot.json
  • internal/api/contract_fixture_test.go

测试不应依赖 /tmp/bat-contract-fixture/;该目录只用于两窗口交接未审核产物。

必须覆盖的 optional 语义

至少需要两组 Rust 输出或派生 fixture 覆盖:

  1. optional 字段非空:
    • launcher_metadata.game_lowest_version
    • launcher_metadata.game_start_exe_name
    • launcher_metadata.manifest_source
    • game_main_config_bootstrap.server_info_data_url
    • game_main_config_bootstrap.default_connection_group
  2. optional 字段为 null 或缺省:
    • Go mirror 不应崩溃。
    • HTTP response 中按当前 Go struct omitempty 策略输出。

用户审核点

落仓库前请用户审核:

  • 归一化是否过度改变 Rust 真实输出。
  • fixture 是否意外绑定真实版本、日期、本机路径或私有部署路径。
  • game_main_config_bootstrap 在 RPC / snapshot 中是否保持同一语义。
  • optional 字段覆盖是否足够。

notes.md 模板

Rust 侧生成 /tmp/bat-contract-fixture/notes.md 时建议使用以下结构:

# bat contract fixture notes

## 生成命令

- catalog.status available=true: ...
- catalog.status available=false: ...
- resource.manifest page0: ...
- official-sync-snapshot: ...

## 原始输出来源

- Rust commit / working tree: ...
- 使用的 fixture root 或临时目录: ...
- 是否依赖真实开发机资源目录: 否

## 归一化

- `${RESOURCE_ROOT}`: ...
- `${STATE_DIR}`: ...
- `${VERSION_ID}`: ...
- `${COMPLETED_UNIX_SECONDS}`: ...
- URL 路径占位符: ...

## 需要用户审核

- ...

完成判定

contract fixture 工作只有在以下条件同时满足时才算完成:

  1. Rust 侧原始 JSON 来自真实 Rust 代码路径。
  2. 归一化 JSON 经过用户审核。
  3. Go 侧测试读取归一化 fixture 并验证 mirror struct / launcher bootstrap 行为。
  4. Go 测试不依赖开发机资源目录、远程长期运行 bat/tmp 中的交接目录。
  5. 文档记录 fixture 覆盖的风险和仍未覆盖的字段。

建议给另一个窗口的短指令

请读取 docs/reports/BAT_API_CONTRACT_FIXTURE_HANDOFF.md。
你负责 Rust bat 侧 contract fixture 原始输出:
1. catalog.status available=true
2. catalog.status available=false
3. resource.manifest page0
4. 对应 official-sync-snapshot.json
请输出到 /tmp/bat-contract-fixture/rust/,不要手写 JSON,不要引用开发机真实资源目录。
输出后在 /tmp/bat-contract-fixture/notes.md 说明生成命令、是否做过归一化、哪些字段需要用户审核。

当前状态

  • Go bat-api 已具备消费 launcher_metadata / game_main_config_bootstrap 的 mirror struct。
  • Go bat-api 已具备 player-facing HTTP 控制面、OpenAPI 和管理控制白名单。
  • 已归一化的 Rust contract fixture 已落仓库:
    • internal/api/testdata/contract/catalog-status.available.json
    • internal/api/testdata/contract/catalog-status.unavailable.json
    • internal/api/testdata/contract/resource-manifest.page0.json
    • internal/api/testdata/contract/official-sync-snapshot.json
  • 原始交接产物仍位于 /tmp/bat-contract-fixture/;仓库内归一化 fixture 用于 schema/mirror 回归, 同机 live socket 验证使用 make bat-api-local-live-smoke,不依赖该交接目录。
  • Go contract 测试读取仓库内归一化 fixture,不依赖 /tmp/bat-contract-fixture/、开发机资源目录或远端长期运行的 bat
  • 真实长期 daemon 的生产部署仍需由部署环境持续运行;仓库已在本地隔离环境通过真实 daemon socket 完成端到端调用、版本切换、无 release、RPC 断线和恢复验证。真实官方 网络全量下载仍由 make official-smoke 独立负责。