openrtc 1.0.2

OpenRTC: a Rust-first P2P runtime for device discovery, signaling, and iroh/QUIC networking.
Documentation
use crate::connection_manager::ConnectionHealth;
use std::collections::HashMap;

/// Per-transport health tracker. Stateless — caller drives transitions by
/// reporting last_pong_at_ms and now_ms.
pub struct TransportHealthEvaluator {
    pub suspect_after_ms: i64,
    pub stale_after_ms: i64,
}

impl TransportHealthEvaluator {
    pub fn new(suspect_after_ms: i64, stale_after_ms: i64) -> Self {
        Self {
            suspect_after_ms,
            stale_after_ms,
        }
    }

    pub fn evaluate(
        &self,
        last_pong_at_ms: Option<i64>,
        first_ping_sent_at_ms: Option<i64>,
        now_ms: i64,
    ) -> ConnectionHealth {
        let reference_ms = last_pong_at_ms.or(first_ping_sent_at_ms);
        let Some(ref_ms) = reference_ms else {
            return ConnectionHealth::Unknown;
        };
        let silence_ms = now_ms.saturating_sub(ref_ms);
        if silence_ms >= self.stale_after_ms {
            ConnectionHealth::Stale
        } else if silence_ms >= self.suspect_after_ms {
            ConnectionHealth::Suspect
        } else {
            ConnectionHealth::Healthy
        }
    }
}

/// Aggregate per-transport healths into a single peer-level health.
/// Priority: any Healthy → Healthy, all Stale → Stale, else Suspect.
/// Empty map → Unknown.
pub fn aggregate_peer_health(healths: &HashMap<String, ConnectionHealth>) -> ConnectionHealth {
    if healths.is_empty() {
        return ConnectionHealth::Unknown;
    }
    let mut any_healthy = false;
    let mut all_stale = true;
    for h in healths.values() {
        match h {
            ConnectionHealth::Healthy => {
                any_healthy = true;
                all_stale = false;
            }
            ConnectionHealth::Suspect | ConnectionHealth::Unknown => {
                all_stale = false;
            }
            ConnectionHealth::Stale => {}
        }
    }
    if any_healthy {
        ConnectionHealth::Healthy
    } else if all_stale {
        ConnectionHealth::Stale
    } else {
        ConnectionHealth::Suspect
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn eval(last_pong: Option<i64>, first_ping: Option<i64>, now: i64) -> ConnectionHealth {
        TransportHealthEvaluator::new(10_000, 30_000).evaluate(last_pong, first_ping, now)
    }

    #[test]
    fn unknown_when_no_ping_sent() {
        assert_eq!(eval(None, None, 1000), ConnectionHealth::Unknown);
    }

    #[test]
    fn healthy_shortly_after_pong() {
        assert_eq!(eval(Some(0), None, 5_000), ConnectionHealth::Healthy);
    }

    #[test]
    fn suspect_after_missed_pings() {
        assert_eq!(eval(Some(0), None, 15_000), ConnectionHealth::Suspect);
    }

    #[test]
    fn stale_after_threshold() {
        assert_eq!(eval(Some(0), None, 35_000), ConnectionHealth::Stale);
    }

    #[test]
    fn recovery_resets_to_healthy() {
        // Last pong 2s ago — healthy
        assert_eq!(eval(Some(98_000), None, 100_000), ConnectionHealth::Healthy);
    }

    #[test]
    fn aggregate_any_healthy_wins() {
        let mut map = HashMap::new();
        map.insert("iroh".into(), ConnectionHealth::Stale);
        map.insert("webrtc".into(), ConnectionHealth::Healthy);
        assert_eq!(aggregate_peer_health(&map), ConnectionHealth::Healthy);
    }

    #[test]
    fn aggregate_all_stale() {
        let mut map = HashMap::new();
        map.insert("iroh".into(), ConnectionHealth::Stale);
        map.insert("webrtc".into(), ConnectionHealth::Stale);
        assert_eq!(aggregate_peer_health(&map), ConnectionHealth::Stale);
    }

    #[test]
    fn aggregate_mixed_suspect() {
        let mut map = HashMap::new();
        map.insert("iroh".into(), ConnectionHealth::Suspect);
        map.insert("webrtc".into(), ConnectionHealth::Stale);
        assert_eq!(aggregate_peer_health(&map), ConnectionHealth::Suspect);
    }

    #[test]
    fn aggregate_empty_unknown() {
        assert_eq!(
            aggregate_peer_health(&HashMap::new()),
            ConnectionHealth::Unknown
        );
    }
}