use ftr::{Ftr, SegmentType, TracerouteConfig};
use std::net::Ipv4Addr;
#[tokio::test]
async fn test_v6_segment_types() {
let segments = vec![
SegmentType::Lan,
SegmentType::Isp,
SegmentType::Transit, SegmentType::Destination, SegmentType::Unknown,
];
assert_eq!(segments.len(), 5);
for (i, seg1) in segments.iter().enumerate() {
for (j, seg2) in segments.iter().enumerate() {
if i == j {
assert_eq!(seg1, seg2, "Same segment should be equal");
} else {
assert_ne!(seg1, seg2, "Different segments should not be equal");
}
}
}
}
#[tokio::test]
async fn test_destination_asn_field() {
let ftr = Ftr::new();
let config = TracerouteConfig::builder()
.target("127.0.0.1")
.max_hops(3)
.enable_asn_lookup(false) .enable_rdns(false)
.build()
.unwrap();
let result = ftr.trace_with_config(config).await.unwrap();
assert!(
result.destination_asn.is_none(),
"Without ASN lookup, destination_asn should be None"
);
let _ = result.destination_asn; }
#[tokio::test]
async fn test_transit_segment_classification() {
let ftr = Ftr::new();
let config = TracerouteConfig::builder()
.target("127.0.0.1")
.max_hops(5)
.enable_asn_lookup(false)
.enable_rdns(false)
.build()
.unwrap();
let result = ftr.trace_with_config(config).await.unwrap();
for hop in &result.hops {
match hop.segment {
SegmentType::Lan
| SegmentType::Isp
| SegmentType::Transit
| SegmentType::Destination
| SegmentType::Unknown => {}
}
}
}
#[test]
fn test_segment_serialization() {
use serde_json;
let transit = SegmentType::Transit;
let destination = SegmentType::Destination;
let transit_json = serde_json::to_string(&transit).unwrap();
let dest_json = serde_json::to_string(&destination).unwrap();
assert_eq!(
transit_json, "\"Transit\"",
"Transit should serialize as \"Transit\""
);
assert_eq!(
dest_json, "\"Destination\"",
"Destination should serialize as \"Destination\""
);
}
#[test]
fn test_public_ip_classification() {
let private_ips = vec![
Ipv4Addr::new(192, 168, 1, 1), Ipv4Addr::new(10, 0, 0, 1), Ipv4Addr::new(172, 16, 0, 1), Ipv4Addr::new(127, 0, 0, 1), Ipv4Addr::new(169, 254, 0, 1), ];
for ip in &private_ips {
assert!(
ip.is_private() || ip.is_loopback() || ip.is_link_local(),
"{} should be a private/internal IP",
ip
);
}
let public_ips = vec![
Ipv4Addr::new(8, 8, 8, 8),
Ipv4Addr::new(206, 223, 116, 16), ];
for ip in &public_ips {
assert!(
!ip.is_private() && !ip.is_loopback() && !ip.is_link_local(),
"{} should be a public IP",
ip
);
}
let cgnat_start = Ipv4Addr::new(100, 64, 0, 0);
let cgnat_end = Ipv4Addr::new(100, 127, 255, 255);
assert!(
Ipv4Addr::new(100, 64, 0, 1) >= cgnat_start && Ipv4Addr::new(100, 64, 0, 1) <= cgnat_end,
"100.64.0.1 should be in CGNAT range"
);
assert!(
Ipv4Addr::new(100, 127, 255, 254) >= cgnat_start
&& Ipv4Addr::new(100, 127, 255, 254) <= cgnat_end,
"100.127.255.254 should be in CGNAT range"
);
}
#[tokio::test]
async fn test_localhost_trace_segments() {
let ftr = Ftr::new();
let config = TracerouteConfig::builder()
.target("127.0.0.1")
.max_hops(3)
.enable_asn_lookup(false)
.enable_rdns(false)
.build()
.unwrap();
let result = ftr.trace_with_config(config).await.unwrap();
assert!(!result.hops.is_empty(), "Localhost trace should have hops");
if let Some(first_hop) = result.hops.first() {
if first_hop.addr == Some(std::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))) {
assert_eq!(
first_hop.segment,
SegmentType::Lan,
"Localhost (127.0.0.1) should be classified as LAN"
);
}
}
}
#[test]
fn test_rtt_precision_helper() {
fn round_to_one_decimal(ms: f64) -> f64 {
(ms * 10.0).round() / 10.0
}
assert_eq!(round_to_one_decimal(1.234), 1.2);
assert_eq!(round_to_one_decimal(1.567), 1.6);
assert_eq!(round_to_one_decimal(10.951), 11.0);
assert_eq!(round_to_one_decimal(5.449), 5.4);
assert_eq!(round_to_one_decimal(5.450), 5.5);
assert_eq!(round_to_one_decimal(5.451), 5.5);
}
#[test]
fn test_v6_version_string() {
let version = env!("CARGO_PKG_VERSION");
assert!(
version.starts_with("0."),
"Expected 0.x.y version, got {}",
version
);
}