use super::*;
use rand::SeedableRng;
#[test]
fn probe_timeout_matches_the_adr_collection_ceiling() {
assert_eq!(PROBE_TIMEOUT, Duration::from_millis(500));
}
#[test]
fn outer_pull_deadline_exceeds_what_every_candidate_can_actually_cost() {
let attempts = u32::try_from(MAX_PROVIDER_ATTEMPTS).unwrap_or(u32::MAX);
for per_secs in [1_u64, 5, 20, 60] {
for stall_secs in [1_u64, 5, 20, 120] {
let per = Duration::from_secs(per_secs);
let stall = Duration::from_secs(stall_secs);
let outer = outer_pull_deadline(per, stall);
let worst_candidate = CHANNEL_OPEN_CALLER_BUDGET
.saturating_add(per)
.saturating_add(stall);
let all_candidates = worst_candidate.saturating_mul(attempts);
assert!(
outer > all_candidates,
"outer {outer:?} must exceed {MAX_PROVIDER_ATTEMPTS}×{worst_candidate:?} \
(channel open + stream open + stall), or the loop cannot reach the \
last candidate"
);
assert_eq!(outer, all_candidates + PULL_THROUGH_OUTER_SLACK);
}
}
}
#[test]
fn outer_pull_deadline_at_defaults_is_167_5s() {
let outer = outer_pull_deadline(Duration::from_secs(20), Duration::from_secs(20));
assert_eq!(
outer,
Duration::from_millis(167_500),
"(5s + 20s + 20s) × 3 + (500ms + 4 × 8s)"
);
}
#[test]
fn outer_pull_deadline_handles_edges() {
let attempts = u32::try_from(MAX_PROVIDER_ATTEMPTS).unwrap_or(u32::MAX);
assert_eq!(
outer_pull_deadline(Duration::ZERO, Duration::ZERO),
CHANNEL_OPEN_CALLER_BUDGET.saturating_mul(attempts) + PULL_THROUGH_OUTER_SLACK
);
assert_eq!(
outer_pull_deadline(Duration::MAX, Duration::ZERO),
Duration::MAX
);
assert_eq!(
outer_pull_deadline(Duration::ZERO, Duration::MAX),
Duration::MAX
);
}
#[test]
fn score_reputation_1_0_is_baseline() {
let s = compute_score(100, 10, 1.0);
assert!((s - 1000.0).abs() < 1e-9, "got {s}");
}
#[test]
fn score_reputation_0_5_is_4x_baseline() {
let baseline = compute_score(100, 10, 1.0);
let s = compute_score(100, 10, 0.5);
assert!(
(s / baseline - 4.0).abs() < 1e-3,
"got ratio {}",
s / baseline
);
}
#[test]
fn score_reputation_0_1_is_100x_baseline() {
let baseline = compute_score(100, 10, 1.0);
let s = compute_score(100, 10, 0.1);
assert!(
(s / baseline - 100.0).abs() < 1e-3,
"got ratio {}",
s / baseline
);
}
#[test]
fn score_reputation_0_0_clamps_to_floor() {
let at_zero = compute_score(100, 10, 0.0);
let at_floor = compute_score(100, 10, 0.1);
assert!((at_zero - at_floor).abs() < 1e-9);
}
#[test]
fn score_negative_reputation_clamps_to_floor() {
let s = compute_score(100, 10, -0.5);
let at_floor = compute_score(100, 10, 0.1);
assert!((s - at_floor).abs() < 1e-9);
}
#[test]
fn score_nan_reputation_clamps_to_floor() {
let s = compute_score(100, 10, f32::NAN);
let at_floor = compute_score(100, 10, 0.1);
assert!(s.is_finite(), "NaN reputation must not produce a NaN score");
assert!((s - at_floor).abs() < 1e-9);
}
#[test]
fn score_reputation_above_1_0_clamps_to_ceiling() {
let baseline = compute_score(100, 10, 1.0);
let above = compute_score(100, 10, 10.0);
assert!((above - baseline).abs() < 1e-9, "got {above} vs {baseline}");
}
#[test]
fn score_reputation_one_ulp_above_domain_clamps_to_ceiling() {
let baseline = compute_score(100, 10, 1.0);
let above = compute_score(100, 10, 1.0 + f32::EPSILON);
assert!((above - baseline).abs() < 1e-9, "got {above} vs {baseline}");
}
#[test]
fn score_infinite_reputation_clamps_to_ceiling() {
let baseline = compute_score(100, 10, 1.0);
let s = compute_score(100, 10, f32::INFINITY);
assert!(s.is_finite(), "got {s}");
assert!(
s > 0.0,
"infinite reputation must not score 0.0 and sort first"
);
assert!((s - baseline).abs() < 1e-9, "got {s} vs {baseline}");
}
#[test]
fn score_floor_value_is_zero_point_one() {
let at_floor = compute_score(100, 10, 0.0);
assert!((at_floor - 100_000.0).abs() < 1e-2, "got {at_floor}");
}
#[test]
fn score_handles_max_inputs_without_overflow() {
let s = compute_score(u64::MAX, u32::MAX, 1.0);
assert!(s.is_finite(), "got {s}");
}
fn make_candidate(node_id: u8, rate: u64, rtt: u32, rep: f32) -> Candidate {
Candidate {
node_id: [node_id; 32],
rate_per_mb: rate,
rtt_ms: rtt,
reputation: rep,
region: "US".to_string(),
stake: 0,
coverage: Coverage::empty(),
total_bytes_hint: None,
}
}
fn with_region(mut c: Candidate, region: &str) -> Candidate {
c.region = region.to_string();
c
}
fn with_stake(mut c: Candidate, stake: u64) -> Candidate {
c.stake = stake;
c
}
fn with_coverage(mut c: Candidate, coverage: Coverage) -> Candidate {
c.coverage = coverage;
c
}
#[test]
fn rank_candidates_carries_each_candidates_fresh_probe_coverage() {
let block0 = Coverage::from_block_indices(2, [0].into_iter());
let block1 = Coverage::from_block_indices(2, [1].into_iter());
let holder0 = with_coverage(make_candidate(1, 100, 10, 1.0), block0.clone());
let holder1 = with_coverage(make_candidate(2, 200, 10, 1.0), block1.clone());
let out = rank_candidates(vec![holder0, holder1]);
assert_eq!(out.len(), 2);
let ranked0 = out
.iter()
.find(|r| r.candidate.node_id[0] == 1)
.expect("holder0 present in ranked output");
let ranked1 = out
.iter()
.find(|r| r.candidate.node_id[0] == 2)
.expect("holder1 present in ranked output");
assert_eq!(ranked0.candidate.coverage, block0);
assert_eq!(ranked1.candidate.coverage, block1);
}
#[test]
fn higher_stake_wins_when_geo_tied() {
let small_stake = with_stake(with_region(make_candidate(1, 100, 10, 1.0), "US"), 1_000);
let big_stake = with_stake(with_region(make_candidate(2, 100, 10, 1.0), "US"), 10_000);
let out = rank_candidates(vec![small_stake, big_stake]);
assert_eq!(out.first().map(|r| r.candidate.node_id[0]), Some(2));
}
#[test]
fn tier_two_prunes_the_loser_then_tier_three_randomizes_the_survivors() {
let a = with_stake(with_region(make_candidate(1, 100, 10, 1.0), "US"), 1_000);
let b = with_stake(with_region(make_candidate(2, 100, 10, 1.0), "US"), 1_000);
let laggard = with_stake(with_region(make_candidate(3, 100, 10, 1.0), "US"), 0);
let mut winners = std::collections::HashSet::new();
for seed in 0u64..32 {
let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
let out = rank_candidates_with_rng(vec![a.clone(), b.clone(), laggard.clone()], &mut rng);
let first = out.first().map(|r| r.candidate.node_id[0]);
assert_ne!(
first,
Some(3),
"seed {seed}: the lower-stake candidate must never survive tier 2"
);
if let Some(id) = first {
winners.insert(id);
}
if winners.len() == 2 {
break;
}
}
assert_eq!(
winners.len(),
2,
"expected both top-stake candidates to win across 32 seeds, saw {winners:?}"
);
}
#[test]
fn higher_stake_wins_when_all_regions_unseen() {
let us_rich = with_stake(with_region(make_candidate(1, 100, 10, 1.0), "US"), 10_000);
let de_poor = with_stake(with_region(make_candidate(2, 100, 10, 1.0), "DE"), 1_000);
let out = rank_candidates(vec![us_rich, de_poor]);
assert_eq!(out.first().map(|r| r.candidate.node_id[0]), Some(1));
}
#[test]
fn rank_empty_returns_empty() {
let out = rank_candidates(vec![]);
assert!(out.is_empty());
}
#[test]
fn rank_single_candidate_returns_it() {
let c = make_candidate(1, 100, 10, 1.0);
let out = rank_candidates(vec![c.clone()]);
assert_eq!(out.len(), 1);
assert_eq!(out.first().map(|r| r.candidate.node_id), Some(c.node_id));
}
#[test]
fn rank_orders_by_score_ascending() {
let c_dear = make_candidate(3, 100, 10, 1.0);
let c_cheap = make_candidate(1, 1, 10, 1.0);
let c_mid = make_candidate(2, 10, 10, 1.0);
let out = rank_candidates(vec![c_dear, c_cheap, c_mid]);
let ids: Vec<u8> = out.iter().map(|r| r.candidate.node_id[0]).collect();
assert_eq!(ids, vec![1, 2, 3]);
}
#[test]
fn within_1_percent_counts_as_tied() {
let high_score = with_stake(with_region(make_candidate(1, 100, 10, 1.0), "US"), 1_000); let low_score = with_stake(with_region(make_candidate(2, 1005, 1, 1.0), "US"), 10_000); let out = rank_candidates(vec![high_score, low_score]);
assert_eq!(out.first().map(|r| r.candidate.node_id[0]), Some(2));
}
#[test]
fn outside_1_percent_does_not_tie_break() {
let cheap = with_stake(with_region(make_candidate(1, 100, 10, 1.0), "US"), 1_000); let dear = with_stake(with_region(make_candidate(2, 1020, 1, 1.0), "US"), 10_000); let out = rank_candidates(vec![cheap, dear]);
assert_eq!(out.first().map(|r| r.candidate.node_id[0]), Some(1));
}
#[test]
fn at_1_percent_boundary_counts_as_tied() {
let high_score = with_stake(with_region(make_candidate(1, 100, 10, 1.0), "US"), 1_000); let low_score = with_stake(with_region(make_candidate(2, 1010, 1, 1.0), "US"), 10_000); let out = rank_candidates(vec![high_score, low_score]);
assert_eq!(out.first().map(|r| r.candidate.node_id[0]), Some(2));
}
#[test]
fn zero_score_candidates_group_together_for_tie_break() {
let zero_low_stake = with_stake(with_region(make_candidate(1, 0, 10, 1.0), "US"), 1_000); let zero_high_stake = with_stake(with_region(make_candidate(2, 0, 10, 1.0), "US"), 10_000); let out = rank_candidates(vec![zero_low_stake, zero_high_stake]);
assert_eq!(out.first().map(|r| r.candidate.node_id[0]), Some(2));
}
#[test]
fn zero_score_beats_positive_score() {
let zero = make_candidate(1, 0, 10, 1.0); let positive = make_candidate(2, 1, 1, 1.0); let out = rank_candidates(vec![positive, zero]);
assert_eq!(out.first().map(|r| r.candidate.node_id[0]), Some(1));
}
#[test]
fn sub_millisecond_rtt_keeps_the_price_signal() {
for _ in 0..64 {
let dear = make_candidate(1, 10, 0, 0.5);
let cheap = make_candidate(2, 5, 0, 0.5);
let out = rank_candidates(vec![dear, cheap]);
assert_eq!(out.first().map(|r| r.candidate.node_id[0]), Some(2));
}
}
#[test]
fn sub_millisecond_rtt_does_not_outrank_a_cheaper_peer() {
let dear_fast = make_candidate(1, 10, 0, 0.5);
let cheap = make_candidate(2, 5, 1, 0.5);
let out = rank_candidates(vec![dear_fast, cheap]);
assert_eq!(out.first().map(|r| r.candidate.node_id[0]), Some(2));
}
#[test]
fn geo_diversity_prefers_unseen_region() {
let us1 = with_region(make_candidate(1, 100, 10, 1.0), "US");
let us2 = with_region(make_candidate(2, 100, 10, 1.0), "US");
let de = with_region(make_candidate(3, 100, 10, 1.0), "DE");
let mut tested_first_us = false;
for seed in 0u64..32 {
let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
let out = rank_candidates_with_rng(vec![us1.clone(), us2.clone(), de.clone()], &mut rng);
let regions: Vec<String> = out.iter().map(|r| r.candidate.region.clone()).collect();
assert_eq!(regions.len(), 3);
if regions.first().map(String::as_str) == Some("US") {
assert_eq!(
regions.get(1).map(String::as_str),
Some("DE"),
"seed {seed}: after first US pick, geo tier must surface DE next"
);
tested_first_us = true;
}
}
assert!(
tested_first_us,
"expected at least one seed in 0..32 to produce first-pick=US"
);
}
#[test]
fn geo_diversity_only_applies_within_tie_group() {
let us = with_region(make_candidate(1, 1000, 10, 1.0), "US"); let de = with_region(make_candidate(2, 100, 10, 1.0), "DE"); let out = rank_candidates(vec![us, de]);
assert_eq!(out.first().map(|r| r.candidate.node_id[0]), Some(2));
}
#[test]
fn random_breaks_full_ties_deterministically_with_seed() {
let a = make_candidate(1, 100, 10, 1.0);
let b = make_candidate(2, 100, 10, 1.0);
let mut rng_1 = rand::rngs::StdRng::seed_from_u64(42);
let out_1 = rank_candidates_with_rng(vec![a.clone(), b.clone()], &mut rng_1);
let mut rng_2 = rand::rngs::StdRng::seed_from_u64(42);
let out_2 = rank_candidates_with_rng(vec![a, b], &mut rng_2);
let ids_1: Vec<u8> = out_1.iter().map(|r| r.candidate.node_id[0]).collect();
let ids_2: Vec<u8> = out_2.iter().map(|r| r.candidate.node_id[0]).collect();
assert_eq!(ids_1, ids_2, "same seed must produce same order");
}
#[test]
fn random_tier_yields_different_orders_for_different_seeds() {
let a = make_candidate(1, 100, 10, 1.0);
let b = make_candidate(2, 100, 10, 1.0);
let mut saw_ab = false;
let mut saw_ba = false;
for seed in 0u64..32 {
let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
let out = rank_candidates_with_rng(vec![a.clone(), b.clone()], &mut rng);
match out.first().map(|r| r.candidate.node_id[0]) {
Some(1) => saw_ab = true,
Some(2) => saw_ba = true,
_ => {}
}
if saw_ab && saw_ba {
break;
}
}
assert!(saw_ab && saw_ba, "expected both orderings across 32 seeds");
}
#[test]
fn geo_tier_resets_between_tie_groups() {
let g1_us = with_region(make_candidate(1, 1, 10, 1.0), "US");
let g1_de = with_region(make_candidate(2, 1, 10, 1.0), "DE");
let g2_us = with_region(make_candidate(3, 100, 10, 1.0), "US");
let g2_jp = with_region(make_candidate(4, 100, 10, 1.0), "JP");
let mut saw_g2_us_after_g1_us = false;
for seed in 0u64..64 {
let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
let out = rank_candidates_with_rng(
vec![g1_us.clone(), g1_de.clone(), g2_us.clone(), g2_jp.clone()],
&mut rng,
);
let regions: Vec<String> = out.iter().map(|r| r.candidate.region.clone()).collect();
let g1_first_us = regions.first().map(String::as_str) == Some("US");
let g2_first_us = regions.get(2).map(String::as_str) == Some("US");
if g1_first_us && g2_first_us {
saw_g2_us_after_g1_us = true;
break;
}
}
assert!(
saw_g2_us_after_g1_us,
"geo tier should reset between tie groups; without reset, group 2 would never pick US first after group 1 picked US"
);
}
#[test]
fn rank_candidates_keeps_negative_reputation_candidate() {
let neg = make_candidate(1, 100, 10, -0.5);
let out = rank_candidates(vec![neg]);
assert_eq!(out.len(), 1);
assert_eq!(out.first().map(|r| r.candidate.node_id[0]), Some(1));
}
#[test]
fn rank_candidates_denies_out_of_domain_reputation_the_top_slot() {
let bogus = make_candidate(1, 100, 10, f32::INFINITY);
let honest = make_candidate(2, 50, 10, 1.0);
let out = rank_candidates(vec![bogus, honest]);
assert_eq!(out.len(), 2, "reputation is a weight, never a veto");
assert_eq!(out.first().map(|r| r.candidate.node_id[0]), Some(2));
}