use crate::features::mutation::SimpleRng;
use crate::mesh::NodeId;
use std::net::SocketAddr;
const TRANSPORT_MAGIC: &[u8; 4] = b"UTPT";
const CONFIRM_MAGIC: &[u8; 4] = b"UTPC";
const TRANSPORT_VERSION: u8 = 1;
const TRANSPORT_HEADER_LEN: usize = 4 + 1 + 8;
const CONFIRM_FRAME_LEN: usize = 4 + 1 + 8 + 1;
const MAX_PAYLOAD: usize = 100_000_000;
#[cfg(not(target_arch = "wasm32"))]
const HANDSHAKE_TIMEOUT_SECS: u64 = 30;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ConfirmStatus {
Accepted,
Refused,
}
impl ConfirmStatus {
fn as_u8(self) -> u8 {
match self {
ConfirmStatus::Accepted => 1,
ConfirmStatus::Refused => 0,
}
}
fn from_u8(v: u8) -> Option<Self> {
match v {
1 => Some(ConfirmStatus::Accepted),
0 => Some(ConfirmStatus::Refused),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ConfirmOutcome {
Accepted,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TransportError {
BadMagic,
BadVersion,
TooLarge,
Truncated,
Refused,
Timeout,
Connect(String),
Io(String),
MalformedConfirm,
Unavailable,
}
impl std::fmt::Display for TransportError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TransportError::BadMagic => write!(f, "bad magic"),
TransportError::BadVersion => write!(f, "unsupported version"),
TransportError::TooLarge => write!(f, "payload exceeds cap"),
TransportError::Truncated => write!(f, "truncated frame"),
TransportError::Refused => write!(f, "destination refused (no headroom)"),
TransportError::Timeout => write!(f, "confirm timed out"),
TransportError::Connect(e) => write!(f, "connect: {}", e),
TransportError::Io(e) => write!(f, "io: {}", e),
TransportError::MalformedConfirm => write!(f, "malformed confirm"),
TransportError::Unavailable => write!(f, "transport unavailable on this platform"),
}
}
}
pub fn encode_transport_frame(payload: &[u8]) -> Result<Vec<u8>, TransportError> {
if payload.len() > MAX_PAYLOAD {
return Err(TransportError::TooLarge);
}
let mut buf = Vec::with_capacity(TRANSPORT_HEADER_LEN + payload.len());
buf.extend_from_slice(TRANSPORT_MAGIC);
buf.push(TRANSPORT_VERSION);
buf.extend_from_slice(&(payload.len() as u64).to_be_bytes());
buf.extend_from_slice(payload);
Ok(buf)
}
pub fn decode_transport_frame(data: &[u8]) -> Result<&[u8], TransportError> {
if data.len() < TRANSPORT_HEADER_LEN {
return Err(TransportError::Truncated);
}
if &data[0..4] != TRANSPORT_MAGIC {
return Err(TransportError::BadMagic);
}
if data[4] != TRANSPORT_VERSION {
return Err(TransportError::BadVersion);
}
let len = u64::from_be_bytes(data[5..13].try_into().unwrap()) as usize;
if len > MAX_PAYLOAD {
return Err(TransportError::TooLarge);
}
let end = TRANSPORT_HEADER_LEN + len;
if data.len() < end {
return Err(TransportError::Truncated);
}
Ok(&data[TRANSPORT_HEADER_LEN..end])
}
pub fn encode_confirm_frame(node_id: &NodeId, status: ConfirmStatus) -> Vec<u8> {
let mut buf = Vec::with_capacity(CONFIRM_FRAME_LEN);
buf.extend_from_slice(CONFIRM_MAGIC);
buf.push(TRANSPORT_VERSION);
buf.extend_from_slice(node_id);
buf.push(status.as_u8());
buf
}
pub fn decode_confirm_frame(data: &[u8]) -> Result<(NodeId, ConfirmStatus), TransportError> {
if data.len() < CONFIRM_FRAME_LEN {
return Err(TransportError::Truncated);
}
if &data[0..4] != CONFIRM_MAGIC {
return Err(TransportError::BadMagic);
}
if data[4] != TRANSPORT_VERSION {
return Err(TransportError::BadVersion);
}
let mut node_id = [0u8; 8];
node_id.copy_from_slice(&data[5..13]);
let status = ConfirmStatus::from_u8(data[13]).ok_or(TransportError::MalformedConfirm)?;
Ok((node_id, status))
}
pub struct HandleResult {
pub confirm_frame: Vec<u8>,
pub snapshot: Option<crate::persist::VmSnapshot>,
}
impl HandleResult {
pub fn accepted(&self) -> bool {
self.snapshot.is_some()
}
}
pub fn handle_transport_frame(
frame: &[u8],
dest_res: &crate::resources::HostResources,
) -> Result<HandleResult, TransportError> {
let payload = decode_transport_frame(frame)?;
let snap = match crate::persist::deserialize_snapshot(payload) {
Some(s) => s,
None => {
return Ok(HandleResult {
confirm_frame: encode_confirm_frame(&[0u8; 8], ConfirmStatus::Refused),
snapshot: None,
});
}
};
if dest_res.has_admission_headroom() {
let confirm_frame = encode_confirm_frame(&snap.node_id, ConfirmStatus::Accepted);
Ok(HandleResult {
confirm_frame,
snapshot: Some(snap),
})
} else {
let confirm_frame = encode_confirm_frame(&snap.node_id, ConfirmStatus::Refused);
Ok(HandleResult {
confirm_frame,
snapshot: None,
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Candidate {
pub headroom_pct: u8,
pub addr: SocketAddr,
}
pub fn choose_destination<'a>(
candidates: &'a [Candidate],
rng: &mut SimpleRng,
) -> Option<&'a Candidate> {
let max_abundant = candidates
.iter()
.map(|c| c.headroom_pct)
.filter(|&h| crate::resources::headroom_pct_abundant(h))
.max();
if let Some(max) = max_abundant {
let mut chosen: Option<&Candidate> = None;
let mut seen = 0usize;
for c in candidates.iter().filter(|c| c.headroom_pct == max) {
seen += 1;
if rng.next_usize(seen) == 0 {
chosen = Some(c);
}
}
return chosen;
}
candidates
.iter()
.find(|c| crate::resources::headroom_pct_sufficient(c.headroom_pct))
}
pub fn is_mislocated(local: &crate::resources::HostResources) -> bool {
!local.has_headroom()
}
pub fn should_release(outcome: &Result<ConfirmOutcome, TransportError>) -> bool {
matches!(outcome, Ok(ConfirmOutcome::Accepted))
}
#[derive(Debug)]
pub enum TransportAttempt {
NotMislocated,
NoDestination,
CannotAfford,
Attempted(Result<ConfirmOutcome, TransportError>),
}
pub fn attempt_transport<S>(
local: &crate::resources::HostResources,
candidates: &[Candidate],
can_afford: bool,
tie_seed: u64,
send: S,
) -> TransportAttempt
where
S: FnOnce(SocketAddr) -> Result<ConfirmOutcome, TransportError>,
{
if !is_mislocated(local) {
return TransportAttempt::NotMislocated;
}
let mut rng = SimpleRng::new(tie_seed);
let dest = match choose_destination(candidates, &mut rng) {
Some(d) => d,
None => return TransportAttempt::NoDestination,
};
if !can_afford {
return TransportAttempt::CannotAfford;
}
TransportAttempt::Attempted(send(dest.addr))
}
#[cfg(not(target_arch = "wasm32"))]
pub fn send_transport(addr: &str, payload: &[u8]) -> Result<ConfirmOutcome, TransportError> {
use std::io::{Read, Write};
use std::net::TcpStream;
use std::time::Duration;
let frame = encode_transport_frame(payload)?;
let mut stream =
TcpStream::connect(addr).map_err(|e| TransportError::Connect(e.to_string()))?;
let timeout = Some(Duration::from_secs(HANDSHAKE_TIMEOUT_SECS));
stream.set_write_timeout(timeout).ok();
stream.set_read_timeout(timeout).ok();
stream
.write_all(&frame)
.map_err(|e| io_to_transport_err(&e))?;
let mut buf = [0u8; CONFIRM_FRAME_LEN];
stream
.read_exact(&mut buf)
.map_err(|e| io_to_transport_err(&e))?;
let (_echoed_id, status) = decode_confirm_frame(&buf)?;
match status {
ConfirmStatus::Accepted => Ok(ConfirmOutcome::Accepted),
ConfirmStatus::Refused => Err(TransportError::Refused),
}
}
#[cfg(not(target_arch = "wasm32"))]
fn io_to_transport_err(e: &std::io::Error) -> TransportError {
use std::io::ErrorKind;
match e.kind() {
ErrorKind::WouldBlock | ErrorKind::TimedOut => TransportError::Timeout,
_ => TransportError::Io(e.to_string()),
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn start_transport_listener(
port: u16,
) -> Result<std::sync::mpsc::Receiver<crate::persist::VmSnapshot>, TransportError> {
use std::io::{Read, Write};
use std::net::TcpListener;
use std::time::Duration;
let listener = TcpListener::bind(format!("0.0.0.0:{}", port))
.map_err(|e| TransportError::Io(format!("bind {}: {}", port, e)))?;
let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
for mut stream in listener.incoming().flatten() {
let timeout = Some(Duration::from_secs(HANDSHAKE_TIMEOUT_SECS));
stream.set_read_timeout(timeout).ok();
stream.set_write_timeout(timeout).ok();
let mut header = [0u8; TRANSPORT_HEADER_LEN];
if stream.read_exact(&mut header).is_err() {
continue;
}
if &header[0..4] != TRANSPORT_MAGIC || header[4] != TRANSPORT_VERSION {
continue;
}
let len = u64::from_be_bytes(header[5..13].try_into().unwrap()) as usize;
if len > MAX_PAYLOAD {
continue;
}
let mut payload = vec![0u8; len];
if stream.read_exact(&mut payload).is_err() {
continue;
}
let mut frame = Vec::with_capacity(TRANSPORT_HEADER_LEN + len);
frame.extend_from_slice(&header);
frame.extend_from_slice(&payload);
let res = crate::resources::HostResources::measure();
if let Ok(result) = handle_transport_frame(&frame, &res) {
let _ = stream.write_all(&result.confirm_frame);
if let Some(snap) = result.snapshot {
let _ = tx.send(snap);
}
}
}
});
Ok(rx)
}
#[cfg(target_arch = "wasm32")]
pub fn send_transport(_addr: &str, _payload: &[u8]) -> Result<ConfirmOutcome, TransportError> {
Err(TransportError::Unavailable)
}
#[cfg(target_arch = "wasm32")]
pub fn start_transport_listener(_port: u16) -> Result<(), TransportError> {
Err(TransportError::Unavailable)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::features::fitness::FitnessTracker;
use crate::goals::GoalRegistry;
use crate::persist::{deserialize_snapshot, serialize_snapshot, VmSnapshot};
use crate::resources::HostResources;
use crate::types::{Entry, Instruction};
fn sample_snapshot() -> VmSnapshot {
let mut fitness = FitnessTracker::new();
fitness.score = 4242;
fitness.tasks_completed = 7;
fitness.evolution_count = 3;
let dictionary = vec![
Entry {
name: "SOL-ANTIBODY".to_string(),
immediate: false,
hidden: false,
body: vec![Instruction::Literal(42), Instruction::Primitive(3)],
},
Entry {
name: "DOUBLE".to_string(),
immediate: false,
hidden: false,
body: vec![Instruction::Literal(2), Instruction::Primitive(5)],
},
];
VmSnapshot {
node_id: [1, 2, 3, 4, 5, 6, 7, 8],
dictionary,
memory: vec![0i64; 65536],
here: 0,
goals: GoalRegistry::empty(),
fitness,
code_strings: vec![
": SOL-ANTIBODY 42 . ;".to_string(),
": DOUBLE 2 * ;".to_string(),
],
}
}
fn available() -> HostResources {
HostResources::from_parts(1000, 500, 0.0, 4)
}
fn over_ceiling() -> HostResources {
HostResources::from_parts(1000, 50, 0.0, 4)
}
fn near_ceiling() -> HostResources {
HostResources::from_parts(1000, 220, 0.0, 4)
}
#[test]
fn transport_frame_round_trips() {
let payload = b"the complete self as USAV bytes";
let frame = encode_transport_frame(payload).unwrap();
let decoded = decode_transport_frame(&frame).unwrap();
assert_eq!(decoded, payload);
}
#[test]
fn confirm_frame_round_trips() {
let id = [9, 8, 7, 6, 5, 4, 3, 2];
for status in [ConfirmStatus::Accepted, ConfirmStatus::Refused] {
let frame = encode_confirm_frame(&id, status);
let (decoded_id, decoded_status) = decode_confirm_frame(&frame).unwrap();
assert_eq!(decoded_id, id);
assert_eq!(decoded_status, status);
}
}
#[test]
fn transport_frame_rejects_bad_magic() {
let mut frame = encode_transport_frame(b"x").unwrap();
frame[0] = b'Z';
assert_eq!(decode_transport_frame(&frame), Err(TransportError::BadMagic));
}
#[test]
fn transport_frame_rejects_bad_version() {
let mut frame = encode_transport_frame(b"x").unwrap();
frame[4] = 99;
assert_eq!(
decode_transport_frame(&frame),
Err(TransportError::BadVersion)
);
}
#[test]
fn transport_frame_rejects_truncated() {
let frame = encode_transport_frame(b"hello world").unwrap();
let truncated = &frame[..TRANSPORT_HEADER_LEN + 3];
assert_eq!(
decode_transport_frame(truncated),
Err(TransportError::Truncated)
);
assert_eq!(
decode_transport_frame(&frame[..4]),
Err(TransportError::Truncated)
);
}
#[test]
fn transport_frame_rejects_over_cap_declared_length() {
let mut frame = Vec::new();
frame.extend_from_slice(TRANSPORT_MAGIC);
frame.push(TRANSPORT_VERSION);
frame.extend_from_slice(&((MAX_PAYLOAD as u64) + 1).to_be_bytes());
assert_eq!(decode_transport_frame(&frame), Err(TransportError::TooLarge));
}
#[test]
fn encode_rejects_over_cap_payload() {
assert!(encode_transport_frame(b"normal").is_ok());
}
#[test]
fn confirm_frame_rejects_bad_magic_and_version() {
let id = [0u8; 8];
let mut frame = encode_confirm_frame(&id, ConfirmStatus::Accepted);
frame[0] = b'Z';
assert_eq!(decode_confirm_frame(&frame), Err(TransportError::BadMagic));
let mut frame = encode_confirm_frame(&id, ConfirmStatus::Accepted);
frame[4] = 99;
assert_eq!(decode_confirm_frame(&frame), Err(TransportError::BadVersion));
let mut frame = encode_confirm_frame(&id, ConfirmStatus::Accepted);
frame[13] = 42;
assert_eq!(
decode_confirm_frame(&frame),
Err(TransportError::MalformedConfirm)
);
assert_eq!(
decode_confirm_frame(&frame[..5]),
Err(TransportError::Truncated)
);
}
#[test]
fn complete_self_survives_encode_decode_deserialize() {
let snap = sample_snapshot();
let usav = serialize_snapshot(&snap);
let frame = encode_transport_frame(&usav).unwrap();
let payload = decode_transport_frame(&frame).unwrap();
let landed = deserialize_snapshot(payload).expect("USAV deserializes");
assert_eq!(landed.node_id, [1, 2, 3, 4, 5, 6, 7, 8]);
assert!(
landed.dictionary.iter().any(|e| e.name == "SOL-ANTIBODY"),
"evolved SOL-* antibody must survive transport"
);
let antibody = landed
.dictionary
.iter()
.find(|e| e.name == "SOL-ANTIBODY")
.unwrap();
assert_eq!(antibody.body.len(), 2);
assert_eq!(landed.fitness.score, 4242);
assert_eq!(landed.fitness.tasks_completed, 7);
assert_eq!(landed.fitness.evolution_count, 3);
assert_eq!(
landed.code_strings,
vec![
": SOL-ANTIBODY 42 . ;".to_string(),
": DOUBLE 2 * ;".to_string()
]
);
}
#[test]
fn handle_accepts_with_headroom_and_returns_snapshot() {
let usav = serialize_snapshot(&sample_snapshot());
let frame = encode_transport_frame(&usav).unwrap();
let result = handle_transport_frame(&frame, &available()).unwrap();
assert!(result.accepted());
let snap = result.snapshot.expect("accepted → snapshot handed back");
assert_eq!(snap.node_id, [1, 2, 3, 4, 5, 6, 7, 8]);
let (id, status) = decode_confirm_frame(&result.confirm_frame).unwrap();
assert_eq!(id, [1, 2, 3, 4, 5, 6, 7, 8]);
assert_eq!(status, ConfirmStatus::Accepted);
}
#[test]
fn handle_refuses_without_headroom_and_returns_no_snapshot() {
let usav = serialize_snapshot(&sample_snapshot());
let frame = encode_transport_frame(&usav).unwrap();
let result = handle_transport_frame(&frame, &over_ceiling()).unwrap();
assert!(!result.accepted());
assert!(
result.snapshot.is_none(),
"refused → caller is NOT asked to instantiate"
);
let (id, status) = decode_confirm_frame(&result.confirm_frame).unwrap();
assert_eq!(id, [1, 2, 3, 4, 5, 6, 7, 8], "still echoes the node_id");
assert_eq!(status, ConfirmStatus::Refused);
}
#[test]
fn handle_refuses_within_admission_margin_even_under_ceiling() {
let usav = serialize_snapshot(&sample_snapshot());
let frame = encode_transport_frame(&usav).unwrap();
assert!(near_ceiling().has_headroom());
let result = handle_transport_frame(&frame, &near_ceiling()).unwrap();
assert!(
!result.accepted(),
"must refuse inbound within the admission margin"
);
assert!(result.snapshot.is_none());
let (id, status) = decode_confirm_frame(&result.confirm_frame).unwrap();
assert_eq!(id, [1, 2, 3, 4, 5, 6, 7, 8], "still echoes the node_id");
assert_eq!(status, ConfirmStatus::Refused);
}
#[test]
fn handle_fails_closed_on_unavailable_reading() {
let usav = serialize_snapshot(&sample_snapshot());
let frame = encode_transport_frame(&usav).unwrap();
let result = handle_transport_frame(&frame, &HostResources::unavailable()).unwrap();
assert!(!result.accepted());
assert!(result.snapshot.is_none());
let (_id, status) = decode_confirm_frame(&result.confirm_frame).unwrap();
assert_eq!(status, ConfirmStatus::Refused);
}
#[test]
fn handle_refuses_undeserializable_self() {
let frame = encode_transport_frame(b"not a snapshot").unwrap();
let result = handle_transport_frame(&frame, &available()).unwrap();
assert!(!result.accepted());
assert!(result.snapshot.is_none());
let (_id, status) = decode_confirm_frame(&result.confirm_frame).unwrap();
assert_eq!(status, ConfirmStatus::Refused);
}
#[test]
fn handle_drops_structurally_invalid_frame() {
let mut frame = encode_transport_frame(b"x").unwrap();
frame[0] = b'Z';
assert!(handle_transport_frame(&frame, &available()).is_err());
}
fn would_release(outcome: &Result<ConfirmOutcome, TransportError>) -> bool {
matches!(outcome, Ok(ConfirmOutcome::Accepted))
}
#[test]
fn accepted_confirm_permits_release() {
let id = [1, 2, 3, 4, 5, 6, 7, 8];
let confirm = encode_confirm_frame(&id, ConfirmStatus::Accepted);
let (_id, status) = decode_confirm_frame(&confirm).unwrap();
let outcome: Result<ConfirmOutcome, TransportError> = match status {
ConfirmStatus::Accepted => Ok(ConfirmOutcome::Accepted),
ConfirmStatus::Refused => Err(TransportError::Refused),
};
assert!(would_release(&outcome));
}
#[test]
fn refused_timeout_and_garbage_never_release() {
let refused = encode_confirm_frame(&[0u8; 8], ConfirmStatus::Refused);
let (_id, status) = decode_confirm_frame(&refused).unwrap();
let refused_outcome: Result<ConfirmOutcome, TransportError> = match status {
ConfirmStatus::Accepted => Ok(ConfirmOutcome::Accepted),
ConfirmStatus::Refused => Err(TransportError::Refused),
};
assert!(!would_release(&refused_outcome));
let timeout_outcome: Result<ConfirmOutcome, TransportError> =
Err(TransportError::Timeout);
assert!(!would_release(&timeout_outcome));
let garbage = b"not even close to a confirm frame!!!";
let decoded = decode_confirm_frame(garbage);
assert!(decoded.is_err());
let garbage_outcome: Result<ConfirmOutcome, TransportError> =
decoded.map(|_| ConfirmOutcome::Accepted);
assert!(!would_release(&garbage_outcome));
let connect_outcome: Result<ConfirmOutcome, TransportError> =
Err(TransportError::Connect("refused".into()));
assert!(!would_release(&connect_outcome));
}
fn addr(port: u16) -> SocketAddr {
format!("127.0.0.1:{}", port).parse().unwrap()
}
#[test]
fn choose_destination_first_sufficient_when_none_abundant() {
let a = Candidate {
headroom_pct: 30,
addr: addr(9001),
};
let b = Candidate {
headroom_pct: 45, addr: addr(9002),
};
let c = Candidate {
headroom_pct: 35,
addr: addr(9003),
};
let view = [a, b, c];
let mut rng = SimpleRng::new(0);
let chosen = choose_destination(&view, &mut rng).unwrap();
assert_eq!(
chosen.addr,
addr(9001),
"no abundant peer → first-sufficient (herd-avoiding)"
);
}
#[test]
fn choose_destination_prefers_abundant_over_earlier_sufficient() {
let a = Candidate {
headroom_pct: 40,
addr: addr(9001),
};
let b = Candidate {
headroom_pct: 70,
addr: addr(9002),
};
let view = [a, b];
let mut rng = SimpleRng::new(0);
let chosen = choose_destination(&view, &mut rng).unwrap();
assert_eq!(
chosen.addr,
addr(9002),
"an abundant peer is preferred over an earlier merely-sufficient one"
);
}
#[test]
fn choose_destination_picks_emptiest_among_abundant() {
let a = Candidate {
headroom_pct: 55,
addr: addr(9001),
};
let b = Candidate {
headroom_pct: 80,
addr: addr(9002),
};
let c = Candidate {
headroom_pct: 60,
addr: addr(9003),
};
let view = [a, b, c];
for seed in 0..8 {
let mut rng = SimpleRng::new(seed);
let chosen = choose_destination(&view, &mut rng).unwrap();
assert_eq!(
chosen.addr,
addr(9002),
"unique emptiest abundant peer wins regardless of seed"
);
}
}
#[test]
fn choose_destination_random_among_equally_abundant() {
let tied = [addr(9001), addr(9002), addr(9003)];
let view = [
Candidate {
headroom_pct: 70,
addr: tied[0],
},
Candidate {
headroom_pct: 55,
addr: addr(9009),
},
Candidate {
headroom_pct: 70,
addr: tied[1],
},
Candidate {
headroom_pct: 70,
addr: tied[2],
},
];
let mut picks = std::collections::HashSet::new();
for seed in 0..64 {
let mut rng = SimpleRng::new(seed);
let chosen = choose_destination(&view, &mut rng).unwrap();
assert!(
tied.contains(&chosen.addr),
"must pick a tied-maximum peer, got {}",
chosen.addr
);
assert_ne!(chosen.headroom_pct, 55, "must not pick the lower peer");
picks.insert(chosen.addr);
}
assert!(
picks.len() > 1,
"random tie-break must spread across tied peers, got only {:?}",
picks
);
let pick = |s| {
let mut r = SimpleRng::new(s);
choose_destination(&view, &mut r).unwrap().addr
};
assert_eq!(pick(7), pick(7), "same seed must be deterministic");
}
#[test]
fn choose_destination_skips_insufficient_peers() {
let tight = Candidate {
headroom_pct: 10,
addr: addr(9001),
};
let ok = Candidate {
headroom_pct: 35,
addr: addr(9002),
};
let view = [tight, ok];
let mut rng = SimpleRng::new(0);
let chosen = choose_destination(&view, &mut rng).unwrap();
assert_eq!(chosen.addr, addr(9002));
}
#[test]
fn choose_destination_none_when_no_peer_sufficient() {
let tight_a = Candidate {
headroom_pct: 5,
addr: addr(9001),
};
let tight_b = Candidate {
headroom_pct: 15,
addr: addr(9002),
};
let mut rng = SimpleRng::new(0);
assert!(choose_destination(&[tight_a, tight_b], &mut rng).is_none());
assert!(choose_destination(&[], &mut rng).is_none());
}
#[test]
fn is_mislocated_only_when_local_lacks_headroom() {
let healthy = HostResources::from_parts(1000, 500, 0.0, 4);
assert!(!is_mislocated(&healthy));
let pressed = HostResources::from_parts(1000, 50, 0.0, 4);
assert!(is_mislocated(&pressed));
assert!(is_mislocated(&HostResources::unavailable()));
}
#[test]
fn attempt_transport_not_mislocated_is_noop() {
let healthy = HostResources::from_parts(1000, 500, 0.0, 4);
let cands = [Candidate {
headroom_pct: 90,
addr: addr(9001),
}];
let mut sent = false;
let attempt = attempt_transport(&healthy, &cands, true, 0, |_| {
sent = true;
Ok(ConfirmOutcome::Accepted)
});
assert!(matches!(attempt, TransportAttempt::NotMislocated));
assert!(!sent, "must not transport when not mislocated");
}
#[test]
fn attempt_transport_no_destination_is_noop() {
let pressed = HostResources::from_parts(1000, 50, 0.0, 4);
let cands = [Candidate {
headroom_pct: 5,
addr: addr(9001),
}];
let mut sent = false;
let attempt = attempt_transport(&pressed, &cands, true, 0, |_| {
sent = true;
Ok(ConfirmOutcome::Accepted)
});
assert!(matches!(attempt, TransportAttempt::NoDestination));
assert!(!sent);
}
#[test]
fn attempt_transport_cannot_afford_does_not_send() {
let pressed = HostResources::from_parts(1000, 50, 0.0, 4);
let cands = [Candidate {
headroom_pct: 90,
addr: addr(9001),
}];
let mut sent = false;
let attempt = attempt_transport(&pressed, &cands, false, 0, |_| {
sent = true;
Ok(ConfirmOutcome::Accepted)
});
assert!(matches!(attempt, TransportAttempt::CannotAfford));
assert!(!sent, "a starving unit cannot flee");
}
#[test]
fn attempt_transport_sends_when_mislocated_afford_and_dest_exists() {
let pressed = HostResources::from_parts(1000, 50, 0.0, 4);
let cands = [Candidate {
headroom_pct: 90,
addr: addr(9099),
}];
let mut sent_addr = None;
let attempt = attempt_transport(&pressed, &cands, true, 0, |a| {
sent_addr = Some(a);
Ok(ConfirmOutcome::Accepted)
});
assert_eq!(sent_addr, Some(addr(9099)));
match attempt {
TransportAttempt::Attempted(o) => assert!(should_release(&o)),
other => panic!("expected Attempted, got {other:?}"),
}
}
#[test]
fn attempt_transport_err_does_not_permit_release() {
let pressed = HostResources::from_parts(1000, 50, 0.0, 4);
let cands = [Candidate {
headroom_pct: 90,
addr: addr(9001),
}];
let attempt = attempt_transport(&pressed, &cands, true, 0, |_| {
Err(TransportError::Refused)
});
match attempt {
TransportAttempt::Attempted(o) => assert!(!should_release(&o)),
other => panic!("expected Attempted, got {other:?}"),
}
}
}