Files
ProxyWarden/src-tauri/tests/domain_tests.rs
T

212 lines
6.9 KiB
Rust

use proxywarden_lib::models::{
ComponentId, ProfileInput, ProfileItemInput, ProfileItemType, Protocol, ProxyProtocol,
TargetInput, TargetKind,
};
use proxywarden_lib::validation::{normalize_profile, normalize_target};
#[test]
fn normalizes_profile_source_items() {
let profile = normalize_profile(ProfileInput {
id: Some("Discord + Vesktop".to_string()),
name: " Discord + Vesktop ".to_string(),
enabled: true,
target_id: " home-gateway ".to_string(),
protocols: vec!["tcp".to_string(), "UDP".to_string(), "TCP".to_string()],
items: vec![
ProfileItemInput {
item_type: "process".to_string(),
value: "Discord.exe".to_string(),
recursive: None,
},
ProfileItemInput {
item_type: "folder".to_string(),
value: "%LOCALAPPDATA%\\Vesktop".to_string(),
recursive: Some(true),
},
ProfileItemInput {
item_type: "exe".to_string(),
value: "C:\\Games\\Game\\game.exe".to_string(),
recursive: Some(true),
},
],
})
.expect("profile should normalize");
assert_eq!(profile.id, "discord-vesktop");
assert_eq!(profile.name, "Discord + Vesktop");
assert_eq!(profile.target_id, "home-gateway");
assert_eq!(profile.protocols, vec![Protocol::Tcp, Protocol::Udp]);
assert_eq!(profile.items[0].item_type, ProfileItemType::Process);
assert_eq!(profile.items[0].value, "Discord");
assert!(!profile.items[0].recursive);
assert_eq!(profile.items[1].item_type, ProfileItemType::Folder);
assert!(profile.items[1].recursive);
assert_eq!(profile.items[2].item_type, ProfileItemType::Exe);
assert!(!profile.items[2].recursive);
}
#[test]
fn rejects_unsupported_profile_protocols() {
let error = normalize_profile(ProfileInput {
id: None,
name: "Bad protocol".to_string(),
enabled: true,
target_id: "home-gateway".to_string(),
protocols: vec!["icmp".to_string()],
items: vec![ProfileItemInput {
item_type: "process".to_string(),
value: "Discord".to_string(),
recursive: None,
}],
})
.expect_err("unsupported protocol should fail");
assert!(error.iter().any(|item| item.field == "protocols"));
}
#[test]
fn normalizes_external_target_without_local_singbox() {
let target = normalize_target(TargetInput {
id: Some("Home Gateway".to_string()),
name: " Home Gateway ".to_string(),
kind: "external".to_string(),
protocol: "socks5".to_string(),
host: " 192.168.50.111 ".to_string(),
port: 8080,
requires_component: None,
})
.expect("external target should normalize");
assert_eq!(target.id, "home-gateway");
assert_eq!(target.kind, TargetKind::External);
assert_eq!(target.protocol, ProxyProtocol::Socks5);
assert_eq!(target.host, "192.168.50.111");
assert_eq!(target.port, 8080);
assert_eq!(target.requires_component, None);
}
#[test]
fn local_singbox_target_can_exist_before_component_is_installed() {
let target = normalize_target(TargetInput {
id: Some("local-singbox".to_string()),
name: "Local sing-box".to_string(),
kind: "local".to_string(),
protocol: "socks5".to_string(),
host: "127.0.0.1".to_string(),
port: 1080,
requires_component: Some("singbox".to_string()),
})
.expect("local target definition should not require installed component");
assert_eq!(target.kind, TargetKind::Local);
assert_eq!(target.requires_component, Some(ComponentId::Singbox));
}
#[test]
fn rejects_malformed_target_fields() {
let error = normalize_target(TargetInput {
id: None,
name: "".to_string(),
kind: "external".to_string(),
protocol: "ftp".to_string(),
host: "".to_string(),
port: 70_000,
requires_component: Some("unknown".to_string()),
})
.expect_err("invalid target should fail");
assert!(error.iter().any(|item| item.field == "name"));
assert!(error.iter().any(|item| item.field == "host"));
assert!(error.iter().any(|item| item.field == "port"));
assert!(error.iter().any(|item| item.field == "protocol"));
assert!(error.iter().any(|item| item.field == "requires_component"));
}
#[test]
fn unicode_names_receive_distinct_stable_ids() {
let profile = normalize_profile(ProfileInput {
id: None,
name: "Игры".to_string(),
enabled: true,
target_id: "main-proxy".to_string(),
protocols: vec!["TCP".to_string()],
items: vec![ProfileItemInput {
item_type: "process".to_string(),
value: "game.exe".to_string(),
recursive: None,
}],
})
.expect("unicode profile should normalize");
let other = normalize_profile(ProfileInput {
id: None,
name: "Работа".to_string(),
enabled: true,
target_id: "main-proxy".to_string(),
protocols: vec!["TCP".to_string()],
items: vec![ProfileItemInput {
item_type: "process".to_string(),
value: "work.exe".to_string(),
recursive: None,
}],
})
.expect("second unicode profile should normalize");
assert!(profile.id.starts_with("profile-"));
assert!(other.id.starts_with("profile-"));
assert_ne!(profile.id, other.id);
}
#[test]
fn rejects_host_with_scheme_credentials_or_path() {
for host in [
"socks5://proxy.example.test",
"user@proxy.example.test",
"proxy.example.test/path",
] {
let error = normalize_target(TargetInput {
id: None,
name: "Invalid host".to_string(),
kind: "external".to_string(),
protocol: "socks5".to_string(),
host: host.to_string(),
port: 1080,
requires_component: None,
})
.expect_err("host must not contain URL syntax");
assert!(error.iter().any(|item| item.field == "host"));
}
}
#[test]
fn rejects_relative_or_non_executable_profile_paths() {
let error = normalize_profile(ProfileInput {
id: None,
name: "Invalid paths".to_string(),
enabled: true,
target_id: "main-proxy".to_string(),
protocols: vec!["TCP".to_string()],
items: vec![
ProfileItemInput {
item_type: "folder".to_string(),
value: r"relative\folder".to_string(),
recursive: None,
},
ProfileItemInput {
item_type: "exe".to_string(),
value: r"C:\Games\game.txt".to_string(),
recursive: None,
},
],
})
.expect_err("unsafe path shapes should fail validation");
assert_eq!(
error
.iter()
.filter(|item| item.field == "items.value")
.count(),
2
);
}