314 lines
10 KiB
Rust
314 lines
10 KiB
Rust
use proxywarden_lib::component_detection::{
|
|
detect_proxyfier_install_with_host, detect_singbox_install_with_host,
|
|
proxyfier_component_from_detection, singbox_component_from_detection, ProxyfierDetectionHost,
|
|
ProxyfierEngine, RegistryInstallEntry,
|
|
};
|
|
use proxywarden_lib::models::ComponentState;
|
|
use std::{
|
|
collections::{HashMap, HashSet},
|
|
path::{Path, PathBuf},
|
|
};
|
|
|
|
#[test]
|
|
fn detects_existing_proxifyre_from_registry_install_location() {
|
|
let host = MockHost::new()
|
|
.with_registry("ProxiFyre", r"C:\Tools\ProxiFyre")
|
|
.with_path(r"C:\Tools\ProxiFyre")
|
|
.with_path(r"C:\Tools\ProxiFyre\ProxiFyre.exe")
|
|
.with_service_path(
|
|
"ProxiFyreService",
|
|
r#""C:\Tools\ProxiFyre\ProxiFyre.exe" --service"#,
|
|
);
|
|
|
|
let detected = detect_proxyfier_install_with_host(&host)
|
|
.expect("existing ProxiFyre install should be detected");
|
|
|
|
assert_eq!(detected.engine, ProxyfierEngine::ProxiFyre);
|
|
assert_eq!(detected.install_dir, PathBuf::from(r"C:\Tools\ProxiFyre"));
|
|
assert_eq!(
|
|
detected.config_path,
|
|
Some(PathBuf::from(r"C:\Tools\ProxiFyre\app-config.json"))
|
|
);
|
|
assert!(detected.running);
|
|
assert_eq!(detected.service_name, Some("ProxiFyreService".to_string()));
|
|
assert_eq!(detected.service_status, Some("running".to_string()));
|
|
|
|
let component = proxyfier_component_from_detection(Some(&detected));
|
|
assert_eq!(component.state, ComponentState::Running);
|
|
assert!(component.installed);
|
|
assert!(component.running);
|
|
assert_eq!(component.path, Some(r"C:\Tools\ProxiFyre".to_string()));
|
|
assert_eq!(component.service_name, Some("ProxiFyreService".to_string()));
|
|
assert_eq!(component.service_status, Some("running".to_string()));
|
|
assert!(component.problems.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn ignores_empty_common_proxifyre_folder_without_executable() {
|
|
let host = MockHost::new().with_path(r"C:\Tools\ProxiFyre");
|
|
|
|
assert!(detect_proxyfier_install_with_host(&host).is_none());
|
|
|
|
let component = proxyfier_component_from_detection(None);
|
|
assert_eq!(component.state, ComponentState::Missing);
|
|
assert!(!component.installed);
|
|
}
|
|
|
|
#[test]
|
|
fn ignores_plain_proxifier_install() {
|
|
let host = MockHost::new()
|
|
.with_registry("Proxifier", r"C:\Program Files\Proxifier")
|
|
.with_path(r"C:\Program Files\Proxifier")
|
|
.with_process("Proxifier.exe");
|
|
|
|
assert!(detect_proxyfier_install_with_host(&host).is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn env_override_can_point_to_portable_proxifyre_install() {
|
|
let host = MockHost::new()
|
|
.with_env("PROXYWARDEN_PROXIFYRE_ROOT", r"D:\Portable\ProxiFyre")
|
|
.with_path(r"D:\Portable\ProxiFyre\ProxiFyre.exe");
|
|
|
|
let detected = detect_proxyfier_install_with_host(&host)
|
|
.expect("env override should be checked before common paths");
|
|
|
|
assert_eq!(detected.engine, ProxyfierEngine::ProxiFyre);
|
|
assert_eq!(
|
|
detected.executable_path,
|
|
PathBuf::from(r"D:\Portable\ProxiFyre\ProxiFyre.exe")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn reports_stopped_proxifyre_service_when_executable_exists() {
|
|
let host = MockHost::new()
|
|
.with_env("PROXYWARDEN_PROXIFYRE_ROOT", r"C:\Tools\ProxiFyre")
|
|
.with_path(r"C:\Tools\ProxiFyre\ProxiFyre.exe")
|
|
.with_stopped_service_path(
|
|
"ProxiFyreService",
|
|
r#""C:\Tools\ProxiFyre\ProxiFyre.exe" --service"#,
|
|
);
|
|
|
|
let detected =
|
|
detect_proxyfier_install_with_host(&host).expect("proxifyre executable should be detected");
|
|
let component = proxyfier_component_from_detection(Some(&detected));
|
|
|
|
assert_eq!(component.state, ComponentState::Installed);
|
|
assert!(component.installed);
|
|
assert!(!component.running);
|
|
assert_eq!(component.service_name, Some("ProxiFyreService".to_string()));
|
|
assert_eq!(component.service_status, Some("stopped".to_string()));
|
|
assert!(component.problems.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn missing_proxyfier_returns_install_action_status() {
|
|
let component = proxyfier_component_from_detection(None);
|
|
|
|
assert_eq!(component.state, ComponentState::Missing);
|
|
assert!(!component.installed);
|
|
assert_eq!(component.actions, vec!["Установить ProxiFyre"]);
|
|
}
|
|
|
|
#[test]
|
|
fn ignores_known_service_name_when_path_points_to_foreign_binary() {
|
|
let host = MockHost::new()
|
|
.with_env("PROXYWARDEN_PROXIFYRE_ROOT", r"C:\Tools\ProxiFyre")
|
|
.with_path(r"C:\Tools\ProxiFyre\ProxiFyre.exe")
|
|
.with_service_path(
|
|
"ProxiFyreService",
|
|
r#""C:\Foreign\ProxiFyre.exe" --service"#,
|
|
);
|
|
|
|
let detected =
|
|
detect_proxyfier_install_with_host(&host).expect("executable should still be detected");
|
|
|
|
assert!(!detected.running);
|
|
assert_eq!(detected.service_status, None);
|
|
}
|
|
|
|
#[test]
|
|
fn ignores_known_service_name_without_path_metadata() {
|
|
let host = MockHost::new()
|
|
.with_env("PROXYWARDEN_PROXIFYRE_ROOT", r"C:\Tools\ProxiFyre")
|
|
.with_path(r"C:\Tools\ProxiFyre\ProxiFyre.exe")
|
|
.with_service("ProxiFyreService");
|
|
|
|
let detected =
|
|
detect_proxyfier_install_with_host(&host).expect("executable should still be detected");
|
|
|
|
assert!(!detected.running);
|
|
assert_eq!(detected.service_status, None);
|
|
}
|
|
|
|
#[test]
|
|
fn detects_running_local_singbox_from_default_install_root_and_service() {
|
|
let host = MockHost::new()
|
|
.with_path(r"C:\Program Files\ProxyWarden\components\sing-box\sing-box.exe")
|
|
.with_service("ProxyWardenSingBox");
|
|
|
|
let detected =
|
|
detect_singbox_install_with_host(&host).expect("existing sing-box should be detected");
|
|
|
|
assert_eq!(
|
|
detected.executable_path,
|
|
PathBuf::from(r"C:\Program Files\ProxyWarden\components\sing-box\sing-box.exe")
|
|
);
|
|
assert_eq!(detected.service_name, "ProxyWardenSingBox");
|
|
assert!(detected.running);
|
|
|
|
let component = singbox_component_from_detection(Some(&detected));
|
|
assert_eq!(component.state, ComponentState::Running);
|
|
assert!(component.installed);
|
|
assert!(component.running);
|
|
assert_eq!(
|
|
component.path,
|
|
Some(r"C:\Program Files\ProxyWarden\components\sing-box\sing-box.exe".to_string())
|
|
);
|
|
assert!(component.problems.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn detects_stopped_local_singbox_from_env_override() {
|
|
let host = MockHost::new()
|
|
.with_env("PROXYWARDEN_SINGBOX_ROOT", r"D:\Portable\sing-box")
|
|
.with_path(r"D:\Portable\sing-box\sing-box.exe");
|
|
|
|
let detected = detect_singbox_install_with_host(&host).expect("env override should be checked");
|
|
let component = singbox_component_from_detection(Some(&detected));
|
|
|
|
assert_eq!(
|
|
detected.executable_path,
|
|
PathBuf::from(r"D:\Portable\sing-box\sing-box.exe")
|
|
);
|
|
assert_eq!(component.state, ComponentState::Stopped);
|
|
assert!(component.installed);
|
|
assert!(!component.running);
|
|
assert_eq!(
|
|
component.service_name,
|
|
Some("ProxyWardenSingBox".to_string())
|
|
);
|
|
assert_eq!(component.service_status, Some("stopped".to_string()));
|
|
assert!(component
|
|
.problems
|
|
.iter()
|
|
.any(|problem| problem.contains("остановлена")));
|
|
}
|
|
|
|
#[test]
|
|
fn missing_local_singbox_returns_optional_install_action_status() {
|
|
let component = singbox_component_from_detection(None);
|
|
|
|
assert_eq!(component.state, ComponentState::Missing);
|
|
assert!(!component.installed);
|
|
assert!(!component.running);
|
|
assert_eq!(component.actions, vec!["Установить Local sing-box"]);
|
|
assert!(component.problems.is_empty());
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct MockHost {
|
|
env: HashMap<String, String>,
|
|
paths: HashSet<String>,
|
|
processes: HashSet<String>,
|
|
services: HashMap<String, String>,
|
|
service_paths: HashMap<String, String>,
|
|
registry: Vec<RegistryInstallEntry>,
|
|
}
|
|
|
|
impl MockHost {
|
|
fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
fn with_env(mut self, name: &str, value: &str) -> Self {
|
|
self.env.insert(name.to_string(), value.to_string());
|
|
self
|
|
}
|
|
|
|
fn with_path(mut self, path: &str) -> Self {
|
|
self.paths.insert(normalize_path(path));
|
|
self
|
|
}
|
|
|
|
fn with_process(mut self, process: &str) -> Self {
|
|
self.processes.insert(process.to_ascii_lowercase());
|
|
self
|
|
}
|
|
|
|
fn with_service(mut self, service: &str) -> Self {
|
|
self.services
|
|
.insert(service.to_ascii_lowercase(), "running".to_string());
|
|
self
|
|
}
|
|
|
|
fn with_service_path(mut self, service: &str, path_name: &str) -> Self {
|
|
self.services
|
|
.insert(service.to_ascii_lowercase(), "running".to_string());
|
|
self.service_paths
|
|
.insert(service.to_ascii_lowercase(), path_name.to_string());
|
|
self
|
|
}
|
|
|
|
fn with_stopped_service_path(mut self, service: &str, path_name: &str) -> Self {
|
|
self.services
|
|
.insert(service.to_ascii_lowercase(), "stopped".to_string());
|
|
self.service_paths
|
|
.insert(service.to_ascii_lowercase(), path_name.to_string());
|
|
self
|
|
}
|
|
|
|
fn with_registry(mut self, display_name: &str, install_location: &str) -> Self {
|
|
self.registry.push(RegistryInstallEntry {
|
|
display_name: display_name.to_string(),
|
|
install_location: Some(PathBuf::from(install_location)),
|
|
display_icon: None,
|
|
});
|
|
self
|
|
}
|
|
}
|
|
|
|
impl ProxyfierDetectionHost for MockHost {
|
|
fn env_var(&self, name: &str) -> Option<String> {
|
|
self.env.get(name).cloned()
|
|
}
|
|
|
|
fn path_exists(&self, path: &Path) -> bool {
|
|
self.paths
|
|
.contains(&normalize_path(&path.display().to_string()))
|
|
}
|
|
|
|
fn process_running(&self, process_name: &str) -> bool {
|
|
self.processes.contains(&process_name.to_ascii_lowercase())
|
|
}
|
|
|
|
fn service_status(&self, service_name: &str) -> Option<String> {
|
|
self.services
|
|
.get(&service_name.to_ascii_lowercase())
|
|
.cloned()
|
|
}
|
|
|
|
fn service_info(
|
|
&self,
|
|
service_name: &str,
|
|
) -> Option<proxywarden_lib::component_detection::DetectedService> {
|
|
let key = service_name.to_ascii_lowercase();
|
|
self.services.get(&key).map(|status| {
|
|
proxywarden_lib::component_detection::DetectedService {
|
|
name: service_name.to_string(),
|
|
status: status.clone(),
|
|
path_name: self.service_paths.get(&key).cloned(),
|
|
}
|
|
})
|
|
}
|
|
|
|
fn registry_install_entries(&self) -> Vec<RegistryInstallEntry> {
|
|
self.registry.clone()
|
|
}
|
|
}
|
|
|
|
fn normalize_path(path: &str) -> String {
|
|
path.replace('/', "\\").to_ascii_lowercase()
|
|
}
|