use super::*;
use crate::engine::http::compute_http2_client_tuning;
use crate::engine::http::{extract_origin, strip_sensitive_headers};
#[test]
fn memory_budget_scales_by_available_memory() {
assert_eq!(compute_effective_connection_budget(32, 2048), 32);
assert_eq!(compute_effective_connection_budget(32, 900), 24);
assert_eq!(compute_effective_connection_budget(32, 400), 16);
assert_eq!(compute_effective_connection_budget(32, 200), 8);
assert_eq!(compute_effective_connection_budget(1, 200), 1);
}
#[test]
fn connection_cv_requires_meaningful_samples() {
assert!(compute_connection_cv(&[]).is_none());
assert!(compute_connection_cv(&[0.0, 0.0]).is_none());
let cv = compute_connection_cv(&[100.0, 100.0, 200.0, 200.0]).unwrap();
assert!(cv > 0.0);
}
#[test]
fn origin_key_normalizes_scheme_host_and_port() {
assert_eq!(origin_key("https://example.com/path"), "https://example.com:443");
assert_eq!(origin_key("http://example.com/test"), "http://example.com:80");
assert_eq!(origin_key("not a url"), "not a url");
}
#[test]
fn origin_phi_ratio_store_prunes_least_recently_used_entries() {
let mut store = OriginPhiRatioStore::default();
for idx in 0..ORIGIN_PHI_RATIO_CAPACITY {
store.update_origin_ratio(format!("https://host{idx}.example:443"), 1.1);
}
assert_eq!(store.len(), ORIGIN_PHI_RATIO_CAPACITY);
let keep_key = "https://host0.example:443";
assert_eq!(store.ratio_for_origin(keep_key), 1.1);
store.update_origin_ratio("https://new.example:443".to_string(), 1.9);
assert_eq!(store.len(), ORIGIN_PHI_RATIO_CAPACITY);
assert_eq!(store.current_ratio(keep_key), Some(1.1));
assert_eq!(store.current_ratio("https://new.example:443"), Some(1.9));
}
#[test]
fn protocol_thresholds_are_directionally_sensible() {
assert!(
protocol_effective_add_threshold(ProtocolFamily::Http1, 0.30)
> protocol_effective_add_threshold(ProtocolFamily::Http2, 0.30)
);
assert!(
compute_protocol_aware_steal_floor_bytes(ProtocolFamily::Http2, 0.80, 0.0)
< compute_protocol_aware_steal_floor_bytes(ProtocolFamily::Http1, 0.80, 0.0)
);
assert_eq!(protocol_prefetch_handshake_ms(ProtocolFamily::Http2), 250);
}
#[test]
fn http2_client_tuning_scales_with_expected_concurrency() {
let small = compute_http2_client_tuning(2);
let large = compute_http2_client_tuning(16);
assert!(large.http2_stream_window_bytes >= small.http2_stream_window_bytes);
assert!(large.http2_connection_window_bytes >= small.http2_connection_window_bytes);
assert!(large.http2_max_send_buffer_bytes >= small.http2_max_send_buffer_bytes);
}
#[test]
fn learned_http2_tuning_is_origin_scoped_and_pruned() {
let mut store = OriginH2TuningStore::default();
let tuning = learn_http2_client_tuning(4, 3, 120.0, 12.0 * MB as f64);
store.update_origin_tuning("https://example.com:443".to_string(), tuning);
assert_eq!(
store.current_tuning("https://example.com:443").unwrap().source,
H2TuningSource::LearnedOrigin
);
for idx in 0..ORIGIN_PHI_RATIO_CAPACITY {
store.update_origin_tuning(
format!("https://host{idx}.example:443"),
compute_http2_client_tuning(2),
);
}
assert!(store.len() <= ORIGIN_PHI_RATIO_CAPACITY);
}
#[test]
fn extract_origin_parses_scheme_host_port() {
assert_eq!(
extract_origin("https://example.com/file.zip"),
"https://example.com:443"
);
assert_eq!(
extract_origin("http://example.com:8080/file.zip"),
"http://example.com:8080"
);
assert_eq!(
extract_origin("https://cdn.example.com/path?a=1"),
"https://cdn.example.com:443"
);
assert_eq!(extract_origin("not a url"), "");
assert_eq!(
extract_origin("https://example.com:443/foo"),
extract_origin("https://example.com/bar"),
);
assert_ne!(
extract_origin("https://example.com/page"),
extract_origin("https://other.example.com/page"),
);
}
#[test]
fn strip_sensitive_headers_removes_auth_cookie_referer() {
use ::http::HeaderMap;
let mut headers = HeaderMap::new();
headers.insert("x-custom", "keep".parse().unwrap());
headers.insert(::http::header::AUTHORIZATION, "Bearer token".parse().unwrap());
headers.insert(::http::header::COOKIE, "session=abc".parse().unwrap());
headers.insert(::http::header::REFERER, "https://origin.com".parse().unwrap());
headers.insert(::http::header::ACCEPT, "text/html".parse().unwrap());
let safe = strip_sensitive_headers(&headers);
assert!(safe.get("x-custom").is_some());
assert!(safe.get(::http::header::ACCEPT).is_some());
assert!(safe.get(::http::header::AUTHORIZATION).is_none());
assert!(safe.get(::http::header::COOKIE).is_none());
assert!(safe.get(::http::header::REFERER).is_none());
assert_eq!(safe.len(), 2); }
#[test]
fn classify_response_body_returns_none_for_binary_data() {
assert!(classify_response_body(&[0u8; 100]).is_none());
assert!(classify_response_body(b"{\"key\": \"value\"}").is_none());
}
#[test]
fn classify_response_body_returns_none_for_short_data() {
assert!(classify_response_body(b"<html>").is_none());
}
#[test]
fn classify_response_body_detects_cloudflare_challenge() {
let body = b"<html><body>Checking your browser before accessing... <title>Attention Required! | Cloudflare</title></body></html>";
let result = classify_response_body(body);
assert!(matches!(result, Some(ChallengeKind::CloudflareChallenge)));
}
#[test]
fn classify_response_body_detects_captcha() {
let body = b"<html><body><div class=\"g-recaptcha\">Please complete the CAPTCHA challenge</div></body></html>";
let result = classify_response_body(body);
assert!(matches!(result, Some(ChallengeKind::CaptchaChallenge)));
}
#[test]
fn classify_response_body_returns_unexpected_html_for_unknown_html() {
let body = b"<html><body><h1>Welcome</h1><p>Some content here</p></body></html>";
let result = classify_response_body(body);
assert!(matches!(result, Some(ChallengeKind::UnexpectedHtml)));
}
#[test]
fn cookie_jar_match_url_filters_by_domain_path_and_secure() {
let mut jar = crate::service::CookieJar::new();
jar.insert(crate::service::CookieEntry::new("session", "abc", "example.com"));
jar.insert(crate::service::CookieEntry::new("tracking", "xyz", "ads.com"));
let url = url::Url::parse("https://example.com/page").unwrap();
let matched = jar.match_url(&url);
assert_eq!(matched.len(), 1);
assert_eq!(matched[0].name, "session");
let header_val = jar.header_value_for_url(&url);
assert_eq!(header_val, Some("session=abc".to_string()));
let other_url = url::Url::parse("https://other.com/page").unwrap();
assert!(jar.match_url(&other_url).is_empty());
}
#[test]
fn cookie_jar_header_value_for_url_returns_none_when_empty() {
let jar = crate::service::CookieJar::new();
let url = url::Url::parse("https://example.com").unwrap();
assert!(jar.header_value_for_url(&url).is_none());
}
#[test]
fn challenge_requires_browser_session_returns_false_for_non_aborting_types() {
assert!(!ChallengeKind::UnexpectedHtml.requires_browser_session());
assert!(!ChallengeKind::AuthInterstitial.requires_browser_session());
assert!(ChallengeKind::CloudflareChallenge.requires_browser_session());
assert!(ChallengeKind::CaptchaChallenge.requires_browser_session());
assert!(ChallengeKind::BrowserCheck.requires_browser_session());
}
#[test]
fn service_cookie_jar_merges_into_request_context_with_dedup() {
use crate::service::{CookieJar, CookieEntry, RequestContext};
use url::Url;
let mut jar = CookieJar::new();
jar.insert(CookieEntry::new("session", "abc", "example.com"));
jar.insert(CookieEntry::new("tracking", "xyz", "example.com"));
let mut ctx = RequestContext::new();
ctx.cookies = Some(vec![
CookieEntry::new("session", "override", "example.com"), CookieEntry::new("custom", "val", "example.com"),
]);
let url = Url::parse("https://example.com/page").unwrap();
let jar_cookies: Vec<CookieEntry> = jar.match_url(&url).into_iter().cloned().collect();
let mut existing = ctx.cookies.take().unwrap_or_default();
for c in jar_cookies {
if !existing.iter().any(|ec| ec.name == c.name && ec.domain == c.domain && ec.path == c.path) {
existing.push(c);
}
}
ctx.cookies = Some(existing);
let cookies = ctx.cookies.as_ref().unwrap();
assert_eq!(cookies.len(), 3, "should have custom + session(override) + tracking");
let session = cookies.iter().find(|c| c.name == "session").unwrap();
assert_eq!(
session.value, "override",
"per-request session cookie should take priority over jar session cookie"
);
assert!(cookies.iter().any(|c| c.name == "tracking"), "jar tracking cookie should be present");
assert!(cookies.iter().any(|c| c.name == "custom"), "per-request custom cookie should be present");
}