Add device HWID support to sing-box subscriptions
This commit is contained in:
@@ -25,9 +25,10 @@ mod validation;
|
||||
|
||||
use commands::{
|
||||
fetch_singbox_subscription_with_fetcher, forget_singbox_subscription_in_storage,
|
||||
generate_singbox_config_with_services, save_singbox_subscription_to_storage,
|
||||
select_singbox_server_in_storage, Clock, SaveSingBoxSubscriptionInputDto,
|
||||
SelectSingBoxServerInputDto, SubscriptionFetcher,
|
||||
generate_singbox_config_with_services, save_singbox_device_hwid_to_storage,
|
||||
save_singbox_subscription_to_storage, select_singbox_server_in_storage, Clock,
|
||||
SaveSingBoxDeviceHwidInputDto, SaveSingBoxSubscriptionInputDto, SelectSingBoxServerInputDto,
|
||||
SubscriptionFetcher,
|
||||
};
|
||||
use models::{
|
||||
ActivityLevel, ComponentId, LocalSingBoxConfig, ProxyProtocol, SubscriptionCache,
|
||||
@@ -49,6 +50,7 @@ fn saves_subscription_url_without_exposing_secret_query() {
|
||||
&storage,
|
||||
SaveSingBoxSubscriptionInputDto {
|
||||
subscription_url: " https://sub.example.test/path?token=secret ".to_string(),
|
||||
device_hwid: Some(" hwid-1234567890 ".to_string()),
|
||||
},
|
||||
&FixedClock,
|
||||
)
|
||||
@@ -61,11 +63,58 @@ fn saves_subscription_url_without_exposing_secret_query() {
|
||||
config.subscription_url,
|
||||
Some("https://sub.example.test/path?token=secret".to_string())
|
||||
);
|
||||
assert_eq!(config.device_hwid, Some("hwid-1234567890".to_string()));
|
||||
assert!(status.config.has_subscription);
|
||||
assert!(status.config.has_device_hwid);
|
||||
assert_eq!(
|
||||
status.config.subscription_display_url,
|
||||
Some("https://sub.example.test/...".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
status.config.device_hwid_display,
|
||||
Some("hwid...7890".to_string())
|
||||
);
|
||||
|
||||
cleanup(&root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saves_and_clears_device_hwid_without_exposing_raw_value() {
|
||||
let root = test_root("save-hwid");
|
||||
let storage = JsonStorage::new(root.clone());
|
||||
|
||||
let status = save_singbox_device_hwid_to_storage(
|
||||
&storage,
|
||||
SaveSingBoxDeviceHwidInputDto {
|
||||
device_hwid: Some(" device-abcdef1234 ".to_string()),
|
||||
},
|
||||
&FixedClock,
|
||||
)
|
||||
.expect("device hwid should be saved");
|
||||
let config = storage
|
||||
.read_local_singbox_config()
|
||||
.expect("read local sing-box config");
|
||||
|
||||
assert_eq!(config.device_hwid, Some("device-abcdef1234".to_string()));
|
||||
assert!(status.config.has_device_hwid);
|
||||
assert_eq!(
|
||||
status.config.device_hwid_display,
|
||||
Some("devi...1234".to_string())
|
||||
);
|
||||
|
||||
let status = save_singbox_device_hwid_to_storage(
|
||||
&storage,
|
||||
SaveSingBoxDeviceHwidInputDto { device_hwid: None },
|
||||
&FixedClock,
|
||||
)
|
||||
.expect("device hwid should be cleared");
|
||||
let config = storage
|
||||
.read_local_singbox_config()
|
||||
.expect("read local sing-box config");
|
||||
|
||||
assert_eq!(config.device_hwid, None);
|
||||
assert!(!status.config.has_device_hwid);
|
||||
assert_eq!(status.config.device_hwid_display, None);
|
||||
|
||||
cleanup(&root);
|
||||
}
|
||||
@@ -79,6 +128,7 @@ fn rejects_non_http_subscription_url() {
|
||||
&storage,
|
||||
SaveSingBoxSubscriptionInputDto {
|
||||
subscription_url: "file:///C:/sub.txt".to_string(),
|
||||
device_hwid: None,
|
||||
},
|
||||
&FixedClock,
|
||||
)
|
||||
@@ -97,6 +147,7 @@ fn fetches_subscription_cache_and_selects_first_server() {
|
||||
&storage,
|
||||
SaveSingBoxSubscriptionInputDto {
|
||||
subscription_url: "https://sub.example.test/path?token=secret".to_string(),
|
||||
device_hwid: None,
|
||||
},
|
||||
&FixedClock,
|
||||
)
|
||||
@@ -127,6 +178,33 @@ fn fetches_subscription_cache_and_selects_first_server() {
|
||||
cleanup(&root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fetches_subscription_with_saved_device_hwid() {
|
||||
let root = test_root("fetch-subscription-hwid");
|
||||
let storage = JsonStorage::new(root.clone());
|
||||
save_singbox_subscription_to_storage(
|
||||
&storage,
|
||||
SaveSingBoxSubscriptionInputDto {
|
||||
subscription_url: "https://sub.example.test/path?token=secret".to_string(),
|
||||
device_hwid: Some("hwid-abcdef1234".to_string()),
|
||||
},
|
||||
&FixedClock,
|
||||
)
|
||||
.expect("save subscription URL and HWID");
|
||||
|
||||
fetch_singbox_subscription_with_fetcher(
|
||||
&storage,
|
||||
&HwidAssertingFetcher {
|
||||
cache: sample_cache(),
|
||||
expected_hwid: Some("hwid-abcdef1234"),
|
||||
},
|
||||
&FixedClock,
|
||||
)
|
||||
.expect("fetch subscription through mock");
|
||||
|
||||
cleanup(&root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selects_server_from_cached_subscription() {
|
||||
let root = test_root("select-server");
|
||||
@@ -193,6 +271,7 @@ fn generate_writes_config_and_local_singbox_target() {
|
||||
storage
|
||||
.write_local_singbox_config(&LocalSingBoxConfig {
|
||||
subscription_url: Some("https://sub.example.test/path".to_string()),
|
||||
device_hwid: Some("hwid-abcdef1234".to_string()),
|
||||
selected_server_tag: Some("nl-1".to_string()),
|
||||
..LocalSingBoxConfig::default()
|
||||
})
|
||||
@@ -259,6 +338,7 @@ fn forget_subscription_clears_url_selection_and_cache() {
|
||||
storage
|
||||
.write_local_singbox_config(&LocalSingBoxConfig {
|
||||
subscription_url: Some("https://sub.example.test/path".to_string()),
|
||||
device_hwid: Some("hwid-abcdef1234".to_string()),
|
||||
selected_server_tag: Some("nl-1".to_string()),
|
||||
..LocalSingBoxConfig::default()
|
||||
})
|
||||
@@ -278,6 +358,7 @@ fn forget_subscription_clears_url_selection_and_cache() {
|
||||
|
||||
assert!(!status.config.has_subscription);
|
||||
assert_eq!(config.subscription_url, None);
|
||||
assert_eq!(config.device_hwid, None);
|
||||
assert_eq!(config.selected_server_tag, None);
|
||||
assert_eq!(cache, None);
|
||||
|
||||
@@ -290,12 +371,34 @@ impl SubscriptionFetcher for MockFetcher {
|
||||
fn fetch_subscription(
|
||||
&self,
|
||||
url: &str,
|
||||
_identity: &subscription::SubscriptionFetchIdentity,
|
||||
) -> Result<SubscriptionCache, subscription::SubscriptionError> {
|
||||
assert_eq!(url, "https://sub.example.test/path?token=secret");
|
||||
Ok(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
struct HwidAssertingFetcher {
|
||||
cache: SubscriptionCache,
|
||||
expected_hwid: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl SubscriptionFetcher for HwidAssertingFetcher {
|
||||
fn fetch_subscription(
|
||||
&self,
|
||||
url: &str,
|
||||
identity: &subscription::SubscriptionFetchIdentity,
|
||||
) -> Result<SubscriptionCache, subscription::SubscriptionError> {
|
||||
assert_eq!(url, "https://sub.example.test/path?token=secret");
|
||||
assert_eq!(identity.device_hwid.as_deref(), self.expected_hwid);
|
||||
assert_eq!(identity.app_name, "ProxyWarden");
|
||||
assert!(identity.user_agent.starts_with("ProxyWarden/"));
|
||||
assert_eq!(identity.device_os, std::env::consts::OS);
|
||||
assert_eq!(identity.device_model, "ProxyWarden");
|
||||
Ok(self.cache.clone())
|
||||
}
|
||||
}
|
||||
|
||||
struct MockChecker;
|
||||
|
||||
impl SingBoxConfigChecker for MockChecker {
|
||||
|
||||
Reference in New Issue
Block a user