use hypertor::onion_service::{
ClientAuthKey, ClientAuthMode, OnionService, OnionServiceConfig, ServiceState,
};
use hypertor::security::{ClientSecurityConfig, SecurityLevel, ServiceSecurityConfig};
use hypertor::*;
use std::time::Duration;
#[test]
fn test_vanguard_mode_disabled() {
let mode = VanguardMode::Disabled;
assert!(matches!(mode, VanguardMode::Disabled));
}
#[test]
fn test_vanguard_mode_lite() {
let mode = VanguardMode::Lite;
assert!(matches!(mode, VanguardMode::Lite));
}
#[test]
fn test_vanguard_mode_full() {
let mode = VanguardMode::Full;
assert!(matches!(mode, VanguardMode::Full));
}
#[test]
fn test_vanguards_required_for_high_security() {
let config = OnionServiceConfig::high_security();
assert_eq!(
config.vanguard_mode,
Some(VanguardMode::Full),
"SECURITY FAILURE: high_security() must enable full vanguards!"
);
}
#[test]
fn test_vanguards_in_security_level_maximum() {
let level = SecurityLevel::Maximum;
assert_eq!(
level.vanguard_mode(),
VanguardMode::Full,
"SECURITY FAILURE: Maximum security level must use full vanguards!"
);
}
#[test]
fn test_pow_disabled_by_default() {
let config = OnionServiceConfig::default();
assert!(
!config.enable_pow,
"PoW should be disabled by default for compatibility"
);
}
#[test]
fn test_pow_enabled_with_method() {
let config = OnionServiceConfig::new("test").with_pow();
assert!(config.enable_pow, "with_pow() must enable proof-of-work");
}
#[test]
fn test_pow_queue_depth_configurable() {
let config = OnionServiceConfig::new("test")
.with_pow()
.pow_queue_depth(32000);
assert!(config.enable_pow);
assert_eq!(config.pow_queue_depth, Some(32000));
}
#[test]
fn test_pow_required_for_high_security() {
let config = OnionServiceConfig::high_security();
assert!(
config.enable_pow,
"SECURITY FAILURE: high_security() must enable PoW!"
);
assert!(
config.pow_queue_depth.unwrap_or(0) >= 16000,
"high_security() should have larger PoW queue"
);
}
#[test]
fn test_pow_in_enhanced_security_level() {
let level = SecurityLevel::Enhanced;
assert!(
level.pow_enabled(),
"Enhanced security level should enable PoW"
);
}
#[test]
fn test_rate_limit_not_set_by_default() {
let config = OnionServiceConfig::default();
assert!(
config.rate_limit_at_intro.is_none(),
"Rate limit should not be set by default"
);
}
#[test]
fn test_rate_limit_configurable() {
let config = OnionServiceConfig::new("test").rate_limit_at_intro(50.0, 100);
assert_eq!(config.rate_limit_at_intro, Some((50.0, 100)));
}
#[test]
fn test_rate_limit_in_high_security() {
let config = OnionServiceConfig::high_security();
assert!(
config.rate_limit_at_intro.is_some(),
"SECURITY FAILURE: high_security() must enable rate limiting!"
);
let (rate, _burst) = config.rate_limit_at_intro.unwrap();
assert!(
rate <= 20.0,
"high_security() should have restrictive rate limit"
);
}
#[test]
fn test_aggressive_rate_limit() {
let config = OnionServiceConfig::new("fortress").rate_limit_at_intro(1.0, 5);
let (rate, burst) = config.rate_limit_at_intro.unwrap();
assert_eq!(rate, 1.0);
assert_eq!(burst, 5);
}
#[test]
fn test_stream_limit_default() {
let config = OnionServiceConfig::default();
assert_eq!(
config.max_streams_per_circuit, 65535,
"Default should allow many streams for compatibility"
);
}
#[test]
fn test_stream_limit_configurable() {
let config = OnionServiceConfig::new("test").max_streams_per_circuit(50);
assert_eq!(config.max_streams_per_circuit, 50);
}
#[test]
fn test_stream_limit_in_high_security() {
let config = OnionServiceConfig::high_security();
assert!(
config.max_streams_per_circuit <= 100,
"SECURITY FAILURE: high_security() must limit streams per circuit!"
);
}
#[test]
fn test_intro_points_default() {
let config = OnionServiceConfig::default();
assert_eq!(
config.num_intro_points, 3,
"Default should be 3 intro points"
);
}
#[test]
fn test_intro_points_configurable() {
let config = OnionServiceConfig::new("test").num_intro_points(7);
assert_eq!(config.num_intro_points, 7);
}
#[test]
fn test_intro_points_clamped() {
let too_low = OnionServiceConfig::new("test").num_intro_points(0);
assert_eq!(too_low.num_intro_points, 1);
let too_high = OnionServiceConfig::new("test").num_intro_points(100);
assert_eq!(too_high.num_intro_points, 20);
}
#[test]
fn test_intro_points_in_high_security() {
let config = OnionServiceConfig::high_security();
assert!(
config.num_intro_points >= 5,
"high_security() should have more intro points for availability"
);
}
#[test]
fn test_client_auth_disabled_by_default() {
let config = OnionServiceConfig::default();
assert!(
!config.has_client_auth(),
"Client auth should be disabled by default"
);
assert!(config.authorized_clients.is_empty());
}
#[test]
fn test_client_auth_modes() {
let _none = ClientAuthMode::None;
let _basic = ClientAuthMode::Basic;
let _stealth = ClientAuthMode::Stealth;
}
#[test]
fn test_client_auth_key_generation() {
let (key, secret) = ClientAuthKey::generate("alice");
assert_eq!(key.client_id, "alice");
assert!(!key.is_expired());
assert_eq!(secret.len(), 32);
}
#[test]
fn test_client_auth_key_expiry() {
let (key, _) = ClientAuthKey::generate("bob");
let expired = key.with_expiry(Duration::ZERO);
std::thread::sleep(Duration::from_millis(1));
assert!(
expired.is_expired(),
"Key with zero duration should be expired"
);
}
#[test]
fn test_secret_key_debug_redacted() {
let (_, secret) = ClientAuthKey::generate("test");
let debug = format!("{:?}", secret);
assert!(
debug.contains("REDACTED"),
"SECURITY FAILURE: SecretKey debug output must be redacted!"
);
assert!(
!debug.contains("0x"),
"SECURITY FAILURE: SecretKey must not show hex bytes!"
);
}
#[test]
fn test_secret_key_len() {
let (_, secret) = ClientAuthKey::generate("test");
assert_eq!(secret.len(), 32);
assert!(!secret.is_empty());
}
#[test]
fn test_bridge_configuration() {
let _builder =
TorClientBuilder::new().bridge("obfs4 192.0.2.1:443 FINGERPRINT cert=xyz iat-mode=0");
}
#[test]
fn test_bridge_multiple_configuration() {
let _builder = TorClientBuilder::new()
.bridge("obfs4 192.0.2.1:443 FINGERPRINT cert=xyz iat-mode=0")
.bridge("obfs4 192.0.2.2:443 FINGERPRINT cert=abc iat-mode=0")
.bridge("obfs4 192.0.2.3:443 FINGERPRINT cert=def iat-mode=0");
}
#[test]
fn test_bridge_with_iterator_api() {
let bridges = vec![
"obfs4 192.0.2.1:443 FINGERPRINT cert=xyz iat-mode=0",
"obfs4 192.0.2.2:443 FINGERPRINT cert=abc iat-mode=0",
];
let _builder = TorClientBuilder::new().bridges(bridges);
}
#[test]
fn test_transport_configuration() {
let _builder = TorClientBuilder::new().transport("obfs4", "/usr/bin/obfs4proxy");
}
#[test]
fn test_transport_multiple_configuration() {
let _builder = TorClientBuilder::new()
.transport("obfs4", "/usr/bin/obfs4proxy")
.transport("snowflake", "/usr/bin/snowflake-client")
.transport("webtunnel", "/usr/bin/webtunnel");
}
#[test]
fn test_china_censorship_scenario() {
let _builder = TorClientBuilder::new()
.bridge("obfs4 192.0.2.1:443 FINGERPRINT cert=xyz iat-mode=0")
.bridge("obfs4 192.0.2.2:443 FINGERPRINT cert=abc iat-mode=0")
.transport("obfs4", "/usr/bin/obfs4proxy")
.vanguards(VanguardMode::Full);
}
#[test]
fn test_free_press_server_scenario() {
let config = OnionServiceConfig::new("securedrop")
.with_pow()
.pow_queue_depth(16000)
.rate_limit_at_intro(10.0, 20)
.max_streams_per_circuit(100)
.vanguards_full()
.num_intro_points(5);
assert!(config.enable_pow, "Free press server MUST have PoW!");
assert_eq!(
config.vanguard_mode,
Some(VanguardMode::Full),
"Free press server MUST have full vanguards!"
);
assert!(
config.rate_limit_at_intro.is_some(),
"Free press server MUST have rate limiting!"
);
}
#[test]
fn test_private_service_scenario() {
let config = OnionServiceConfig::new("private")
.with_pow()
.vanguards_full()
.rate_limit_at_intro(5.0, 10)
.max_streams_per_circuit(50);
assert!(config.enable_pow);
assert!(config.vanguard_mode == Some(VanguardMode::Full));
}
#[test]
fn test_maximum_hardening_all_features() {
let config = OnionServiceConfig::new("fortress")
.port(443)
.vanguards_full()
.with_pow()
.pow_queue_depth(32000)
.rate_limit_at_intro(5.0, 10)
.max_streams_per_circuit(50)
.num_intro_points(10);
assert_eq!(config.port, 443);
assert_eq!(config.vanguard_mode, Some(VanguardMode::Full));
assert!(config.enable_pow);
assert_eq!(config.pow_queue_depth, Some(32000));
assert_eq!(config.rate_limit_at_intro, Some((5.0, 10)));
assert_eq!(config.max_streams_per_circuit, 50);
assert_eq!(config.num_intro_points, 10);
}
#[test]
fn test_security_level_standard() {
let level = SecurityLevel::Standard;
let config = level.onion_service_config("test");
assert!(!config.enable_pow);
}
#[test]
fn test_security_level_enhanced() {
let level = SecurityLevel::Enhanced;
let config = level.onion_service_config("test");
assert!(config.enable_pow);
assert!(config.rate_limit_at_intro.is_some());
}
#[test]
fn test_security_level_maximum() {
let level = SecurityLevel::Maximum;
let config = level.onion_service_config("test");
assert!(config.enable_pow);
assert!(config.rate_limit_at_intro.is_some());
assert_eq!(config.max_streams_per_circuit, 100);
assert_eq!(config.num_intro_points, 5);
}
#[test]
fn test_client_security_config_strict_isolation() {
let config = ClientSecurityConfig::maximum();
assert!(
config.strict_isolation,
"Maximum client security must have strict isolation"
);
assert_eq!(config.vanguard_mode, VanguardMode::Full);
}
#[test]
fn test_service_security_config_complete() {
let config = ServiceSecurityConfig::maximum();
assert!(config.enable_pow);
assert!(config.rate_limit.is_some());
assert_eq!(config.max_streams, 100);
assert_eq!(config.num_intro_points, 5);
}
#[test]
fn test_service_initial_state() {
let service = OnionService::new(OnionServiceConfig::default());
assert_eq!(service.state(), ServiceState::Idle);
assert!(service.address().is_none());
assert!(!service.is_running());
}
#[test]
fn test_onion_config_fluent_api() {
let config = OnionServiceConfig::new("test")
.port(8080)
.with_pow()
.vanguards_lite()
.rate_limit_at_intro(100.0, 200)
.max_streams_per_circuit(500)
.num_intro_points(3);
assert_eq!(config.nickname, "test");
assert_eq!(config.port, 8080);
assert!(config.enable_pow);
assert_eq!(config.vanguard_mode, Some(VanguardMode::Lite));
assert_eq!(config.rate_limit_at_intro, Some((100.0, 200)));
assert_eq!(config.max_streams_per_circuit, 500);
assert_eq!(config.num_intro_points, 3);
}
#[test]
fn test_client_builder_fluent_api() {
let _builder = TorClientBuilder::new()
.timeout(Duration::from_secs(60))
.max_connections(20)
.vanguards_full()
.bridge("test bridge line")
.transport("obfs4", "/path/to/binary");
}
#[test]
fn test_rate_limit_parameters_valid() {
let config = OnionServiceConfig::new("test").rate_limit_at_intro(10.0, 20);
assert_eq!(config.rate_limit_at_intro, Some((10.0, 20)));
}
#[test]
fn test_rate_limit_zero_rate() {
let config = OnionServiceConfig::new("test").rate_limit_at_intro(0.0, 10);
assert_eq!(config.rate_limit_at_intro, Some((0.0, 10)));
}
#[test]
fn test_stream_limit_range() {
for limit in [1, 10, 100, 1000, u32::MAX] {
let config = OnionServiceConfig::new("test").max_streams_per_circuit(limit);
assert_eq!(config.max_streams_per_circuit, limit);
}
}
#[test]
fn test_pow_queue_depth_values() {
for depth in [100, 1000, 16000, 32000] {
let config = OnionServiceConfig::new("test")
.with_pow()
.pow_queue_depth(depth);
assert_eq!(config.pow_queue_depth, Some(depth));
}
}
#[test]
fn test_timeout_configuration() {
let timeouts = [
Duration::from_secs(1),
Duration::from_secs(30),
Duration::from_secs(120),
Duration::from_millis(500),
];
for timeout in timeouts {
let _builder = TorClientBuilder::new().timeout(timeout);
}
}
#[test]
fn test_max_connections_range() {
for limit in [1, 5, 10, 50, 100] {
let _builder = TorClientBuilder::new().max_connections(limit);
}
}
#[test]
fn test_nickname_with_special_chars() {
let nicknames = [
"simple",
"with-dash",
"with_underscore",
"CamelCase",
"mix123",
];
for nick in nicknames {
let config = OnionServiceConfig::new(nick);
assert_eq!(config.nickname, nick);
}
}
#[test]
fn test_port_configuration() {
for port in [80, 443, 8080, 9000, 65535] {
let config = OnionServiceConfig::new("test").port(port);
assert_eq!(config.port, port);
}
}
#[test]
fn test_multiple_bridges_accumulate() {
let _builder = TorClientBuilder::new()
.bridge("bridge1")
.bridge("bridge2")
.bridge("bridge3");
}
#[test]
fn test_multiple_transports_accumulate() {
let _builder = TorClientBuilder::new()
.transport("obfs4", "/path/to/obfs4proxy")
.transport("snowflake", "/path/to/snowflake")
.transport("meek", "/path/to/meek");
}
#[test]
fn test_vanguard_mode_override() {
let _builder = TorClientBuilder::new()
.vanguards(VanguardMode::Lite)
.vanguards(VanguardMode::Full);
}
#[test]
fn test_security_config_chaining() {
let config = OnionServiceConfig::new("chain-test")
.with_pow()
.pow_queue_depth(8000)
.rate_limit_at_intro(5.0, 10)
.max_streams_per_circuit(50)
.vanguards_full()
.num_intro_points(4)
.port(8080);
assert!(config.enable_pow);
assert_eq!(config.pow_queue_depth, Some(8000));
assert_eq!(config.rate_limit_at_intro, Some((5.0, 10)));
assert_eq!(config.max_streams_per_circuit, 50);
assert_eq!(config.vanguard_mode, Some(VanguardMode::Full));
assert_eq!(config.num_intro_points, 4);
assert_eq!(config.port, 8080);
}
#[test]
fn test_defense_in_depth_client() {
let _builder = TorClientBuilder::new()
.bridge("obfs4 192.0.2.1:443 FINGERPRINT cert=... iat-mode=0")
.transport("obfs4", "/usr/bin/obfs4proxy")
.vanguards(VanguardMode::Full)
.max_connections(10)
.timeout(Duration::from_secs(120));
}
#[test]
fn test_defense_in_depth_service() {
let config = OnionServiceConfig::new("dod-service")
.with_pow()
.pow_queue_depth(16000)
.rate_limit_at_intro(10.0, 20)
.max_streams_per_circuit(100)
.vanguards_full()
.num_intro_points(5);
assert!(config.enable_pow);
assert!(config.rate_limit_at_intro.is_some());
assert_eq!(config.max_streams_per_circuit, 100);
assert_eq!(config.vanguard_mode, Some(VanguardMode::Full));
assert_eq!(config.num_intro_points, 5);
}