mirror of
https://github.com/nimbold/Firelink.git
synced 2026-09-09 17:25:42 +00:00
fix(network): harden native proxy discovery and FFmpeg provider matching
- discover active macOS dynamic store proxies via scutil before networksetup - treat missing registry keys and unavailable probe tools as clean absence instead of hard error - support two-part versions and tag-exact asset patterns in BtbN FFmpeg updates - normalize BtbN n-prefixed FFmpeg versions in sidecar status checks
This commit is contained in:
@@ -177,14 +177,14 @@ function escapeRegExp(value) {
|
||||
}
|
||||
|
||||
async function latestBtbnFfmpegStableBuild(stableVersion) {
|
||||
if (!/^\d+\.\d+\.\d+$/.test(stableVersion)) {
|
||||
if (!/^\d+\.\d+(?:\.\d+)?$/.test(stableVersion)) {
|
||||
throw new Error(`unsupported FFmpeg stable version: ${stableVersion}`);
|
||||
}
|
||||
const stableSeries = stableVersion.split('.').slice(0, 2).join('.');
|
||||
const versionPattern = escapeRegExp(stableVersion);
|
||||
const seriesPattern = escapeRegExp(stableSeries);
|
||||
const assetPattern = new RegExp(
|
||||
`^ffmpeg-n(${versionPattern}-\\d+-g[0-9a-f]+)-(win64|linux64)-gpl-${seriesPattern}\\.(?:zip|tar\\.xz)$`
|
||||
`^ffmpeg-n(${versionPattern}(?:-\\d+-g[0-9a-f]+)?)-(win64|linux64)-gpl-${seriesPattern}\\.(?:zip|tar\\.xz)$`
|
||||
);
|
||||
const releases = await fetchJson('https://api.github.com/repos/BtbN/FFmpeg-Builds/releases?per_page=10');
|
||||
if (!Array.isArray(releases)) throw new Error('BtbN releases response is not an array');
|
||||
@@ -449,7 +449,12 @@ async function main() {
|
||||
async () => {
|
||||
const build = await latestMartinRiedlMacArm64Release();
|
||||
const stableVersion = await ffmpegStablePromise;
|
||||
if (!build?.version || !build.url || !build.sha256 || build.version !== stableVersion) {
|
||||
if (
|
||||
!build?.version ||
|
||||
!build.url ||
|
||||
!build.sha256 ||
|
||||
compareVersions(build.version, stableVersion) !== 0
|
||||
) {
|
||||
throw new Error('Martin Riedl FFmpeg provider response has no complete matching macOS arm64 stable build');
|
||||
}
|
||||
return build;
|
||||
|
||||
@@ -179,6 +179,33 @@ test('selects a complete BtbN build for the current stable series', async () =>
|
||||
assert.equal(result.hashes.linux, 'b'.repeat(64));
|
||||
});
|
||||
|
||||
test('selects a complete BtbN build for a two-part stable version and tag-exact assets', async () => {
|
||||
const digest = value => `sha256:${value.repeat(64)}`;
|
||||
const release = {
|
||||
tag_name: 'autobuild-test',
|
||||
assets: [
|
||||
{
|
||||
name: 'ffmpeg-n9.0-win64-gpl-9.0.zip',
|
||||
browser_download_url: 'https://example.test/windows-exact.zip',
|
||||
digest: digest('e'),
|
||||
},
|
||||
{
|
||||
name: 'ffmpeg-n9.0-linux64-gpl-9.0.tar.xz',
|
||||
browser_download_url: 'https://example.test/linux-exact.tar.xz',
|
||||
digest: digest('f'),
|
||||
},
|
||||
],
|
||||
};
|
||||
const result = await withMockFetch(
|
||||
async () => new Response(JSON.stringify([release]), { status: 200 }),
|
||||
() => latestBtbnFfmpegStableBuild('9.0'),
|
||||
);
|
||||
|
||||
assert.equal(result.version, '9.0');
|
||||
assert.equal(result.urls.windows, 'https://example.test/windows-exact.zip');
|
||||
assert.equal(result.hashes.linux, 'f'.repeat(64));
|
||||
});
|
||||
|
||||
test('rejects an incomplete BtbN stable target tuple', async () => {
|
||||
const release = {
|
||||
tag_name: 'autobuild-test',
|
||||
|
||||
@@ -4616,6 +4616,14 @@ fn parse_ffmpeg_version(output: &str) -> Option<String> {
|
||||
without_url
|
||||
.split('-')
|
||||
.next()
|
||||
.map(|version| {
|
||||
if let Some(rest) = version.strip_prefix(['n', 'N']) {
|
||||
if rest.chars().next().is_some_and(|c| c.is_ascii_digit()) {
|
||||
return rest;
|
||||
}
|
||||
}
|
||||
version
|
||||
})
|
||||
.filter(|version| !version.trim().is_empty())
|
||||
.map(str::to_string)
|
||||
}
|
||||
@@ -17932,6 +17940,13 @@ mod tests {
|
||||
assert_eq!(parse_ffmpeg_version(output), Some("9.0.1".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_btbn_ffmpeg_prefixed_version() {
|
||||
let output = "ffmpeg version n9.0.1-11-ge47273f4d9 Copyright (c) 2000-2026 the FFmpeg developers";
|
||||
|
||||
assert_eq!(parse_ffmpeg_version(output), Some("9.0.1".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uses_fragment_progress_instead_of_temporary_hls_size_estimates() {
|
||||
let line = format!(
|
||||
|
||||
+298
-60
@@ -100,22 +100,7 @@ async fn native_system_proxy(runner: &dyn ProxyCommandRunner) -> Result<Option<S
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
async fn native_system_proxy(runner: &dyn ProxyCommandRunner) -> Result<Option<String>, String> {
|
||||
let mode = runner
|
||||
.stdout(
|
||||
"gsettings",
|
||||
&string_args(&["get", "org.gnome.system.proxy", "mode"]),
|
||||
)
|
||||
.await?;
|
||||
if strip_gsettings_string(&mode) != "manual" {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
for (service, scheme) in [("https", "http"), ("http", "http"), ("socks", "socks5")] {
|
||||
if let Some(proxy) = linux_gsettings_proxy(runner, service, scheme).await {
|
||||
return Ok(Some(proxy));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
linux_system_proxy(runner).await
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
|
||||
@@ -127,20 +112,28 @@ fn string_args(args: &[&str]) -> Vec<String> {
|
||||
args.iter().map(|value| (*value).to_string()).collect()
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
all(not(target_os = "windows"), not(target_os = "linux"), not(target_os = "macos")),
|
||||
allow(dead_code)
|
||||
)]
|
||||
fn is_probe_unavailable(error: &str) -> bool {
|
||||
error.contains("is unavailable") || error.contains("exited unsuccessfully")
|
||||
}
|
||||
|
||||
fn proxy_from_environment() -> Option<String> {
|
||||
[
|
||||
"HTTPS_PROXY",
|
||||
"https_proxy",
|
||||
"HTTP_PROXY",
|
||||
"http_proxy",
|
||||
"ALL_PROXY",
|
||||
"all_proxy",
|
||||
("HTTPS_PROXY", "http"),
|
||||
("https_proxy", "http"),
|
||||
("HTTP_PROXY", "http"),
|
||||
("http_proxy", "http"),
|
||||
("ALL_PROXY", "socks5"),
|
||||
("all_proxy", "socks5"),
|
||||
]
|
||||
.into_iter()
|
||||
.find_map(|name| {
|
||||
.find_map(|(name, scheme)| {
|
||||
std::env::var(name)
|
||||
.ok()
|
||||
.and_then(|value| normalize_proxy_address(&value, "http"))
|
||||
.and_then(|value| normalize_proxy_address(&value, scheme))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -180,7 +173,8 @@ fn proxy_from_host_port(host: &str, port: &str, scheme: &str) -> Option<String>
|
||||
};
|
||||
let port = port.trim().parse::<u16>().ok().filter(|port| *port != 0)?;
|
||||
if host.is_empty()
|
||||
|| host.contains(['/', '@', '?', '#'])
|
||||
|| host.eq_ignore_ascii_case("(null)")
|
||||
|| host.contains(['/', '@', '?', '#', '(', ')'])
|
||||
|| host.chars().any(char::is_whitespace)
|
||||
{
|
||||
return None;
|
||||
@@ -224,13 +218,64 @@ fn parse_windows_proxy_server(value: &str) -> Option<String> {
|
||||
https.or(http).or(socks)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg_attr(not(target_os = "macos"), allow(dead_code))]
|
||||
fn scutil_dict_value<'a>(output: &'a str, key: &str) -> Option<&'a str> {
|
||||
for line in output.lines() {
|
||||
let trimmed = line.trim();
|
||||
if let Some((k, v)) = trimmed.split_once(':') {
|
||||
if k.trim().eq_ignore_ascii_case(key) {
|
||||
let val = v.trim();
|
||||
if !val.is_empty() {
|
||||
return Some(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg_attr(not(target_os = "macos"), allow(dead_code))]
|
||||
fn parse_macos_scutil_proxy(output: &str) -> Option<String> {
|
||||
for (enable_key, proxy_key, port_key, scheme) in [
|
||||
("HTTPSEnable", "HTTPSProxy", "HTTPSPort", "http"),
|
||||
("HTTPEnable", "HTTPProxy", "HTTPPort", "http"),
|
||||
("SOCKSEnable", "SOCKSProxy", "SOCKSPort", "socks5"),
|
||||
] {
|
||||
if scutil_dict_value(output, enable_key) == Some("1") {
|
||||
if let (Some(server), Some(port)) = (
|
||||
scutil_dict_value(output, proxy_key),
|
||||
scutil_dict_value(output, port_key),
|
||||
) {
|
||||
if let Some(proxy) = proxy_from_host_port(server, port, scheme) {
|
||||
return Some(proxy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg_attr(not(target_os = "macos"), allow(dead_code))]
|
||||
async fn macos_system_proxy(runner: &dyn ProxyCommandRunner) -> Result<Option<String>, String> {
|
||||
let services_output = runner
|
||||
if let Ok(scutil_output) = runner.stdout("scutil", &string_args(&["--proxy"])).await {
|
||||
if let Some(proxy) = parse_macos_scutil_proxy(&scutil_output) {
|
||||
return Ok(Some(proxy));
|
||||
}
|
||||
}
|
||||
|
||||
let services_output = match runner
|
||||
.stdout("networksetup", &string_args(&["-listallnetworkservices"]))
|
||||
.await?;
|
||||
.await
|
||||
{
|
||||
Ok(output) => output,
|
||||
Err(error) => {
|
||||
if is_probe_unavailable(&error) {
|
||||
return Ok(None);
|
||||
}
|
||||
return Err(error);
|
||||
}
|
||||
};
|
||||
let services = parse_macos_network_services(&services_output)?;
|
||||
let mut successful_probe = false;
|
||||
for (target, scheme) in [
|
||||
("securewebproxy", "http"),
|
||||
("webproxy", "http"),
|
||||
@@ -239,18 +284,13 @@ async fn macos_system_proxy(runner: &dyn ProxyCommandRunner) -> Result<Option<St
|
||||
for service in &services {
|
||||
let args = vec![format!("-get{target}"), service.clone()];
|
||||
if let Ok(output) = runner.stdout("networksetup", &args).await {
|
||||
successful_probe = true;
|
||||
if let Some(proxy) = parse_macos_networksetup_proxy(&output, scheme) {
|
||||
return Ok(Some(proxy));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !services.is_empty() && !successful_probe {
|
||||
Err("failed to query enabled macOS proxy settings".to_string())
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[cfg_attr(not(target_os = "macos"), allow(dead_code))]
|
||||
@@ -290,7 +330,36 @@ fn macos_networksetup_value<'a>(output: &'a str, key: &str) -> Option<&'a str> {
|
||||
.filter(|value| !value.is_empty())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
|
||||
async fn linux_system_proxy(runner: &dyn ProxyCommandRunner) -> Result<Option<String>, String> {
|
||||
let mode = match runner
|
||||
.stdout(
|
||||
"gsettings",
|
||||
&string_args(&["get", "org.gnome.system.proxy", "mode"]),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(mode) => mode,
|
||||
Err(error) => {
|
||||
if is_probe_unavailable(&error) {
|
||||
return Ok(None);
|
||||
}
|
||||
return Err(error);
|
||||
}
|
||||
};
|
||||
if strip_gsettings_string(&mode) != "manual" {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
for (service, scheme) in [("https", "http"), ("http", "http"), ("socks", "socks5")] {
|
||||
if let Some(proxy) = linux_gsettings_proxy(runner, service, scheme).await {
|
||||
return Ok(Some(proxy));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
|
||||
async fn linux_gsettings_proxy(
|
||||
runner: &dyn ProxyCommandRunner,
|
||||
service: &str,
|
||||
@@ -322,9 +391,9 @@ fn strip_gsettings_string(value: &str) -> String {
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
|
||||
async fn windows_system_proxy(runner: &dyn ProxyCommandRunner) -> Result<Option<String>, String> {
|
||||
let output = runner
|
||||
let output = match runner
|
||||
.stdout(
|
||||
"reg",
|
||||
&string_args(&[
|
||||
@@ -334,7 +403,16 @@ async fn windows_system_proxy(runner: &dyn ProxyCommandRunner) -> Result<Option<
|
||||
"ProxyEnable",
|
||||
]),
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
{
|
||||
Ok(output) => output,
|
||||
Err(error) => {
|
||||
if is_probe_unavailable(&error) {
|
||||
return Ok(None);
|
||||
}
|
||||
return Err(error);
|
||||
}
|
||||
};
|
||||
let enabled = registry_value(&output, "ProxyEnable")
|
||||
.as_deref()
|
||||
.is_some_and(windows_proxy_enabled);
|
||||
@@ -342,7 +420,7 @@ async fn windows_system_proxy(runner: &dyn ProxyCommandRunner) -> Result<Option<
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let output = runner
|
||||
let output = match runner
|
||||
.stdout(
|
||||
"reg",
|
||||
&string_args(&[
|
||||
@@ -352,7 +430,16 @@ async fn windows_system_proxy(runner: &dyn ProxyCommandRunner) -> Result<Option<
|
||||
"ProxyServer",
|
||||
]),
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
{
|
||||
Ok(output) => output,
|
||||
Err(error) => {
|
||||
if is_probe_unavailable(&error) {
|
||||
return Ok(None);
|
||||
}
|
||||
return Err(error);
|
||||
}
|
||||
};
|
||||
Ok(registry_value(&output, "ProxyServer").and_then(|value| parse_windows_proxy_server(&value)))
|
||||
}
|
||||
|
||||
@@ -395,9 +482,9 @@ fn registry_value(output: &str, name: &str) -> Option<String> {
|
||||
mod proxy_tests {
|
||||
use super::{
|
||||
bounded_native_system_proxy, normalize_proxy_address, parse_macos_network_services,
|
||||
parse_macos_networksetup_proxy, parse_windows_proxy_server, proxy_from_host_port,
|
||||
registry_value, strip_gsettings_string, windows_proxy_enabled, ProxyCommandRunner,
|
||||
SystemProxyCommandRunner,
|
||||
parse_macos_networksetup_proxy, parse_macos_scutil_proxy, parse_windows_proxy_server,
|
||||
proxy_from_host_port, registry_value, strip_gsettings_string, windows_proxy_enabled,
|
||||
ProxyCommandRunner, SystemProxyCommandRunner,
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -407,32 +494,57 @@ mod proxy_tests {
|
||||
#[cfg(target_os = "macos")]
|
||||
struct SlowProxyCommandRunner;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
struct ScutilProxyCommandRunner;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[async_trait::async_trait]
|
||||
impl ProxyCommandRunner for MockProxyCommandRunner {
|
||||
async fn stdout(&self, program: &str, args: &[String]) -> Result<String, String> {
|
||||
assert_eq!(program, "networksetup");
|
||||
match args
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice()
|
||||
{
|
||||
["-listallnetworkservices"] => Ok("Wi-Fi\nEthernet\n".to_string()),
|
||||
["-getsecurewebproxy", "Wi-Fi"] => {
|
||||
Ok("Enabled: No\nServer: ignored.example\nPort: 443\n".to_string())
|
||||
}
|
||||
["-getsecurewebproxy", "Ethernet"] => {
|
||||
Ok("Enabled: Yes\nServer: secure.example\nPort: 8443\n".to_string())
|
||||
}
|
||||
["-getwebproxy", _] | ["-getsocksfirewallproxy", _] => {
|
||||
panic!("lower-priority proxy was queried after HTTPS succeeded")
|
||||
match program {
|
||||
"scutil" => {
|
||||
assert_eq!(args, &["--proxy"]);
|
||||
Ok("<dictionary> {\n HTTPEnable : 0\n HTTPSEnable : 0\n}\n".to_string())
|
||||
}
|
||||
"networksetup" => match args
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice()
|
||||
{
|
||||
["-listallnetworkservices"] => Ok("Wi-Fi\nEthernet\n".to_string()),
|
||||
["-getsecurewebproxy", "Wi-Fi"] => {
|
||||
Ok("Enabled: No\nServer: ignored.example\nPort: 443\n".to_string())
|
||||
}
|
||||
["-getsecurewebproxy", "Ethernet"] => {
|
||||
Ok("Enabled: Yes\nServer: secure.example\nPort: 8443\n".to_string())
|
||||
}
|
||||
["-getwebproxy", _] | ["-getsocksfirewallproxy", _] => {
|
||||
panic!("lower-priority proxy was queried after HTTPS succeeded")
|
||||
}
|
||||
_ => Err("unexpected command".to_string()),
|
||||
},
|
||||
_ => Err("unexpected command".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[async_trait::async_trait]
|
||||
impl ProxyCommandRunner for ScutilProxyCommandRunner {
|
||||
async fn stdout(&self, program: &str, args: &[String]) -> Result<String, String> {
|
||||
assert_eq!(program, "scutil");
|
||||
assert_eq!(args, &["--proxy"]);
|
||||
Ok(r#"<dictionary> {
|
||||
HTTPSEnable : 1
|
||||
HTTPSPort : 8443
|
||||
HTTPSProxy : scutil.example
|
||||
}
|
||||
"#
|
||||
.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[async_trait::async_trait]
|
||||
impl ProxyCommandRunner for SlowProxyCommandRunner {
|
||||
@@ -594,6 +706,132 @@ HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings
|
||||
assert!(!windows_proxy_enabled("0X0"));
|
||||
assert!(windows_proxy_enabled("0X1"));
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[tokio::test]
|
||||
async fn selects_active_scutil_proxy_before_networksetup() {
|
||||
assert_eq!(
|
||||
super::macos_system_proxy(&ScutilProxyCommandRunner)
|
||||
.await
|
||||
.unwrap()
|
||||
.as_deref(),
|
||||
Some("http://scutil.example:8443")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_macos_scutil_proxy_outputs() {
|
||||
let scutil_https = r#"<dictionary> {
|
||||
HTTPEnable : 1
|
||||
HTTPPort : 8080
|
||||
HTTPProxy : 127.0.0.1
|
||||
HTTPSEnable : 1
|
||||
HTTPSPort : 8443
|
||||
HTTPSProxy : secure.local
|
||||
}
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse_macos_scutil_proxy(scutil_https).as_deref(),
|
||||
Some("http://secure.local:8443")
|
||||
);
|
||||
|
||||
let scutil_socks = r#"<dictionary> {
|
||||
HTTPEnable : 0
|
||||
HTTPSEnable : 0
|
||||
SOCKSEnable : 1
|
||||
SOCKSPort : 1080
|
||||
SOCKSProxy : 127.0.0.1
|
||||
}
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse_macos_scutil_proxy(scutil_socks).as_deref(),
|
||||
Some("socks5://127.0.0.1:1080")
|
||||
);
|
||||
|
||||
let scutil_ipv6 = r#"<dictionary> {
|
||||
HTTPEnable : 1
|
||||
HTTPPort : 8080
|
||||
HTTPProxy : 2001:db8::1
|
||||
}
|
||||
"#;
|
||||
assert_eq!(
|
||||
parse_macos_scutil_proxy(scutil_ipv6).as_deref(),
|
||||
Some("http://[2001:db8::1]:8080")
|
||||
);
|
||||
|
||||
let scutil_disabled = r#"<dictionary> {
|
||||
HTTPEnable : 0
|
||||
HTTPSEnable : 0
|
||||
SOCKSEnable : 0
|
||||
}
|
||||
"#;
|
||||
assert_eq!(parse_macos_scutil_proxy(scutil_disabled), None);
|
||||
}
|
||||
|
||||
struct MockWindowsMissingRegRunner;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ProxyCommandRunner for MockWindowsMissingRegRunner {
|
||||
async fn stdout(&self, _program: &str, _args: &[String]) -> Result<String, String> {
|
||||
Err("reg exited unsuccessfully".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
struct MockWindowsEnabledRegRunner;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ProxyCommandRunner for MockWindowsEnabledRegRunner {
|
||||
async fn stdout(&self, program: &str, args: &[String]) -> Result<String, String> {
|
||||
assert_eq!(program, "reg");
|
||||
if args.iter().any(|a| a == "ProxyEnable") {
|
||||
Ok(" ProxyEnable REG_DWORD 0x1\n".to_string())
|
||||
} else if args.iter().any(|a| a == "ProxyServer") {
|
||||
Ok(" ProxyServer REG_SZ 127.0.0.1:8080\n".to_string())
|
||||
} else {
|
||||
Err("reg exited unsuccessfully".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn windows_system_proxy_returns_none_when_value_is_missing() {
|
||||
assert_eq!(
|
||||
super::windows_system_proxy(&MockWindowsMissingRegRunner)
|
||||
.await
|
||||
.unwrap(),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn windows_system_proxy_returns_configured_proxy() {
|
||||
assert_eq!(
|
||||
super::windows_system_proxy(&MockWindowsEnabledRegRunner)
|
||||
.await
|
||||
.unwrap()
|
||||
.as_deref(),
|
||||
Some("http://127.0.0.1:8080")
|
||||
);
|
||||
}
|
||||
|
||||
struct MockLinuxUnavailableGsettingsRunner;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ProxyCommandRunner for MockLinuxUnavailableGsettingsRunner {
|
||||
async fn stdout(&self, _program: &str, _args: &[String]) -> Result<String, String> {
|
||||
Err("gsettings is unavailable: not found".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn linux_system_proxy_returns_none_when_gsettings_is_unavailable() {
|
||||
assert_eq!(
|
||||
super::linux_system_proxy(&MockLinuxUnavailableGsettingsRunner)
|
||||
.await
|
||||
.unwrap(),
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
||||
Reference in New Issue
Block a user