feat: track official versions and import resources

Persist official version-state, extend CAS/ResourceRepository import classification, and add offline catalog and failure regression fixtures.

Fixes #13

Fixes #12

Fixes #8
This commit is contained in:
2026-07-15 19:53:10 +08:00
parent 4192d7ee4c
commit 90307a3243
30 changed files with 1269 additions and 157 deletions
@@ -0,0 +1,7 @@
{
"data_url": "https://prod-clientpatch.bluearchiveyostar.com/r93_token/TableBundles/TableCatalog.bytes",
"hash_url": "https://prod-clientpatch.bluearchiveyostar.com/r93_token/TableBundles/TableCatalog.hash",
"data": "ExcelDB.db",
"official_hash": "0",
"expected_error_contains": "官方 hash 校验失败"
}
@@ -0,0 +1,7 @@
{
"url": "https://prod-clientpatch.bluearchiveyostar.com/r93_token/TableBundles/Forbidden.bytes",
"http_status": 403,
"failure_kind": "http_forbidden",
"retryable": false,
"expected_attempts": 1
}
@@ -0,0 +1,7 @@
{
"url": "https://prod-clientpatch.bluearchiveyostar.com/r93_token/TableBundles/Missing.bytes",
"http_status": 404,
"failure_kind": "http_not_found",
"retryable": false,
"expected_attempts": 1
}
@@ -2,6 +2,8 @@
"imported": [
{
"bytes": 143,
"category": "asset_bundle",
"resource_type": "AssetBundle",
"source_path": "synthetic/minimal.bundle",
"unityfs": {
"block_count": 1,
@@ -13,6 +15,9 @@
}
}
],
"category_counts": {
"asset_bundle": 1
},
"resource_count": 1,
"skipped": [
"synthetic/catalog.json"
@@ -7,9 +7,9 @@ use bat_adapters::official::yostar_jp::{
YostarJpResourceDiscoveryPlan, YostarJpResourceEndpointKind, YostarJpServerInfo,
};
use bat_infrastructure::{
build_official_pull_plan_from_platform_inventory, OfficialGameMainConfigBootstrapService,
OfficialResourcePullService, OfficialUpdateConfig, OfficialUpdateProgress,
OfficialUpdateService, OfficialUpdateStatus,
build_official_pull_plan_from_platform_inventory, read_version_state,
OfficialGameMainConfigBootstrapService, OfficialResourcePullService, OfficialUpdateConfig,
OfficialUpdateProgress, OfficialUpdateService, OfficialUpdateStatus,
};
use std::collections::HashMap;
use std::fs;
@@ -252,15 +252,26 @@ fn official_update_first_run_pulls_without_pre_audit() {
.unwrap()
.file_type()
.is_symlink());
assert_eq!(
config
.output_root
.join("official-download-manifest.json")
.exists(),
false
);
assert!(!config
.output_root
.join("official-download-manifest.json")
.exists());
assert!(published.join("official-download-manifest.json").exists());
assert!(published.join("official-sync-snapshot.json").exists());
let version_state = read_version_state(&report.version_state_path)
.unwrap()
.unwrap();
assert_eq!(
version_state
.current_completed_version
.as_ref()
.unwrap()
.version_path
.as_deref(),
Some(published.as_path())
);
assert!(version_state.in_progress_version.is_none());
assert!(version_state.failed_versions.is_empty());
}
#[test]
@@ -305,6 +316,18 @@ fn official_update_second_run_audits_existing_resources_before_reuse() {
.unwrap()
.file_type()
.is_symlink());
let version_state = read_version_state(&report.version_state_path)
.unwrap()
.unwrap();
assert_eq!(
version_state
.current_completed_version
.as_ref()
.unwrap()
.resource_root,
report.active_resource_root
);
assert!(version_state.in_progress_version.is_none());
}
struct TestHarness {
@@ -99,17 +99,20 @@ async fn imports_synthetic_addressables_catalog_bundle_against_golden() {
"imported": report.imported.iter().map(|imported| {
json!({
"bytes": imported.bytes,
"category": imported.category.as_str(),
"resource_type": format!("{:?}", imported.resource_type),
"source_path": imported.source_path,
"unityfs": {
"block_count": imported.unityfs.block_count,
"directories": imported.unityfs.directories,
"directory_count": imported.unityfs.directory_count,
"unity_version": imported.unityfs.unity_version,
}
"unityfs": imported.unityfs.as_ref().map(|unityfs| json!({
"block_count": unityfs.block_count,
"directories": unityfs.directories,
"directory_count": unityfs.directory_count,
"unity_version": unityfs.unity_version,
})),
})
}).collect::<Vec<_>>(),
"resource_count": indexed.len(),
"skipped": report.skipped,
"category_counts": report.category_counts,
});
let expected: serde_json::Value =
serde_json::from_str(include_str!("golden/synthetic_phase2_import.json")).unwrap();