#[cfg(test)]
mod tests {
use super::super::*;
use std::net::{IpAddr, Ipv4Addr};
use std::time::Duration;
#[test]
fn test_sandwich_logic_isp() {
let mut hops = vec![
ClassifiedHopInfo {
ttl: 1,
segment: SegmentType::Isp,
hostname: Some("hop1".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))),
asn_info: None,
rtt: Some(Duration::from_millis(5)),
},
ClassifiedHopInfo {
ttl: 2,
segment: SegmentType::Unknown,
hostname: Some("hop2".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2))),
asn_info: None,
rtt: Some(Duration::from_millis(10)),
},
ClassifiedHopInfo {
ttl: 3,
segment: SegmentType::Isp,
hostname: Some("hop3".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 3))),
asn_info: None,
rtt: Some(Duration::from_millis(15)),
},
];
super::super::TracerouteEngine::apply_sandwich_logic(&mut hops);
assert_eq!(
hops[1].segment,
SegmentType::Isp,
"Unknown hop between ISP hops should become ISP"
);
}
#[test]
fn test_sandwich_logic_destination() {
let mut hops = vec![
ClassifiedHopInfo {
ttl: 1,
segment: SegmentType::Destination,
hostname: Some("dest1".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 1))),
asn_info: None,
rtt: Some(Duration::from_millis(5)),
},
ClassifiedHopInfo {
ttl: 2,
segment: SegmentType::Transit,
hostname: Some("transit".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 2))),
asn_info: None,
rtt: Some(Duration::from_millis(10)),
},
ClassifiedHopInfo {
ttl: 3,
segment: SegmentType::Destination,
hostname: Some("dest2".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 3))),
asn_info: None,
rtt: Some(Duration::from_millis(15)),
},
];
super::super::TracerouteEngine::apply_sandwich_logic(&mut hops);
assert_eq!(
hops[1].segment,
SegmentType::Destination,
"Transit hop between Destination hops should become Destination"
);
}
#[test]
fn test_sandwich_logic_no_address() {
let mut hops = vec![
ClassifiedHopInfo {
ttl: 1,
segment: SegmentType::Isp,
hostname: Some("hop1".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))),
asn_info: None,
rtt: Some(Duration::from_millis(5)),
},
ClassifiedHopInfo {
ttl: 2,
segment: SegmentType::Unknown,
hostname: None,
addr: None, asn_info: None,
rtt: None,
},
ClassifiedHopInfo {
ttl: 3,
segment: SegmentType::Isp,
hostname: Some("hop3".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 3))),
asn_info: None,
rtt: Some(Duration::from_millis(15)),
},
];
super::super::TracerouteEngine::apply_sandwich_logic(&mut hops);
assert_eq!(
hops[1].segment,
SegmentType::Unknown,
"Silent hop should remain Unknown"
);
}
#[test]
fn test_sandwich_logic_multiple_unknowns() {
let mut hops = vec![
ClassifiedHopInfo {
ttl: 1,
segment: SegmentType::Isp,
hostname: Some("isp1".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))),
asn_info: None,
rtt: Some(Duration::from_millis(5)),
},
ClassifiedHopInfo {
ttl: 2,
segment: SegmentType::Unknown,
hostname: None,
addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2))),
asn_info: None,
rtt: Some(Duration::from_millis(10)),
},
ClassifiedHopInfo {
ttl: 3,
segment: SegmentType::Transit,
hostname: None,
addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 3))),
asn_info: None,
rtt: Some(Duration::from_millis(15)),
},
ClassifiedHopInfo {
ttl: 4,
segment: SegmentType::Unknown,
hostname: None,
addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 4))),
asn_info: None,
rtt: Some(Duration::from_millis(20)),
},
ClassifiedHopInfo {
ttl: 5,
segment: SegmentType::Isp,
hostname: Some("isp2".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 5))),
asn_info: None,
rtt: Some(Duration::from_millis(25)),
},
];
super::super::TracerouteEngine::apply_sandwich_logic(&mut hops);
assert_eq!(
hops[1].segment,
SegmentType::Isp,
"First unknown between ISP hops should become ISP"
);
assert_eq!(
hops[2].segment,
SegmentType::Isp,
"Transit between ISP hops should become ISP"
);
assert_eq!(
hops[3].segment,
SegmentType::Isp,
"Second unknown between ISP hops should become ISP"
);
}
#[test]
fn test_sandwich_logic_mixed_silent_and_responsive() {
let mut hops = vec![
ClassifiedHopInfo {
ttl: 1,
segment: SegmentType::Destination,
hostname: Some("dest1".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 1))),
asn_info: None,
rtt: Some(Duration::from_millis(5)),
},
ClassifiedHopInfo {
ttl: 2,
segment: SegmentType::Unknown,
hostname: None,
addr: None, asn_info: None,
rtt: None,
},
ClassifiedHopInfo {
ttl: 3,
segment: SegmentType::Transit,
hostname: Some("transit".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 3))),
asn_info: None,
rtt: Some(Duration::from_millis(15)),
},
ClassifiedHopInfo {
ttl: 4,
segment: SegmentType::Destination,
hostname: Some("dest2".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 4))),
asn_info: None,
rtt: Some(Duration::from_millis(20)),
},
];
super::super::TracerouteEngine::apply_sandwich_logic(&mut hops);
assert_eq!(
hops[1].segment,
SegmentType::Unknown,
"Silent hop should remain Unknown"
);
assert_eq!(
hops[2].segment,
SegmentType::Destination,
"Transit between Destination hops should become Destination"
);
}
#[test]
fn test_sandwich_logic_edge_cases() {
let mut empty_hops: Vec<ClassifiedHopInfo> = vec![];
super::super::TracerouteEngine::apply_sandwich_logic(&mut empty_hops);
assert_eq!(empty_hops.len(), 0, "Empty list should remain empty");
let mut single_hop = vec![ClassifiedHopInfo {
ttl: 1,
segment: SegmentType::Unknown,
hostname: None,
addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))),
asn_info: None,
rtt: Some(Duration::from_millis(5)),
}];
super::super::TracerouteEngine::apply_sandwich_logic(&mut single_hop);
assert_eq!(
single_hop[0].segment,
SegmentType::Unknown,
"Single hop should not change"
);
let mut two_hops = vec![
ClassifiedHopInfo {
ttl: 1,
segment: SegmentType::Isp,
hostname: Some("isp".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))),
asn_info: None,
rtt: Some(Duration::from_millis(5)),
},
ClassifiedHopInfo {
ttl: 2,
segment: SegmentType::Unknown,
hostname: None,
addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2))),
asn_info: None,
rtt: Some(Duration::from_millis(10)),
},
];
super::super::TracerouteEngine::apply_sandwich_logic(&mut two_hops);
assert_eq!(
two_hops[1].segment,
SegmentType::Unknown,
"Two hops cannot form sandwich"
);
}
#[test]
fn test_sandwich_logic_lan_not_affected() {
let mut hops = vec![
ClassifiedHopInfo {
ttl: 1,
segment: SegmentType::Lan,
hostname: Some("router1".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))),
asn_info: None,
rtt: Some(Duration::from_millis(1)),
},
ClassifiedHopInfo {
ttl: 2,
segment: SegmentType::Unknown,
hostname: None,
addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))),
asn_info: None,
rtt: Some(Duration::from_millis(5)),
},
ClassifiedHopInfo {
ttl: 3,
segment: SegmentType::Lan,
hostname: Some("router2".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 254))),
asn_info: None,
rtt: Some(Duration::from_millis(2)),
},
];
super::super::TracerouteEngine::apply_sandwich_logic(&mut hops);
assert_eq!(
hops[1].segment,
SegmentType::Unknown,
"Unknown between LAN hops should not be sandwiched"
);
}
#[test]
fn test_sandwich_logic_isp_precedence() {
let mut hops = vec![
ClassifiedHopInfo {
ttl: 1,
segment: SegmentType::Isp,
hostname: Some("isp1".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))),
asn_info: None,
rtt: Some(Duration::from_millis(5)),
},
ClassifiedHopInfo {
ttl: 2,
segment: SegmentType::Unknown,
hostname: Some("unknown".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2))),
asn_info: None,
rtt: Some(Duration::from_millis(10)),
},
ClassifiedHopInfo {
ttl: 3,
segment: SegmentType::Isp,
hostname: Some("isp2".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 3))),
asn_info: None,
rtt: Some(Duration::from_millis(15)),
},
ClassifiedHopInfo {
ttl: 4,
segment: SegmentType::Destination,
hostname: Some("dest".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8))),
asn_info: None,
rtt: Some(Duration::from_millis(20)),
},
];
super::super::TracerouteEngine::apply_sandwich_logic(&mut hops);
assert_eq!(
hops[1].segment,
SegmentType::Isp,
"Unknown between ISP hops should become ISP even with Destination present"
);
}
}