fix(app-iced): M120 r1 —— 徽标判据改回「类型是原版」;旧串黑名单翻成白名单

r1 复审 p2-1(真回归,我引入的):`loader_badge` 把「非原版 + 空版本号」也送进了
`Key::LoaderVanilla`。而 `host::game::loader_from_id("1.20.1-forge")` 这类 id 取不到版本段
(它按 `forge-` 找,id 却是 `-forge` 结尾),`LoaderInfo::badge()` 对它们给的正是品牌名
`Forge` / `NeoForge` / `OptiFine` —— 于是这三个显示成「原版 / Vanilla」。

判据改成「**加载器类型本身是原版** ⇒ 过词表,其余交给 `badge()`」:
- 修的是判据,不是加分支:函数注释本来就写着「只有原版这一支要过词表」,代码与注释相矛盾。
- 新回归测试 `the_loader_badge_keeps_the_brand_when_the_version_is_missing`:
  `1.20.1-forge` / `1.20.1-neoforge` / `1.20.1-OptiFine` 三条 id 各断言列表副文案与 LOADER
  卡的值都是品牌名(中英两套语言各跑一遍,品牌名两套语言相同)。
  变异实测:把判据改回旧写法 ⇒ 立刻红(`left: "1.20.4 · Vanilla" / right: "1.20.4 · Forge"`,
  与复审给的实证逐字相同)。

r1 复审 p2-2(不阻塞,按塔台要求顺手做):源码反扫从**黑名单 29 条旧串**翻成**白名单**。
- 先数过:五个页面源码里一共 13 个字面量,其中带 ASCII 单词的只有 6 条,全是专有名词 /
  技术标识 / 占位符名 / 族名前缀(`{date} {time} UTC` / `isolated` / `shared` / `pid` /
  `reason` / `Instances`),逐条写了理由。
- 判据反过来之后,往页面里**新写**一句英文也会红(复审抓到的哑点)——变异实测:
  写进 `"Ready"` ⇒ 红;写回 `"ALL INSTANCES"` ⇒ 红。
- 反向锚保留:扫描器必须真扫到 ≥6 条、`Texts::of(` / `Header::new(texts.title())` /
  `Key::Instances` / `Key::InstancesStage` 必须在用(防止断言空转)。
This commit is contained in:
AskaEth committed 2026-11-05 13:36:45 +08:00
1 parent 1a4e1fee12
commit f86be2bd38
3 files changed
+122 -58

No files matched your search

+55
View File
@@ -1,6 +1,7 @@
//! `app/view.rs` 的守门测试(rebase 到 5adac23 之后 view.rs 贴 400 行上限,按「测试独立成文件」拆出)。
use super::*;
use unlucky_i18n::Key;
use unlucky_presenter::dto::{
InstanceRuntime, InstanceSummary, LaunchStage, LoaderInfo, LoaderKind, Progress,
};
@@ -253,6 +254,60 @@ fn the_vanilla_badge_follows_the_language() {
assert_eq!(zh.instances.rows[0].supporting, "1.20.4 · 原版");
}
/// **品牌名不许被「版本号为空」吞掉**(M120 r1 复审的 p2-1 回归)。
///
/// 背景:`host::game::loader_from_id("1.20.1-forge")` 这类 id 里取不到版本段(它按 `forge-`
/// 找,而 id 的写法是 `-forge` 结尾),于是 `LoaderInfo { kind: Forge, version: "" }`。
/// `LoaderInfo::badge()` 对这种输入给的正是品牌名 `Forge`——所以判据只能是「类型是原版」,
/// 不能是「版本号为空」。
///
/// 三个 id 各一条断言(复审点名的三档);`Vanilla` 那一支由
/// `the_vanilla_badge_follows_the_language` 守,两条合起来把 `loader_badge` 的两侧都钉住。
///
/// 抓得住:`loader_badge` 把「非原版 + 空版本号」也送进 `Key::LoaderVanilla`
/// (r1 的真实形状:`left: "1.20.4 · Vanilla" / right: "1.20.4 · Forge"`);
/// 抓不住:`loader_from_id` 认不出自家 `-fabric-` / `-quilt-` 口径这个**更老**的 bug
/// (在 presenter 里,另一条 mission)。
#[test]
fn the_loader_badge_keeps_the_brand_when_the_version_is_missing() {
// (版本 id, 期望徽标) —— id 形状取自 `host::game::loader_from_id` 的三条真实输入
let cases = [
("1.20.1-forge", LoaderKind::Forge, "Forge"),
("1.20.1-neoforge", LoaderKind::NeoForge, "NeoForge"),
("1.20.1-OptiFine", LoaderKind::OptiFine, "OptiFine"),
];
for (id, kind, brand) in cases {
let mut one = instance(InstanceRuntime::Idle);
one.loader = LoaderInfo {
kind,
// `loader_from_id` 对这三条 id 取不到版本段 ⇒ 空串
version: String::new(),
};
let mut state = AppState::new();
state.apply(unlucky_presenter::dto::AppEvent::Core(
unlucky_presenter::dto::CoreEvent::InstancesChanged(vec![one]),
));
state.navigate(Route::Detail("survival".to_string()));
// 品牌名两套语言都不翻译 ⇒ 中英逐字相同
for lang in [Lang::En, Lang::ZhCn] {
let model = at_lang(&state, None, lang);
assert_eq!(
model.instances.rows[0].supporting,
format!("1.20.4 · {brand}"),
"{id} 的徽标被显示成了别的东西(新增的判据「版本号为空 ⇒ 原版」就是这条要拦的)"
);
let detail = model.instances.detail.expect("有详情");
assert_eq!(
detail.meta[1].label,
lang.catalog().text(Key::InstancesMetaLoader)
);
assert_eq!(detail.meta[1].value, brand, "{id} 的 LOADER 卡值");
}
}
}
#[test]
fn the_agent_route_has_no_page() {
let mut state = AppState::new();