test(presenter): 坏清单用例按错误分类区分「JSON 坏」与「签名被拒」
台账 §10 第 7 条 / M199 复审 P3-2:原来只断言「结果是 Failed」,而它喂的每一份清单都签对了 ——验签一旦 fail-open 或签名错误被误分类,这条用例照样绿。 现在两组各钉自己的分类: - 内容坏(5 个变体,签名都对):kind 必须是 invalid_manifest / invalid_version; - 字节好、只有签名对不上(放行就会报「有更新 0.9.0」):kind 必须是 signature_invalid 且不可重试。 变异证明(只改实现侧,跑完已还原): - A core::update::signature::verify_manifest fail-open → ② 分支红,实际 Available(0.9.0); - B core::update::manifest::from_json 把解析失败误分类成 SignatureInvalid → ① 分支红, 「not json:坏内容分类是 signature_invalid」。 两个变异各只打红对应分支,说明两类坏输入确实各走各的分类。 文件仍在 400 行红线内(394 行)。
This commit is contained in:
1 parent
06fcbb8a51
commit
133f14ba0b
1 file changed
+31
-10
@@ -6,7 +6,7 @@
|
|||||||
//! | 用例 | 守什么 |
|
//! | 用例 | 守什么 |
|
||||||
//! |---|---|
|
//! |---|---|
|
||||||
//! | [`version_matrix_decides_who_gets_prompted`] | 版本更高才提示;同版本/更低一律不提示;`v0.2.3` 这类真实 tag 形态 |
|
//! | [`version_matrix_decides_who_gets_prompted`] | 版本更高才提示;同版本/更低一律不提示;`v0.2.3` 这类真实 tag 形态 |
|
||||||
//! | [`a_broken_manifest_degrades_to_failure_not_up_to_date`] | 清单坏 JSON → 「检查失败」,**不是**「已是最新」 |
|
//! | [`a_broken_manifest_degrades_to_failure_not_up_to_date`] | 坏 JSON → `invalid_*`、签名被拒 → `signature_*`;两种都是「检查失败」,**不是**「已是最新」 |
|
||||||
//! | [`a_higher_version_without_our_platform_is_not_up_to_date`] | 有新版但没本平台产物 → 单独的变体 |
|
//! | [`a_higher_version_without_our_platform_is_not_up_to_date`] | 有新版但没本平台产物 → 单独的变体 |
|
||||||
//! | [`sha256_mismatch_refuses_the_apply_and_leaves_no_temp_file`] | 摘要不符拒绝应用、暂存文件不残留、目标没被换掉 |
|
//! | [`sha256_mismatch_refuses_the_apply_and_leaves_no_temp_file`] | 摘要不符拒绝应用、暂存文件不残留、目标没被换掉 |
|
||||||
//! | [`a_successful_apply_leaves_the_new_file_and_a_backup`] | 换上去的是新字节、旧字节留在备份里、回报是「就位」 |
|
//! | [`a_successful_apply_leaves_the_new_file_and_a_backup`] | 换上去的是新字节、旧字节留在备份里、回报是「就位」 |
|
||||||
@@ -191,11 +191,16 @@ fn version_matrix_decides_who_gets_prompted() {
|
|||||||
|
|
||||||
// ------------------------------------------------------------- 清单契约降级
|
// ------------------------------------------------------------- 清单契约降级
|
||||||
|
|
||||||
/// 坏 JSON / 缺字段 ⇒ **检查失败**,绝不是「已是最新」。
|
/// 坏 JSON / 缺字段 / 签名被拒 ⇒ **检查失败**,绝不是「已是最新」;而且两种坏法**分类不同**。
|
||||||
///
|
///
|
||||||
/// 两个变体的文案与后果完全不同:说成「已是最新」会让用户以为网络通、版本新,实际一个字节都没取到。
|
/// M199 复审 P3-2:旧版只断言「结果是 Failed」,而它喂的清单都**签对了**——验签一旦 fail-open,
|
||||||
|
/// 坏签名清单会一路走到解析,坏 JSON 照样解析失败,断言全绿。现在内容坏必须落 `invalid_*`,
|
||||||
|
/// 签名坏必须落 `signature_*` 且不可重试。
|
||||||
|
/// 抓得住:验签被跳过/放行(② 会拿到 `Available 0.9.0`)、签名与解析互相误分类、失败被折叠成「已是最新」;
|
||||||
|
/// 抓不住:签名算法细节(信封字段、`manifest_sha256` 冗余校验、钥表)——那在 core 的 `signature_cases.rs`。
|
||||||
#[test]
|
#[test]
|
||||||
fn a_broken_manifest_degrades_to_failure_not_up_to_date() {
|
fn a_broken_manifest_degrades_to_failure_not_up_to_date() {
|
||||||
|
// ① 签名是对的(`write_manifest` 成对落盘),内容坏:分类只能是「清单结构 / 版本号不合法」。
|
||||||
for (label, manifest) in [
|
for (label, manifest) in [
|
||||||
("not json", "{ this is not json".to_string()),
|
("not json", "{ this is not json".to_string()),
|
||||||
(
|
(
|
||||||
@@ -221,16 +226,32 @@ fn a_broken_manifest_degrades_to_failure_not_up_to_date() {
|
|||||||
let tmp = TempDir::new("badmanifest");
|
let tmp = TempDir::new("badmanifest");
|
||||||
write_manifest(&tmp, &manifest);
|
write_manifest(&tmp, &manifest);
|
||||||
let outcome = outcome_of(job(&tmp).check(&version("0.2.2"), Channel::Stable));
|
let outcome = outcome_of(job(&tmp).check(&version("0.2.2"), Channel::Stable));
|
||||||
|
let Some(failure) = outcome.failure() else {
|
||||||
|
panic!("{label}:坏清单必须降级成「检查失败」,实际 {outcome:?}");
|
||||||
|
};
|
||||||
|
let kind = failure.kind.as_str();
|
||||||
assert!(
|
assert!(
|
||||||
matches!(outcome, UpdateOutcome::Failed(_)),
|
matches!(kind, "invalid_manifest" | "invalid_version"),
|
||||||
"{label}:坏清单必须降级成「检查失败」,实际 {outcome:?}"
|
"{label}:坏内容分类是 {kind}"
|
||||||
);
|
|
||||||
let failure = outcome.failure().expect("失败带原因");
|
|
||||||
assert!(
|
|
||||||
!failure.message.trim().is_empty(),
|
|
||||||
"{label}:失败要有人话原因"
|
|
||||||
);
|
);
|
||||||
|
assert!(!failure.message.trim().is_empty(), "{label}:失败没原因");
|
||||||
}
|
}
|
||||||
|
// ② 清单字节是好的(放行就会报「有更新 0.9.0」),只有签名对不上:必须落 `signature_invalid`。
|
||||||
|
let tmp = TempDir::new("badsignature");
|
||||||
|
tmp.write(
|
||||||
|
"dist/stable.json",
|
||||||
|
manifest_for("0.9.0", b"new", "").as_bytes(),
|
||||||
|
);
|
||||||
|
tmp.write(
|
||||||
|
"dist/stable.json.sig",
|
||||||
|
signature_envelope("another").as_bytes(),
|
||||||
|
);
|
||||||
|
let outcome = outcome_of(job(&tmp).check(&version("0.2.2"), Channel::Stable));
|
||||||
|
let Some(failure) = outcome.failure() else {
|
||||||
|
panic!("签名验不过必须拒装(放行就会报「有更新 0.9.0」),实际 {outcome:?}");
|
||||||
|
};
|
||||||
|
assert_eq!(failure.kind, "signature_invalid", "签名被拒的分类");
|
||||||
|
assert!(!failure.retryable, "签名问题不可重试");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 清单里**一条发布都没有**(`{"channel":"stable"}` / 空 `releases`)⇒ 已是最新,不是失败。
|
/// 清单里**一条发布都没有**(`{"channel":"stable"}` / 空 `releases`)⇒ 已是最新,不是失败。
|
||||||
|
|||||||
Reference in new issue
Block a user