use foundation_proxy::config::{BackendState, BackendTarget, ServiceConfig};
use foundation_proxy::runtime::ServiceRuntime;
fn service_with(backends: Vec<BackendTarget>) -> ServiceRuntime {
let mut cfg = ServiceConfig::new("svc", "svc.local");
cfg.backends = backends;
ServiceRuntime::new(cfg)
}
#[test]
fn test_weighted_round_robin_distribution() {
let svc = service_with(vec![
BackendTarget::new("http://a").with_weight(3),
BackendTarget::new("http://b").with_weight(1),
]);
let mut count_a = 0;
let mut count_b = 0;
let mut leases = Vec::new();
for _ in 0..8 {
let lease = svc.pick().expect("a backend is eligible");
match lease.backend().url() {
"http://a" => count_a += 1,
"http://b" => count_b += 1,
other => panic!("unexpected backend {other}"),
}
leases.push(lease);
}
assert_eq!(count_a, 6, "weight-3 backend should get 6/8");
assert_eq!(count_b, 2, "weight-1 backend should get 2/8");
}
#[test]
fn test_eligibility_excludes_unhealthy_and_paused() {
let svc = service_with(vec![
BackendTarget::new("http://a"),
BackendTarget::new("http://b"),
]);
let backends = svc.backends();
backends[0].set_healthy(false);
for _ in 0..4 {
assert_eq!(svc.pick().unwrap().backend().url(), "http://b");
}
backends[1].set_state(BackendState::Paused);
assert!(svc.pick().is_none(), "no eligible backend → None (handler 503s)");
backends[0].set_healthy(true);
assert_eq!(svc.pick().unwrap().backend().url(), "http://a");
}
#[test]
fn test_max_connections_enforced() {
let svc = service_with(vec![
BackendTarget::new("http://a").with_max_connections(1),
BackendTarget::new("http://b").with_max_connections(1),
]);
let l1 = svc.pick().expect("first pick");
let l2 = svc.pick().expect("second pick spills to the other backend");
assert_ne!(
l1.backend().url(),
l2.backend().url(),
"each backend allows only one in-flight"
);
assert!(svc.pick().is_none(), "both at capacity → None");
let freed = l1.backend().url().to_string();
drop(l1);
let l3 = svc.pick().expect("a slot freed up");
assert_eq!(l3.backend().url(), freed);
drop((l2, l3));
}
#[test]
fn test_inflight_refcount_tracks_leases() {
let svc = service_with(vec![BackendTarget::new("http://a")]);
assert_eq!(svc.backends()[0].inflight(), 0);
let lease = svc.pick().unwrap();
assert_eq!(svc.backends()[0].inflight(), 1, "lease increments in-flight");
drop(lease);
assert_eq!(svc.backends()[0].inflight(), 0, "drop decrements in-flight");
}