use super::host_parser::parse_single_jump_host;
use super::*;
#[test]
fn test_parse_single_jump_host_hostname_only() {
let result = parse_single_jump_host("example.com").unwrap();
assert_eq!(result.host, "example.com");
assert_eq!(result.user, None);
assert_eq!(result.port, None);
}
#[test]
fn test_parse_single_jump_host_with_user() {
let result = parse_single_jump_host("admin@example.com").unwrap();
assert_eq!(result.host, "example.com");
assert_eq!(result.user, Some("admin".to_string()));
assert_eq!(result.port, None);
}
#[test]
fn test_parse_single_jump_host_with_port() {
let result = parse_single_jump_host("example.com:2222").unwrap();
assert_eq!(result.host, "example.com");
assert_eq!(result.user, None);
assert_eq!(result.port, Some(2222));
}
#[test]
fn test_parse_single_jump_host_with_user_and_port() {
let result = parse_single_jump_host("admin@example.com:2222").unwrap();
assert_eq!(result.host, "example.com");
assert_eq!(result.user, Some("admin".to_string()));
assert_eq!(result.port, Some(2222));
}
#[test]
fn test_parse_single_jump_host_ipv6_brackets() {
let result = parse_single_jump_host("[::1]").unwrap();
assert_eq!(result.host, "::1");
assert_eq!(result.user, None);
assert_eq!(result.port, None);
}
#[test]
fn test_parse_single_jump_host_ipv6_with_port() {
let result = parse_single_jump_host("[::1]:2222").unwrap();
assert_eq!(result.host, "::1");
assert_eq!(result.user, None);
assert_eq!(result.port, Some(2222));
}
#[test]
fn test_parse_single_jump_host_ipv6_with_user_and_port() {
let result = parse_single_jump_host("admin@[::1]:2222").unwrap();
assert_eq!(result.host, "::1");
assert_eq!(result.user, Some("admin".to_string()));
assert_eq!(result.port, Some(2222));
}
#[test]
fn test_parse_jump_hosts_multiple() {
let result = parse_jump_hosts("jump1@host1,user@host2:2222,host3").unwrap();
assert_eq!(result.len(), 3);
assert_eq!(result[0].host, "host1");
assert_eq!(result[0].user, Some("jump1".to_string()));
assert_eq!(result[0].port, None);
assert_eq!(result[1].host, "host2");
assert_eq!(result[1].user, Some("user".to_string()));
assert_eq!(result[1].port, Some(2222));
assert_eq!(result[2].host, "host3");
assert_eq!(result[2].user, None);
assert_eq!(result[2].port, None);
}
#[test]
fn test_parse_jump_hosts_whitespace_handling() {
let result = parse_jump_hosts(" host1 , user@host2:2222 , host3 ").unwrap();
assert_eq!(result.len(), 3);
assert_eq!(result[0].host, "host1");
assert_eq!(result[1].host, "host2");
assert_eq!(result[2].host, "host3");
}
#[test]
fn test_parse_jump_hosts_empty_string() {
let result = parse_jump_hosts("").unwrap();
assert_eq!(result.len(), 0);
}
#[test]
fn test_parse_jump_hosts_only_commas() {
let result = parse_jump_hosts(",,");
assert!(result.is_err()); }
#[test]
fn test_parse_single_jump_host_errors() {
assert!(parse_single_jump_host("").is_err());
assert!(parse_single_jump_host("@host").is_err());
assert!(parse_single_jump_host("user@").is_err());
assert!(parse_single_jump_host("host:").is_err());
assert!(parse_single_jump_host("host:0").is_err());
assert!(parse_single_jump_host("host:99999").is_err());
assert!(parse_single_jump_host("[::1").is_err());
assert!(parse_single_jump_host("[]").is_err());
}
#[test]
fn test_jump_host_display() {
let host = JumpHost::new("example.com".to_string(), None, None);
assert_eq!(format!("{host}"), "example.com");
let host = JumpHost::new("example.com".to_string(), Some("user".to_string()), None);
assert_eq!(format!("{host}"), "user@example.com");
let host = JumpHost::new("example.com".to_string(), None, Some(2222));
assert_eq!(format!("{host}"), "example.com:2222");
let host = JumpHost::new(
"example.com".to_string(),
Some("user".to_string()),
Some(2222),
);
assert_eq!(format!("{host}"), "user@example.com:2222");
}
#[test]
fn test_jump_host_effective_values() {
let host = JumpHost::new("example.com".to_string(), None, None);
assert_eq!(host.effective_port(), 22);
assert!(!host.effective_user().is_empty());
let host = JumpHost::new(
"example.com".to_string(),
Some("testuser".to_string()),
Some(2222),
);
assert_eq!(host.effective_port(), 2222);
assert_eq!(host.effective_user(), "testuser");
}
#[test]
#[serial_test::serial]
fn test_max_jump_hosts_limit_exactly_10() {
std::env::remove_var("BSSH_MAX_JUMP_HOSTS");
let spec = (0..10)
.map(|i| format!("host{i}"))
.collect::<Vec<_>>()
.join(",");
let result = parse_jump_hosts(&spec);
assert!(result.is_ok(), "Should accept exactly 10 jump hosts");
assert_eq!(result.unwrap().len(), 10);
}
#[test]
#[serial_test::serial]
fn test_max_jump_hosts_limit_11_rejected() {
std::env::remove_var("BSSH_MAX_JUMP_HOSTS");
let spec = (0..11)
.map(|i| format!("host{i}"))
.collect::<Vec<_>>()
.join(",");
let result = parse_jump_hosts(&spec);
assert!(result.is_err(), "Should reject 11 jump hosts");
let err_msg = result.unwrap_err().to_string();
assert!(
err_msg.contains("Too many jump hosts"),
"Error should mention 'Too many jump hosts', got: {err_msg}"
);
assert!(
err_msg.contains("11"),
"Error should mention the actual count (11), got: {err_msg}"
);
assert!(
err_msg.contains("10"),
"Error should mention the maximum (10), got: {err_msg}"
);
}
#[test]
fn test_max_jump_hosts_limit_excessive() {
let spec = (0..100)
.map(|i| format!("host{i}"))
.collect::<Vec<_>>()
.join(",");
let result = parse_jump_hosts(&spec);
assert!(
result.is_err(),
"Should reject excessive number of jump hosts"
);
let err_msg = result.unwrap_err().to_string();
assert!(
err_msg.contains("Too many jump hosts"),
"Error should be about too many hosts, got: {err_msg}"
);
}
#[test]
#[serial_test::serial]
fn test_get_max_jump_hosts_default() {
std::env::remove_var("BSSH_MAX_JUMP_HOSTS");
let max = get_max_jump_hosts();
assert_eq!(max, 10, "Default should be 10");
}
#[test]
#[serial_test::serial]
fn test_get_max_jump_hosts_custom_value() {
unsafe {
std::env::set_var("BSSH_MAX_JUMP_HOSTS", "15");
}
let max = get_max_jump_hosts();
assert_eq!(max, 15, "Should use custom value from environment");
std::env::remove_var("BSSH_MAX_JUMP_HOSTS");
}
#[test]
#[serial_test::serial]
fn test_get_max_jump_hosts_capped_at_absolute_max() {
unsafe {
std::env::set_var("BSSH_MAX_JUMP_HOSTS", "50");
}
let max = get_max_jump_hosts();
assert_eq!(
max, 30,
"Should be capped at absolute maximum of 30 for security"
);
std::env::remove_var("BSSH_MAX_JUMP_HOSTS");
}
#[test]
#[serial_test::serial]
fn test_get_max_jump_hosts_zero_falls_back() {
unsafe {
std::env::set_var("BSSH_MAX_JUMP_HOSTS", "0");
}
let max = get_max_jump_hosts();
assert_eq!(max, 10, "Zero should fall back to default (10)");
std::env::remove_var("BSSH_MAX_JUMP_HOSTS");
}
#[test]
#[serial_test::serial]
fn test_get_max_jump_hosts_invalid_value() {
unsafe {
std::env::set_var("BSSH_MAX_JUMP_HOSTS", "invalid");
}
let max = get_max_jump_hosts();
assert_eq!(max, 10, "Invalid value should fall back to default (10)");
std::env::remove_var("BSSH_MAX_JUMP_HOSTS");
}
#[test]
#[serial_test::serial]
fn test_max_jump_hosts_respects_environment() {
unsafe {
std::env::set_var("BSSH_MAX_JUMP_HOSTS", "15");
}
let spec_15 = (0..15)
.map(|i| format!("host{i}"))
.collect::<Vec<_>>()
.join(",");
let result = parse_jump_hosts(&spec_15);
assert!(
result.is_ok(),
"Should accept 15 hosts when BSSH_MAX_JUMP_HOSTS=15"
);
assert_eq!(result.unwrap().len(), 15);
let spec_16 = (0..16)
.map(|i| format!("host{i}"))
.collect::<Vec<_>>()
.join(",");
let result = parse_jump_hosts(&spec_16);
assert!(
result.is_err(),
"Should reject 16 hosts when BSSH_MAX_JUMP_HOSTS=15"
);
std::env::remove_var("BSSH_MAX_JUMP_HOSTS");
}