use minijinja::Environment;
use std::net::TcpListener;
use std::path::PathBuf;
use std::sync::Arc;
use tmpltool::TemplateContext;
use tmpltool::is_functions::network::PortAvailable;
fn create_test_env() -> Environment<'static> {
let mut env = Environment::new();
let context = Arc::new(TemplateContext::new(PathBuf::from("."), false));
tmpltool::is_functions::register_all(&mut env, context);
env
}
fn render(env: &Environment, template: &str) -> String {
env.render_str(template, ()).unwrap()
}
fn find_available_port() -> u16 {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let port = listener.local_addr().unwrap().port();
drop(listener); port
}
fn bind_port(port: u16) -> Option<TcpListener> {
TcpListener::bind(("0.0.0.0", port)).ok()
}
#[test]
fn test_port_available_finds_free_port() {
let port = find_available_port();
assert!(PortAvailable::is_available(port));
}
#[test]
fn test_port_available_detects_bound_port() {
let port = find_available_port();
let _listener = bind_port(port);
if _listener.is_some() {
assert!(!PortAvailable::is_available(port));
}
}
#[test]
fn test_is_port_available_function_syntax() {
let env = create_test_env();
let port = find_available_port();
let template = format!(r#"{{{{ is_port_available(port={}) }}}}"#, port);
let result = render(&env, &template);
assert_eq!(result, "true");
}
#[test]
fn test_is_port_available_function_syntax_bound_port() {
let env = create_test_env();
let port = find_available_port();
let _listener = bind_port(port);
if _listener.is_some() {
let template = format!(r#"{{{{ is_port_available(port={}) }}}}"#, port);
let result = render(&env, &template);
assert_eq!(result, "false");
}
}
#[test]
fn test_is_port_available_function_in_conditional() {
let env = create_test_env();
let port = find_available_port();
let template = format!(
r#"{{% if is_port_available(port={}) %}}free{{% else %}}busy{{% endif %}}"#,
port
);
let result = render(&env, &template);
assert_eq!(result, "free");
}
#[test]
fn test_is_port_available_is_syntax() {
let env = create_test_env();
let port = find_available_port();
let template = format!(
r#"{{% if {} is port_available %}}yes{{% else %}}no{{% endif %}}"#,
port
);
let result = render(&env, &template);
assert_eq!(result, "yes");
}
#[test]
fn test_is_port_available_is_syntax_bound_port() {
let env = create_test_env();
let port = find_available_port();
let _listener = bind_port(port);
if _listener.is_some() {
let template = format!(
r#"{{% if {} is port_available %}}yes{{% else %}}no{{% endif %}}"#,
port
);
let result = render(&env, &template);
assert_eq!(result, "no");
}
}
#[test]
fn test_is_port_available_is_syntax_with_variable() {
let mut env = create_test_env();
env.add_template(
"test",
r#"{% if port is port_available %}free{% else %}busy{% endif %}"#,
)
.unwrap();
let port = find_available_port();
let tmpl = env.get_template("test").unwrap();
let result = tmpl.render(minijinja::context! { port => port }).unwrap();
assert_eq!(result, "free");
}
#[test]
fn test_is_port_available_is_syntax_with_string() {
let env = create_test_env();
let port = find_available_port();
let template = format!(
r#"{{% if "{}" is port_available %}}yes{{% else %}}no{{% endif %}}"#,
port
);
let result = render(&env, &template);
assert_eq!(result, "yes");
}
#[test]
fn test_is_port_available_is_syntax_negation() {
let env = create_test_env();
let port = find_available_port();
let _listener = bind_port(port);
if _listener.is_some() {
let template = format!(
r#"{{% if {} is not port_available %}}busy{{% else %}}free{{% endif %}}"#,
port
);
let result = render(&env, &template);
assert_eq!(result, "busy");
}
}
#[test]
fn test_is_port_available_invalid_port_zero() {
let env = create_test_env();
assert_eq!(
render(
&env,
r#"{% if 0 is port_available %}yes{% else %}no{% endif %}"#
),
"no"
);
}
#[test]
fn test_is_port_available_invalid_port_negative() {
let env = create_test_env();
assert_eq!(
render(
&env,
r#"{% if -1 is port_available %}yes{% else %}no{% endif %}"#
),
"no"
);
}
#[test]
fn test_is_port_available_invalid_port_too_high() {
let env = create_test_env();
assert_eq!(
render(
&env,
r#"{% if 65536 is port_available %}yes{% else %}no{% endif %}"#
),
"no"
);
}
#[test]
fn test_is_port_available_non_numeric_string() {
let env = create_test_env();
assert_eq!(
render(
&env,
r#"{% if "not-a-port" is port_available %}yes{% else %}no{% endif %}"#
),
"no"
);
}
#[test]
fn test_is_port_available_empty_string() {
let env = create_test_env();
assert_eq!(
render(
&env,
r#"{% if "" is port_available %}yes{% else %}no{% endif %}"#
),
"no"
);
}
#[test]
fn test_is_port_available_boundary_port_1() {
let env = create_test_env();
let result = render(
&env,
r#"{% if 1 is port_available %}yes{% else %}no{% endif %}"#,
);
assert!(result == "yes" || result == "no");
}
#[test]
fn test_is_port_available_boundary_port_65535() {
let env = create_test_env();
let result = render(
&env,
r#"{% if 65535 is port_available %}yes{% else %}no{% endif %}"#,
);
assert!(result == "yes" || result == "no");
}
#[test]
fn test_is_port_available_function_invalid_port_error() {
let env = create_test_env();
let result = env.render_str(r#"{{ is_port_available(port=0) }}"#, ());
assert!(result.is_err());
let result = env.render_str(r#"{{ is_port_available(port=70000) }}"#, ());
assert!(result.is_err());
}