fix(m161): 收更新键/按钮/矮窗口/启动错误/导入结果五笔债
① update.auto_check / update.channel 登记进 core setting schema(1.6.0)
- 新增 setting/keys/update.rs(两键 + UPDATE_CHANNELS,只允许 stable);
SettingSchema 加 with_all 供域键表批量登记。
- 新增 setting/update_prefs.rs:cache/update/prefs.json → launcher.json 的一次性迁移。
四条硬约束:源指纹闸门(幂等)、不丢 false、新值胜出、旧文件改名 .migrated 后不再被读;
主配置不可写时不动旧文件。收据 config/update-prefs-migration.json。
- presenter Runtime 建会话时迁一次并把结局留在 migration();prefs 字段换成
auto_check + 每次派发从主配置读 channel(不再硬编码 Channel::Stable)。
保留旧字段 auto_check 的真实来源(原有测试 cases 会断言)。
- CLI config get/set/dump 先跑同一次迁移(幂等),输出里给 migration 段与收据路径。
② presenter/dto/events.rs 的「本次会话不再弹提示条」注释改成真实语义
(记的是版本号:同版本压住、更高版本照弹、手动检查清掉)。
③ 检查/下载进行中按钮禁用:pages/update/busy.rs 的 busy/can_check/can_apply;
设置页两颗按钮改画禁用(不是消失),app 层 on_update_action 再挡一道
(忙碌时 CheckNow/Apply 不派发);新增两条用例(含「忙完恢回可点」)。
④ 内容库低高度:content_layout::MIN_BODY_HEIGHT=460 + BodyMode(按固定件+三整行算出来,
用例守着算式);frame::body_height 从 Ui::metrics 算正文可用高,pages::Model 带 window_height。
≥460 列表自己滚;<460 主列整列一个滚动条、列表按自然高度铺开。
⑤ 启动错误文案本地化:presenter 删掉 launch_summary / 「启动失败:」前缀 / 五条中文阶段前缀,
改发 LaunchFailureView{stage,detail};LaunchFinished 改带 exit_code/uptime_ms/failure,
Toast 由 app 层按 i18n key 现求(与 install_cancelled 同构)。InstancesFailed 拆成
InstancesFailedStage(列表单行)+ LaunchStageFailed(详情/Toast 全文)。
⑥ 导入结果块:ImportReportView 加 failure_stage(NotStarted/RolledBack/RollbackIncomplete,
由 core 的 rollback 机器位推出);标题、图标、底色三处分档,预检失败不再说「已回滚」。
顺带:六处注释/文档漂移(update.rs 的分工表与偏好落点、pack 的「已整包回滚」、
build_request 的 Errors、settings/update 的按钮禁用说明等),
以及为守 400 行把 update/cli-config/core-keys/presenter-dto/presenter-launch/i18n-golden
里贴线或越线的文件按模块拆开(纯搬移,行为不变)。
This commit is contained in:
1 parent
b2d9f3927f
commit
03741ecd2d
69 files changed
+2653
-707
No files matched your search
@@ -81,21 +81,32 @@ fn dismissing_without_a_candidate_does_nothing() {
|
||||
}
|
||||
|
||||
/// 关掉「启动时检查」之后:状态上是 no-op、启动路径上根本不派发。
|
||||
///
|
||||
/// 开关的落点是**主配置**(`update.auto_check`),这里顺带把 M161 的迁移走一遍:
|
||||
/// 旧 `cache/update/prefs.json` 里的 `false` 必须是新会话看到的 `false`(不是默认的 `true`)。
|
||||
#[test]
|
||||
fn auto_check_off_means_no_request_on_start() {
|
||||
let tmp = TempDir::new("autocheck");
|
||||
let env = HostEnv::at_root(tmp.join("root"));
|
||||
let prefs_path = env.layout.cache().join("update").join("prefs.json");
|
||||
Prefs { auto_check: false }
|
||||
.save(&prefs_path)
|
||||
.expect("写偏好");
|
||||
std::fs::create_dir_all(prefs_path.parent().expect("父目录")).expect("建 cache");
|
||||
std::fs::write(&prefs_path, r#"{"auto_check": false}"#).expect("写旧偏好");
|
||||
|
||||
let runtime = Runtime::with_factory(&env, factory(&tmp));
|
||||
assert!(!runtime.view().auto_check, "开关要读进快照");
|
||||
assert!(!runtime.auto_check(), "关掉的值一路迁进主配置");
|
||||
assert!(
|
||||
!runtime.should_check_on_start(),
|
||||
"关掉开关后启动不许派发检查"
|
||||
);
|
||||
// 迁移确实发生了:旧文件退役 + 收据落盘(收据可查)。
|
||||
assert!(!prefs_path.exists(), "旧文件必须退役");
|
||||
let receipt = runtime
|
||||
.migration()
|
||||
.receipt()
|
||||
.expect("收据路径")
|
||||
.to_path_buf();
|
||||
assert!(receipt.exists(), "收据要落盘:{}", receipt.display());
|
||||
|
||||
// 结构性判据:`refresh_all` 里那次静默检查必须挂在 `should_check_on_start` 上,
|
||||
// 否则单测绿着而启动仍然联网(这条读源码,理由同 `instances/tests.rs` 的源码断言)。
|
||||
@@ -106,18 +117,30 @@ fn auto_check_off_means_no_request_on_start() {
|
||||
);
|
||||
}
|
||||
|
||||
/// 偏好能落盘、能读回(换一份 Runtime 仍记得上次的选择)。
|
||||
/// 开关能写进主配置、能读回(换一份 Runtime 仍记得上次的选择)。
|
||||
#[test]
|
||||
fn the_auto_check_preference_survives_a_restart() {
|
||||
let tmp = TempDir::new("prefs");
|
||||
let env = HostEnv::at_root(tmp.join("root"));
|
||||
let mut runtime = Runtime::with_factory(&env, factory(&tmp));
|
||||
assert!(runtime.prefs().auto_check, "缺省开");
|
||||
assert!(runtime.auto_check(), "缺省开");
|
||||
|
||||
runtime.set_auto_check(false).expect("落盘");
|
||||
let restarted = Runtime::with_factory(&env, factory(&tmp));
|
||||
assert!(!restarted.prefs().auto_check, "重启后仍记得关掉");
|
||||
assert!(!restarted.auto_check(), "重启后仍记得关掉");
|
||||
assert!(!restarted.view().auto_check);
|
||||
|
||||
// 落点是主配置的 schema 键(CLI 读到的就是这一份),不是更新域自己的文件。
|
||||
let text = std::fs::read_to_string(env.launcher_config()).expect("读主配置");
|
||||
assert!(text.contains("\"auto_check\": false"), "{text}");
|
||||
assert!(
|
||||
!env.layout
|
||||
.cache()
|
||||
.join("update")
|
||||
.join("prefs.json")
|
||||
.exists(),
|
||||
"新写入不许再落到旧文件"
|
||||
);
|
||||
}
|
||||
|
||||
/// GUI 与 CLI 共用**同一个** `Job::apply`(mission:不许两套逻辑)。
|
||||
@@ -145,7 +168,33 @@ fn the_factory_names_its_source_and_target() {
|
||||
let factory = factory(&tmp);
|
||||
assert_eq!(factory.dir(), Some(tmp.join("dist").as_path()));
|
||||
assert_eq!(factory.dest(), tmp.join("bin/unlucky-launcher.exe"));
|
||||
assert_eq!(CHANNEL.as_str(), "stable", "D50:先只发 stable");
|
||||
}
|
||||
|
||||
/// 渠道来自主配置的 `update.channel`(M161:不再是编译期常量)。
|
||||
///
|
||||
/// 抓得住:把渠道写回硬编码常量而键里是别的值;也抓得住「认不出的渠道 panic 而不是回出厂值」。
|
||||
#[test]
|
||||
fn the_channel_is_read_from_the_schema_key() {
|
||||
let tmp = TempDir::new("channel-key");
|
||||
let env = HostEnv::at_root(tmp.join("root"));
|
||||
let runtime = Runtime::with_factory(&env, factory(&tmp));
|
||||
assert_eq!(
|
||||
runtime.channel().as_str(),
|
||||
unlucky_core::setting::update_prefs::DEFAULT_CHANNEL,
|
||||
"缺键 ⇒ 出厂渠道"
|
||||
);
|
||||
assert_eq!(runtime.view().channel, "stable", "快照里的渠道同源");
|
||||
|
||||
// 手改成一个 schema 不允许的值(越过 `config set` 的校验)⇒ 回出厂渠道,不许 panic。
|
||||
std::fs::create_dir_all(env.layout.config()).expect("建 config");
|
||||
std::fs::write(
|
||||
env.launcher_config(),
|
||||
r#"{"$schema":"https://schemas.unlucky-launcher.dev/ull/launcher/1.6.0","schemaVersion":1,
|
||||
"update":{"channel":"hand-edited"}}"#,
|
||||
)
|
||||
.expect("写配置");
|
||||
let runtime = Runtime::with_factory(&env, factory(&tmp));
|
||||
assert_eq!(runtime.channel().as_str(), "stable", "认不出的渠道回出厂值");
|
||||
}
|
||||
|
||||
/// CLI 与 GUI 用的是**同一个** core Job:源码锚点,防止有人另起一条流程。
|
||||
|
||||
Reference in new issue
Block a user