feat: add official full pull smoke

Emit runtime download progress and validation summaries for official sync.

Add repeatable real-network full pull smoke runbook and script.

Fixes #4

Fixes #6
This commit is contained in:
2026-07-14 00:48:59 +08:00
parent 2ff99ae9e3
commit 25c2c4d40f
15 changed files with 435 additions and 57 deletions
+42 -8
View File
@@ -1048,6 +1048,10 @@ impl OfficialUpdateService {
};
if !should_download {
progress(OfficialUpdateProgress::new(
"audit",
verification_progress_message(&report.verification_summary),
));
progress(OfficialUpdateProgress::new(
"finish",
"资源已是最新;无需下载",
@@ -1138,6 +1142,16 @@ impl OfficialUpdateService {
let final_audit = staging_fetcher
.audit_local_manifest(&pull_plan)
.map_err(anyhow::Error::msg)?;
let final_verification_summary = OfficialVerificationSummary::new(
final_audit.manifest_blake3_verified_count(),
final_audit.repair_needed_count(),
pull_report.official_hash_verified_count(),
final_audit.zip_structure_verified_count(),
);
progress(OfficialUpdateProgress::new(
"audit",
verification_progress_message(&final_verification_summary),
));
progress(OfficialUpdateProgress::new(
"snapshot",
format!("写入快照 {}", staging_snapshot_path.display()),
@@ -1175,12 +1189,7 @@ impl OfficialUpdateService {
report.official_seed_hash_verified_count = pull_report.official_hash_verified_count();
report.local_manifest_verified_count = final_audit.verified_count();
report.local_manifest_repair_needed_count = final_audit.repair_needed_count();
report.verification_summary = OfficialVerificationSummary::new(
final_audit.manifest_blake3_verified_count(),
final_audit.repair_needed_count(),
pull_report.official_hash_verified_count(),
final_audit.zip_structure_verified_count(),
);
report.verification_summary = final_verification_summary;
report.snapshot_written = Some(final_snapshot_path);
progress(OfficialUpdateProgress::new(
@@ -1356,7 +1365,13 @@ fn progress_from_pull_event(event: OfficialResourcePullProgress) -> OfficialUpda
match event.kind {
OfficialResourcePullProgressKind::Started => OfficialUpdateProgress::new(
"download",
format!("({}/{}) 开始 {}", event.index, event.total, event.url),
format!(
"下载进度:总体 {}/{} ({:.1}%);单文件开始 URL={}",
event.index,
event.total,
download_progress_percent(event.index, event.total),
event.url
),
)
.with_download_progress(&event),
OfficialResourcePullProgressKind::Finished => {
@@ -1364,9 +1379,10 @@ fn progress_from_pull_event(event: OfficialResourcePullProgress) -> OfficialUpda
OfficialUpdateProgress::new(
"download",
format!(
"({}/{}) 完成 状态={} 文件字节={} 本轮传输字节={} {}",
"下载进度:总体 {}/{} ({:.1}%);单文件完成 状态={} 文件字节={} 本轮传输字节={} URL={}",
event.index,
event.total,
download_progress_percent(event.index, event.total),
status,
event.bytes.unwrap_or(0),
event.transferred_bytes.unwrap_or(0),
@@ -1378,6 +1394,24 @@ fn progress_from_pull_event(event: OfficialResourcePullProgress) -> OfficialUpda
}
}
fn download_progress_percent(index: usize, total: usize) -> f64 {
if total == 0 {
100.0
} else {
(index as f64 / total as f64) * 100.0
}
}
fn verification_progress_message(summary: &OfficialVerificationSummary) -> String {
format!(
"校验结果:官方 .hash={};本地 BLAKE3 通过={};本地需修复={}ZIP 结构通过={}",
summary.official_hash_verified_count,
summary.local_manifest_blake3_verified_count,
summary.local_manifest_repair_needed_count,
summary.zip_structure_verified_count
)
}
fn localized_pull_status(status: crate::OfficialResourcePullStatus) -> &'static str {
match status {
crate::OfficialResourcePullStatus::SkippedExisting => "已复用",
@@ -229,6 +229,17 @@ fn official_update_first_run_pulls_without_pre_audit() {
assert!(events
.iter()
.any(|event| { event.stage == "decision" && event.message.contains("首次拉取=true") }));
assert!(events.iter().any(|event| {
event.stage == "download"
&& event.message.contains("下载进度:总体")
&& event.message.contains("单文件")
}));
assert!(events.iter().any(|event| {
event.stage == "audit"
&& event.message.contains("校验结果:官方 .hash=5")
&& event.message.contains("本地 BLAKE3 通过=19")
&& event.message.contains("ZIP 结构通过=6")
}));
let published = report.published_version_path.as_ref().unwrap();
assert!(published.exists());
@@ -280,6 +291,12 @@ fn official_update_second_run_audits_existing_resources_before_reuse() {
assert!(events.iter().any(|event| {
event.stage == "audit" && event.message.contains("审计本地下载 manifest")
}));
assert!(events.iter().any(|event| {
event.stage == "audit"
&& event.message.contains("校验结果:官方 .hash=0")
&& event.message.contains("本地 BLAKE3 通过=19")
&& event.message.contains("ZIP 结构通过=6")
}));
assert!(events.iter().any(|event| {
event.stage == "decision" && event.message.contains("首次拉取=false")
}));