Refactor ProxyWarden routing and settings flow

This commit is contained in:
2026-07-09 11:51:16 +03:00
parent db0c1dede9
commit 1bb795a532
18 changed files with 1018 additions and 210 deletions

View File

@@ -159,7 +159,7 @@ fn reads_percent_encoded_singbox_tags_as_utf8() {
}
#[test]
fn invalid_subscription_cache_falls_back_to_none() {
fn invalid_subscription_cache_without_backup_returns_error_and_moves_corrupt_file() {
let root = test_root("invalid-subscription-cache");
let storage = JsonStorage::new(root.clone());
fs::create_dir_all(&storage.paths().state_dir).expect("create state dir");
@@ -169,27 +169,62 @@ fn invalid_subscription_cache_falls_back_to_none() {
)
.expect("write invalid cache");
assert_eq!(
storage
.read_singbox_subscription_cache()
.expect("invalid cache fallback"),
None
);
let error = storage
.read_singbox_subscription_cache()
.expect_err("invalid cache should not silently fallback");
assert_eq!(error.kind(), std::io::ErrorKind::InvalidData);
assert!(!storage.paths().singbox_subscription_cache_file.exists());
assert!(has_corrupt_sibling(
&storage.paths().singbox_subscription_cache_file
));
cleanup(&root);
}
#[test]
fn invalid_json_falls_back_to_empty_collection() {
let root = test_root("invalid-json");
fn invalid_json_without_backup_returns_error_and_moves_corrupt_file() {
let root = test_root("invalid-json-no-backup");
let storage = JsonStorage::new(root.clone());
fs::create_dir_all(&storage.paths().config_dir).expect("create config dir");
fs::write(&storage.paths().profiles_file, "{not valid json").expect("write invalid json");
let error = storage
.read_profiles()
.expect_err("invalid profiles should not silently fallback");
assert_eq!(error.kind(), std::io::ErrorKind::InvalidData);
assert!(!storage.paths().profiles_file.exists());
assert!(has_corrupt_sibling(&storage.paths().profiles_file));
cleanup(&root);
}
#[test]
fn invalid_json_recovers_from_valid_backup() {
let root = test_root("invalid-json-valid-backup");
let storage = JsonStorage::new(root.clone());
let backup_profiles = vec![sample_profile("backup")];
let current_profiles = vec![sample_profile("current")];
storage
.write_profiles(&backup_profiles)
.expect("write first profiles");
storage
.write_profiles(&current_profiles)
.expect("write second profiles");
fs::write(&storage.paths().profiles_file, "{not valid json").expect("corrupt live json");
let recovered = storage
.read_profiles()
.expect("invalid profiles should recover from valid backup");
assert_eq!(recovered, backup_profiles);
assert_eq!(
storage.read_profiles().expect("invalid profiles fallback"),
Vec::<Profile>::new()
storage.read_profiles().expect("restored live profiles"),
backup_profiles
);
assert!(has_corrupt_sibling(&storage.paths().profiles_file));
cleanup(&root);
}
@@ -279,6 +314,26 @@ fn write_json<T: serde::Serialize + ?Sized>(path: &Path, value: &T) {
fs::write(path, contents).expect("write json");
}
fn has_corrupt_sibling(path: &Path) -> bool {
let Some(parent) = path.parent() else {
return false;
};
let Some(file_name) = path.file_name().and_then(|value| value.to_str()) else {
return false;
};
let prefix = format!("{file_name}.corrupt.");
fs::read_dir(parent)
.expect("read sibling dir")
.filter_map(Result::ok)
.any(|entry| {
entry
.file_name()
.to_str()
.is_some_and(|name| name.starts_with(&prefix))
})
}
fn sample_profile(id: &str) -> Profile {
Profile {
id: id.to_string(),