use base64::{engine::general_purpose, Engine as _};
use std::collections::HashMap;
use crate::modules::pxutils::*;
#[test]
fn parse_cookie_header_splits_pairs() {
let cookies = parse_cookie_header("cookie1=value1; cookie3=value3");
assert_eq!(cookies.get("cookie1").map(String::as_str), Some("value1"));
assert_eq!(cookies.get("cookie3").map(String::as_str), Some("value3"));
}
#[test]
fn merge_cookie_headers_custom_overrides_cookie() {
let cookie = parse_cookie_header("cookie1=value1; cookie3=value3");
let custom = parse_cookie_header("cookie2=value2; cookie3=value4");
let merged = merge_cookie_headers(cookie, custom);
assert_eq!(merged.get("cookie1").map(String::as_str), Some("value1"));
assert_eq!(merged.get("cookie2").map(String::as_str), Some("value2"));
assert_eq!(merged.get("cookie3").map(String::as_str), Some("value4"));
}
#[test]
fn extract_cookie_value_reads_from_map() {
let mut cookies = HashMap::new();
cookies.insert("_pxvid".to_string(), "vid-123".to_string());
assert_eq!(extract_cookie_value(&cookies, "_pxvid"), "vid-123");
assert!(extract_cookie_value(&cookies, "missing").is_empty());
}
#[test]
fn build_merged_request_cookies_without_custom_header() {
let merged = build_merged_request_cookies("cookie1=value1; _pxvid=vid1", "");
assert_eq!(merged.get("cookie1").map(String::as_str), Some("value1"));
assert_eq!(merged.get("_pxvid").map(String::as_str), Some("vid1"));
}
#[test]
fn decode_jwt_payload_reads_url_safe_segment() {
let payload = serde_json::json!({"user": {"id": "abc"}});
let encoded = general_purpose::URL_SAFE_NO_PAD.encode(payload.to_string().as_bytes());
let token = format!("hdr.{encoded}.sig");
let decoded = decode_jwt_payload(&token);
assert_eq!(
decoded
.as_ref()
.and_then(|v| v.get("user"))
.and_then(|v| v.get("id"))
.and_then(serde_json::Value::as_str),
Some("abc")
);
}
#[test]
fn get_value_at_path_supports_dot_notation() {
let value = serde_json::json!({"profile": {"id": "value_1"}});
assert_eq!(
get_value_at_path(&value, "profile.id").and_then(serde_json::Value::as_str),
Some("value_1")
);
}
#[test]
fn is_valid_uuid_accepts_lowercase_v4() {
assert!(is_valid_uuid("550e8400-e29b-41d4-a716-446655440000"));
}
#[test]
fn is_valid_uuid_rejects_empty_and_missing() {
assert!(!is_valid_uuid(""));
}
#[test]
fn is_valid_uuid_rejects_uppercase() {
assert!(!is_valid_uuid("550E8400-E29B-41D4-A716-446655440000"));
}
#[test]
fn is_valid_uuid_rejects_malformed_values() {
assert!(!is_valid_uuid("vid-123"));
assert!(!is_valid_uuid("not-a-uuid"));
}
#[test]
fn is_valid_uuid_rejects_invalid_version_and_variant() {
assert!(!is_valid_uuid("550e8400-e29b-61d4-a716-446655440000"));
assert!(!is_valid_uuid("550e8400-e29b-41d4-c716-446655440000"));
}
#[test]
fn build_merged_request_cookies_derives_px_fields() {
let merged = build_merged_request_cookies(
"cookie1=value1; _pxvid=vid-cookie; _pxhd=hd-cookie; pxcts=cts-cookie; _pxac=ac-cookie",
"_pxvid=vid-custom; cookie3=value4",
);
assert_eq!(extract_cookie_value(&merged, "_pxvid"), "vid-custom");
assert_eq!(extract_cookie_value(&merged, "_pxhd"), "hd-cookie");
assert_eq!(extract_cookie_value(&merged, "pxcts"), "cts-cookie");
assert_eq!(extract_cookie_value(&merged, "_pxac"), "ac-cookie");
assert_eq!(merged.get("cookie3").map(String::as_str), Some("value4"));
assert_eq!(merged.len(), 6);
}
#[test]
fn first_ip_from_comma_separated_header_uses_first_nonempty() {
assert_eq!(
first_ip_from_header_value(" 203.0.113.1 , 198.51.100.2 "),
Some("203.0.113.1".to_owned())
);
}
#[test]
fn first_ip_from_empty_value_returns_none() {
assert_eq!(first_ip_from_header_value(""), None);
assert_eq!(first_ip_from_header_value(" , "), None);
}
#[test]
fn extract_ip_prefers_first_configured_header() {
let headers_map = HashMap::from([
("True-Client-IP", "1.1.1.1"),
("X-Forwarded-For", "2.2.2.2"),
]);
let ip_headers = vec!["True-Client-IP".to_owned(), "X-Forwarded-For".to_owned()];
assert_eq!(
extract_ip_from_header_values(&ip_headers, |name| {
headers_map.get(name).map(|value| (*value).to_owned())
}),
Some("1.1.1.1".to_owned())
);
}
#[test]
fn extract_ip_skips_missing_and_empty_headers() {
let headers_map = HashMap::from([("X-Forwarded-For", " , 203.0.113.9 ")]);
let ip_headers = vec!["True-Client-IP".to_owned(), "X-Forwarded-For".to_owned()];
assert_eq!(
extract_ip_from_header_values(&ip_headers, |name| {
headers_map.get(name).map(|value| (*value).to_owned())
}),
Some("203.0.113.9".to_owned())
);
}
#[test]
fn extract_ip_returns_none_when_no_configured_header_has_ip() {
let headers_map: HashMap<&str, &str> = HashMap::new();
let ip_headers = vec!["X-Forwarded-For".to_owned()];
assert_eq!(
extract_ip_from_header_values(&ip_headers, |name| {
headers_map.get(name).map(|value| (*value).to_owned())
}),
None
);
}
#[test]
fn ip_matches_filter_exact_ipv4() {
assert!(ip_matches_filter("10.0.0.1", "10.0.0.1"));
assert!(!ip_matches_filter("10.0.0.2", "10.0.0.1"));
}
#[test]
fn ip_matches_filter_cidr_8() {
assert!(ip_matches_filter("10.1.2.3", "10.0.0.0/8"));
assert!(!ip_matches_filter("11.1.2.3", "10.0.0.0/8"));
}
#[test]
fn ip_matches_filter_cidr_16() {
assert!(ip_matches_filter("10.1.2.3", "10.1.0.0/16"));
assert!(!ip_matches_filter("10.2.2.3", "10.1.0.0/16"));
}
#[test]
fn ip_matches_filter_cidr_24() {
assert!(ip_matches_filter("10.1.2.3", "10.1.2.0/24"));
assert!(!ip_matches_filter("10.1.3.3", "10.1.2.0/24"));
}
#[test]
fn ip_matches_filter_cidr_32() {
assert!(ip_matches_filter("10.1.2.3", "10.1.2.3/32"));
assert!(!ip_matches_filter("10.1.2.4", "10.1.2.3/32"));
}
#[test]
fn ip_matches_filter_cidr_0() {
assert!(ip_matches_filter("10.1.2.3", "0.0.0.0/0"));
assert!(ip_matches_filter("203.0.113.9", "0.0.0.0/0"));
}
#[test]
fn ip_matches_filter_rejects_unsupported_cidr_prefix() {
assert!(!ip_matches_filter("10.1.2.3", "10.1.0.0/12"));
}
#[test]
fn ip_matches_filter_rejects_invalid_values() {
assert!(!ip_matches_filter("not-an-ip", "10.0.0.1"));
assert!(!ip_matches_filter("10.0.0.1", "not-an-ip"));
assert!(!ip_matches_filter("10.0.0.1", "10.0.0.0/99"));
}