Unlucky Launcher v0.0.1-alpha
CI / fmt (rustfmt --check) (push) Canceled after 0s
CI / clippy (ubuntu-latest) (push) Canceled after 0s
CI / clippy (windows-latest) (push) Canceled after 0s
CI / test (ubuntu-latest) (push) Canceled after 0s
CI / test (windows-latest) (push) Canceled after 0s
CI / build release (ubuntu-latest) (push) Canceled after 0s
CI / build release (windows-latest) (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
CI / fmt (rustfmt --check) (push) Canceled after 0s
CI / clippy (ubuntu-latest) (push) Canceled after 0s
CI / clippy (windows-latest) (push) Canceled after 0s
CI / test (ubuntu-latest) (push) Canceled after 0s
CI / test (windows-latest) (push) Canceled after 0s
CI / build release (ubuntu-latest) (push) Canceled after 0s
CI / build release (windows-latest) (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Rust 原生单文件 Minecraft 启动器:CLI 完整可用(install/launch/accounts/java/diagnose/agent), MCP server 接入外部 Agent 工具,GUI 开发中。
This commit is contained in:
commit
bc41dfcf7d
517 files changed
+92844
No files matched your search
@@ -0,0 +1,171 @@
|
||||
//! 规则库加载 / 热重载 / 坏文件的降级行为。
|
||||
|
||||
use std::fs;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
const CUSTOM_MEMORY: &str = "\
|
||||
version: 1
|
||||
category: memory
|
||||
rules:
|
||||
- id: memory.custom_only
|
||||
severity: warning
|
||||
pattern: 'CUSTOM TEST SIGNATURE'
|
||||
confidence: 0.9
|
||||
priority: 70
|
||||
cause:
|
||||
key: diagnose.cause.memory.custom_only
|
||||
summary: '测试用自定义规则'
|
||||
fix_action:
|
||||
- kind: seek_help
|
||||
sample: samples/memory/out_of_memory.log
|
||||
";
|
||||
|
||||
#[test]
|
||||
fn embedded_library_is_complete() {
|
||||
let loader = RuleLoader::embedded();
|
||||
assert_eq!(EMBEDDED_RULES.len(), 10, "10 个根因分组各一个 YAML");
|
||||
// 64 = HMCL 59 条全量平移 + 5 条 Vulkan 组(graphics.yaml 末尾的 4 个原因码
|
||||
// + no_graphics_backend)。加规则要同时加样本并同步这个数。
|
||||
assert_eq!(loader.rules().len(), 64);
|
||||
assert!(loader.last_errors().is_empty());
|
||||
assert!(loader.dir().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_directory_falls_back_to_embedded() {
|
||||
let dir = temp_dir("loader-missing").join("nope");
|
||||
let loader = RuleLoader::from_dir(&dir);
|
||||
assert_eq!(loader.rules().len(), 64);
|
||||
assert!(loader.last_errors().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disk_file_replaces_embedded_group() {
|
||||
let dir = temp_dir("loader-override");
|
||||
fs::write(dir.join("memory.yaml"), CUSTOM_MEMORY).expect("写规则文件");
|
||||
let loader = RuleLoader::from_dir(&dir);
|
||||
|
||||
assert_eq!(loader.rules().len(), 63, "memory.yaml 的 2 条被 1 条替换");
|
||||
assert!(loader.last_errors().is_empty());
|
||||
assert!(loader.rule_set().get("memory.custom_only").is_some());
|
||||
assert!(
|
||||
loader.rule_set().get("memory.out_of_memory").is_none(),
|
||||
"同组文件被整份替换"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn refresh_picks_up_edits() {
|
||||
let dir = temp_dir("loader-refresh");
|
||||
let mut loader = RuleLoader::from_dir(&dir);
|
||||
assert_eq!(
|
||||
loader.refresh(),
|
||||
ReloadOutcome::Unchanged,
|
||||
"没变就不该重解析"
|
||||
);
|
||||
|
||||
fs::write(dir.join("memory.yaml"), CUSTOM_MEMORY).expect("写规则文件");
|
||||
assert_eq!(
|
||||
loader.refresh(),
|
||||
ReloadOutcome::Reloaded { rules: 63 },
|
||||
"指纹变了应重新加载"
|
||||
);
|
||||
assert!(loader.rule_set().get("memory.custom_only").is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn broken_yaml_keeps_last_good_rules() {
|
||||
let dir = temp_dir("loader-broken");
|
||||
fs::write(dir.join("memory.yaml"), CUSTOM_MEMORY).expect("写规则文件");
|
||||
let mut loader = RuleLoader::from_dir(&dir);
|
||||
assert_eq!(loader.rules().len(), 63);
|
||||
|
||||
// 用户手改坏了 YAML:保留上一份好规则 + 报错,而不是让诊断能力归零
|
||||
fs::write(
|
||||
dir.join("memory.yaml"),
|
||||
"version: 1\ncategory: memory\nrules: [ oops\n",
|
||||
)
|
||||
.expect("写坏文件");
|
||||
match loader.refresh() {
|
||||
ReloadOutcome::Rejected { errors } => {
|
||||
assert!(!errors.is_empty());
|
||||
assert_eq!(
|
||||
errors[0].kind,
|
||||
unlucky_core::diagnose::loader::LoadErrorKind::Yaml
|
||||
);
|
||||
}
|
||||
other => panic!("应被拒绝:{other:?}"),
|
||||
}
|
||||
assert_eq!(loader.rules().len(), 63, "坏文件不该清空规则库");
|
||||
assert!(loader.rule_set().get("memory.custom_only").is_some());
|
||||
assert!(!loader.last_errors().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_rule_id_across_files_is_rejected() {
|
||||
let dir = temp_dir("loader-duplicate");
|
||||
let duplicated = "\
|
||||
version: 1
|
||||
category: other
|
||||
rules:
|
||||
- id: memory.out_of_memory
|
||||
pattern: 'dup'
|
||||
cause:
|
||||
key: diagnose.cause.dup
|
||||
summary: '重复 id'
|
||||
";
|
||||
fs::write(dir.join("other.yaml"), duplicated).expect("写规则文件");
|
||||
let loader = RuleLoader::from_dir(&dir);
|
||||
|
||||
assert_eq!(loader.rules().len(), 64, "整体失败后回退内嵌规则集");
|
||||
assert!(loader
|
||||
.last_errors()
|
||||
.iter()
|
||||
.any(|error| error.kind == unlucky_core::diagnose::loader::LoadErrorKind::DuplicateId));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_field_is_rejected() {
|
||||
let dir = temp_dir("loader-typo");
|
||||
let typo = "\
|
||||
version: 1
|
||||
category: other
|
||||
rules:
|
||||
- id: other.typo
|
||||
patternn: 'typo in field name'
|
||||
cause:
|
||||
key: diagnose.cause.typo
|
||||
summary: '字段拼错'
|
||||
";
|
||||
fs::write(dir.join("other.yaml"), typo).expect("写规则文件");
|
||||
let loader = RuleLoader::from_dir(&dir);
|
||||
assert_eq!(loader.rules().len(), 64);
|
||||
assert!(
|
||||
!loader.last_errors().is_empty(),
|
||||
"deny_unknown_fields 应把拼错的字段报出来"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rule_set_rejects_invalid_regex() {
|
||||
let sources = vec![(
|
||||
"bad.yaml".to_owned(),
|
||||
"\
|
||||
version: 1
|
||||
category: other
|
||||
rules:
|
||||
- id: other.bad_regex
|
||||
pattern: '(unclosed'
|
||||
cause:
|
||||
key: diagnose.cause.bad
|
||||
summary: '坏正则'
|
||||
"
|
||||
.to_owned(),
|
||||
)];
|
||||
let errors = RuleSet::from_sources(&sources).expect_err("坏正则应失败");
|
||||
assert_eq!(
|
||||
errors[0].kind,
|
||||
unlucky_core::diagnose::loader::LoadErrorKind::Regex
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user