mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 07:24:55 +08:00
test(api): 固化 Rust-Go RPC 契约
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"bat-api/internal/backendrpc"
|
||||
)
|
||||
|
||||
func readContractFixture(t *testing.T, name string) []byte {
|
||||
t.Helper()
|
||||
data, err := os.ReadFile(filepath.Join("testdata", "contract", name))
|
||||
if err != nil {
|
||||
t.Fatalf("read contract fixture %s: %v", name, err)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func TestRustContractFixturesPreserveGoMirror(t *testing.T) {
|
||||
availableRaw := readContractFixture(t, "catalog-status.available.json")
|
||||
unavailableRaw := readContractFixture(t, "catalog-status.unavailable.json")
|
||||
manifestRaw := readContractFixture(t, "resource-manifest.page0.json")
|
||||
snapshotRaw := readContractFixture(t, "official-sync-snapshot.json")
|
||||
|
||||
for name, raw := range map[string][]byte{
|
||||
"catalog available": availableRaw,
|
||||
"catalog unavailable": unavailableRaw,
|
||||
"resource manifest": manifestRaw,
|
||||
"snapshot": snapshotRaw,
|
||||
} {
|
||||
if bytes.Contains(raw, []byte("/tmp/")) {
|
||||
t.Fatalf("%s contains an absolute temporary path", name)
|
||||
}
|
||||
if bytes.Contains(raw, []byte("r93_fixture")) {
|
||||
t.Fatalf("%s contains a concrete release token", name)
|
||||
}
|
||||
}
|
||||
|
||||
summary, resourceRoot, available := parseCatalogStatus(availableRaw)
|
||||
if !available || summary == nil {
|
||||
t.Fatal("available catalog fixture was not accepted")
|
||||
}
|
||||
if summary.Status != "published" || summary.StatusCode != "official.published" {
|
||||
t.Fatalf("catalog status=%+v", summary)
|
||||
}
|
||||
if summary.DistributionStatusCode != "distribution.ready" {
|
||||
t.Fatalf("distribution status=%q", summary.DistributionStatusCode)
|
||||
}
|
||||
if resourceRoot != "${RESOURCE_ROOT}" || summary.VersionID != "${VERSION_ID}" {
|
||||
t.Fatalf("catalog root/version=%q/%q", resourceRoot, summary.VersionID)
|
||||
}
|
||||
if summary.LauncherMetadata == nil || summary.GameMainConfig == nil {
|
||||
t.Fatalf("catalog optional objects missing: %+v", summary)
|
||||
}
|
||||
if summary.LauncherMetadata.GameLowestVersion != "" {
|
||||
t.Fatalf("null optional field became non-empty: %q", summary.LauncherMetadata.GameLowestVersion)
|
||||
}
|
||||
if summary.GameMainConfig.DefaultConnectionGroup != "${CONNECTION_GROUP}" {
|
||||
t.Fatalf("game config=%+v", summary.GameMainConfig)
|
||||
}
|
||||
|
||||
if got, _, ok := parseCatalogStatus(unavailableRaw); got != nil || ok {
|
||||
t.Fatalf("unavailable catalog parsed as available: got=%+v ok=%v", got, ok)
|
||||
}
|
||||
|
||||
var manifest backendrpc.ResourceManifestPage
|
||||
if err := json.Unmarshal(manifestRaw, &manifest); err != nil {
|
||||
t.Fatalf("decode resource manifest: %v", err)
|
||||
}
|
||||
if !manifest.Available || manifest.ManifestVersion != 1 || manifest.TotalEntries != 2 {
|
||||
t.Fatalf("manifest header=%+v", manifest)
|
||||
}
|
||||
if len(manifest.Entries) != 2 {
|
||||
t.Fatalf("manifest entries=%d", len(manifest.Entries))
|
||||
}
|
||||
if manifest.Entries[0].Bytes == nil || *manifest.Entries[0].Bytes != 21 {
|
||||
t.Fatalf("manifest first bytes=%v", manifest.Entries[0].Bytes)
|
||||
}
|
||||
if manifest.Entries[0].BLAKE3 == "" || !strings.Contains(manifest.Entries[0].Destination, "{addressables-root}") {
|
||||
t.Fatalf("manifest first entry=%+v", manifest.Entries[0])
|
||||
}
|
||||
|
||||
var snapshot struct {
|
||||
AppVersion string `json:"app_version"`
|
||||
BundleVersion string `json:"bundle_version"`
|
||||
GameMainConfigBootstrap *GameMainConfigSummary `json:"game_main_config_bootstrap"`
|
||||
LauncherMetadata *LauncherMetadataSummary `json:"launcher_metadata"`
|
||||
LegacyGameMainConfig json.RawMessage `json:"game_main_config"`
|
||||
}
|
||||
if err := json.Unmarshal(snapshotRaw, &snapshot); err != nil {
|
||||
t.Fatalf("decode snapshot: %v", err)
|
||||
}
|
||||
if snapshot.AppVersion != "${APP_VERSION}" || snapshot.BundleVersion != "${BUNDLE_VERSION}" {
|
||||
t.Fatalf("snapshot versions=%q/%q", snapshot.AppVersion, snapshot.BundleVersion)
|
||||
}
|
||||
if snapshot.GameMainConfigBootstrap == nil || snapshot.GameMainConfigBootstrap.ServerInfoDataURL == "" {
|
||||
t.Fatalf("snapshot game config=%+v", snapshot.GameMainConfigBootstrap)
|
||||
}
|
||||
if snapshot.GameMainConfigBootstrap.ServerInfoDataURL != "https://yostar-serverinfo.bluearchiveyostar.com/{server-info-path}" {
|
||||
t.Fatalf("snapshot server info=%q", snapshot.GameMainConfigBootstrap.ServerInfoDataURL)
|
||||
}
|
||||
if snapshot.LauncherMetadata == nil || snapshot.LauncherMetadata.ManifestSource == "" {
|
||||
t.Fatalf("snapshot launcher metadata=%+v", snapshot.LauncherMetadata)
|
||||
}
|
||||
if len(snapshot.LegacyGameMainConfig) != 0 {
|
||||
t.Fatalf("legacy game_main_config field unexpectedly present: %s", snapshot.LegacyGameMainConfig)
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
# Rust contract fixtures
|
||||
|
||||
本目录保存 Go `bat-api` 对 Rust `bat.sock` / snapshot JSON 的跨语言
|
||||
contract fixture。JSON 由 Rust 代码路径产出后归一化,只替换本机路径、版本号、
|
||||
时间戳和 release URL token,不改字段名、层级、null 语义或数字类型。
|
||||
|
||||
覆盖范围:
|
||||
|
||||
- `catalog.status` 可用与不可用响应。
|
||||
- `resource.manifest` 第一页分页响应。
|
||||
- 对应 release 的 `official-sync-snapshot.json`。
|
||||
- `launcher_metadata` 与 `game_main_config_bootstrap` 的 Go mirror 解码。
|
||||
|
||||
这些 fixture 只用于 schema / mirror 回归,不代表真实资源版本,也不替代 live
|
||||
daemon socket 或完整发布切换验证。
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"addressables_catalog_marker_count": 0,
|
||||
"addressables_root": "https://prod-clientpatch.bluearchiveyostar.com/{addressables-root}",
|
||||
"app_version": "${APP_VERSION}",
|
||||
"available": true,
|
||||
"bundle_version": "${BUNDLE_VERSION}",
|
||||
"connection_group_name": "${CONNECTION_GROUP}",
|
||||
"distribution_status": "ready",
|
||||
"distribution_status_code": "distribution.ready",
|
||||
"endpoint_count": 0,
|
||||
"endpoint_marker_count": 0,
|
||||
"game_main_config_bootstrap": {
|
||||
"default_connection_group": "${CONNECTION_GROUP}",
|
||||
"server_info_data_url": "https://yostar-serverinfo.bluearchiveyostar.com/{server-info-path}"
|
||||
},
|
||||
"launcher_metadata": {
|
||||
"game_latest_file_path": "${GAME_PACKAGE_PATH}",
|
||||
"game_latest_version": "${APP_VERSION}",
|
||||
"game_lowest_version": null,
|
||||
"game_start_exe_name": "${GAME_START_EXE_NAME}",
|
||||
"game_start_params": [
|
||||
"${GAME_START_PARAM}"
|
||||
],
|
||||
"launcher_version": "${LAUNCHER_VERSION}",
|
||||
"manifest_file_count": 2,
|
||||
"manifest_source": "${LAUNCHER_MANIFEST_SOURCE}",
|
||||
"manifest_url": "https://launcher-pkg-ba-jp.yo-star.com/{launcher-manifest-path}"
|
||||
},
|
||||
"official_seed_hash_marker_count": 0,
|
||||
"snapshot_version": 2,
|
||||
"status": "published",
|
||||
"status_code": "official.published",
|
||||
"status_phase": "official_sync",
|
||||
"status_retryable": false,
|
||||
"status_terminal": true,
|
||||
"version": {
|
||||
"completed_unix_seconds": 1000,
|
||||
"id": "${VERSION_ID}",
|
||||
"resource_root": "${RESOURCE_ROOT}"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"available": false,
|
||||
"distribution_status": "blocked",
|
||||
"distribution_status_code": "distribution.blocked",
|
||||
"status": "unavailable",
|
||||
"status_code": "official.unavailable",
|
||||
"status_phase": "official_sync",
|
||||
"status_retryable": false,
|
||||
"status_terminal": true
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"addressables_root": "https://prod-clientpatch.bluearchiveyostar.com/{addressables-root}",
|
||||
"app_version": "${APP_VERSION}",
|
||||
"bundle_version": "${BUNDLE_VERSION}",
|
||||
"connection_group_name": "${CONNECTION_GROUP}",
|
||||
"endpoint_markers": [],
|
||||
"endpoints": [],
|
||||
"game_main_config_bootstrap": {
|
||||
"default_connection_group": "${CONNECTION_GROUP}",
|
||||
"server_info_data_url": "https://yostar-serverinfo.bluearchiveyostar.com/{server-info-path}"
|
||||
},
|
||||
"launcher_metadata": {
|
||||
"game_latest_file_path": "${GAME_PACKAGE_PATH}",
|
||||
"game_latest_version": "${APP_VERSION}",
|
||||
"game_lowest_version": null,
|
||||
"game_start_exe_name": "${GAME_START_EXE_NAME}",
|
||||
"game_start_params": [
|
||||
"${GAME_START_PARAM}"
|
||||
],
|
||||
"launcher_version": "${LAUNCHER_VERSION}",
|
||||
"manifest_file_count": 2,
|
||||
"manifest_source": "${LAUNCHER_MANIFEST_SOURCE}",
|
||||
"manifest_url": "https://launcher-pkg-ba-jp.yo-star.com/{launcher-manifest-path}"
|
||||
},
|
||||
"snapshot_version": 2
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"available": true,
|
||||
"entries": [
|
||||
{
|
||||
"blake3": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"bytes": 21,
|
||||
"destination": "prod-clientpatch.bluearchiveyostar.com/{addressables-root}/TableBundles/TableCatalog.bytes",
|
||||
"url": "https://prod-clientpatch.bluearchiveyostar.com/{addressables-root}/TableBundles/TableCatalog.bytes"
|
||||
},
|
||||
{
|
||||
"blake3": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"bytes": 10,
|
||||
"destination": "prod-clientpatch.bluearchiveyostar.com/{addressables-root}/TableBundles/TableCatalog.hash",
|
||||
"url": "https://prod-clientpatch.bluearchiveyostar.com/{addressables-root}/TableBundles/TableCatalog.hash"
|
||||
}
|
||||
],
|
||||
"limit": 2,
|
||||
"manifest_version": 1,
|
||||
"offset": 0,
|
||||
"resource_root": "${RESOURCE_ROOT}",
|
||||
"total_entries": 2
|
||||
}
|
||||
Reference in New Issue
Block a user