#[cfg(test)]
mod tests {
#[allow(unused_imports)] use crate::{JsonHop, JsonIsp, JsonOutput};
use ftr::{
AsnInfo, ClassifiedHopInfo, IspInfo, ProbeProtocol, SegmentType, SocketMode,
TracerouteResult,
};
use serde_json;
use std::net::{IpAddr, Ipv4Addr};
use std::time::Duration;
#[test]
fn test_json_rtt_precision() {
let test_cases = vec![
(1.234567f64, 1.2),
(1.567890, 1.6),
(10.951, 11.0),
(5.449, 5.4),
(5.450, 5.5),
(5.451, 5.5),
(0.05, 0.1),
(0.04, 0.0),
];
for (input, expected) in test_cases {
let rounded = (input * 10.0).round() / 10.0;
assert_eq!(
rounded, expected,
"RTT {} should round to {} with 1 decimal place",
input, expected
);
}
}
#[test]
fn test_json_destination_asn_field() {
let result = TracerouteResult {
target: "google.com".to_string(),
target_ip: IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)),
hops: vec![],
isp_info: Some(IspInfo {
public_ip: IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)),
asn: 12345,
name: "Test ISP".to_string(),
hostname: Some("customer.isp.com".to_string()),
}),
destination_asn: Some(AsnInfo {
asn: 15169,
prefix: "8.8.8.0/24".to_string(),
country_code: "US".to_string(),
registry: "ARIN".to_string(),
name: "GOOGLE".to_string(),
}), protocol_used: ProbeProtocol::Icmp,
socket_mode_used: SocketMode::Raw,
destination_reached: true,
total_duration: Duration::from_millis(100),
};
let json_output = serde_json::json!({
"version": "0.6.0",
"target": result.target,
"target_ip": result.target_ip.to_string(),
"destination_asn": result.destination_asn.as_ref().map(|a| a.asn),
"hops": []
});
let json_str = serde_json::to_string(&json_output).unwrap();
assert!(
json_str.contains("\"destination_asn\":15169"),
"JSON should contain destination_asn field with value 15169"
);
}
#[test]
fn test_json_transit_segment() {
let hop = ClassifiedHopInfo {
ttl: 12,
segment: SegmentType::Transit,
hostname: Some("pat1.sjc.yahoo.com".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(206, 223, 116, 16))),
asn_info: None, rtt: Some(Duration::from_micros(10972)), };
let segment_str = match hop.segment {
SegmentType::Lan => "LAN",
SegmentType::Isp => "ISP",
SegmentType::Transit => "TRANSIT",
SegmentType::Destination => "DESTINATION",
SegmentType::Unknown => "UNKNOWN",
};
assert_eq!(
segment_str, "TRANSIT",
"Transit segment should serialize as 'TRANSIT'"
);
let rtt_ms = hop.rtt_ms().map(|ms| (ms * 10.0).round() / 10.0);
assert_eq!(rtt_ms, Some(11.0), "RTT 10.972ms should round to 11.0");
}
#[test]
fn test_json_destination_segment() {
let hop = ClassifiedHopInfo {
ttl: 15,
segment: SegmentType::Destination,
hostname: Some("google.com".to_string()),
addr: Some(IpAddr::V4(Ipv4Addr::new(142, 250, 189, 206))),
asn_info: Some(AsnInfo {
asn: 15169,
prefix: "142.250.0.0/15".to_string(),
country_code: "US".to_string(),
registry: "arin".to_string(),
name: "GOOGLE, US".to_string(),
}),
rtt: Some(Duration::from_micros(5361)), };
let segment_str = match hop.segment {
SegmentType::Lan => "LAN",
SegmentType::Isp => "ISP",
SegmentType::Transit => "TRANSIT",
SegmentType::Destination => "DESTINATION",
SegmentType::Unknown => "UNKNOWN",
};
assert_eq!(
segment_str, "DESTINATION",
"Destination segment should serialize as 'DESTINATION'"
);
let rtt_ms = hop.rtt_ms().map(|ms| (ms * 10.0).round() / 10.0);
assert_eq!(rtt_ms, Some(5.4), "RTT 5.361ms should round to 5.4");
}
#[test]
fn test_segment_display_format() {
assert_eq!(SegmentType::Lan.to_string(), "LAN ");
assert_eq!(SegmentType::Isp.to_string(), "ISP ");
assert_eq!(SegmentType::Transit.to_string(), "TRANSIT");
assert_eq!(SegmentType::Destination.to_string(), "DESTINATION");
assert_eq!(SegmentType::Unknown.to_string(), "UNKNOWN");
}
#[test]
fn test_silent_hop_json_serialization() {
let hop = ClassifiedHopInfo {
ttl: 5,
segment: SegmentType::Unknown,
hostname: None,
addr: None, asn_info: None,
rtt: None,
};
let has_address = hop.addr.is_some();
let segment_json = if !has_address && hop.segment == SegmentType::Unknown {
serde_json::Value::Null
} else {
serde_json::Value::String("UNKNOWN".to_string())
};
assert_eq!(
segment_json,
serde_json::Value::Null,
"Silent hop with Unknown segment should have null segment in JSON"
);
assert_eq!(hop.rtt_ms(), None, "Silent hop should have no RTT");
}
#[test]
fn test_destination_asn_none() {
let result = TracerouteResult {
target: "example.com".to_string(),
target_ip: IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)),
hops: vec![],
isp_info: None,
destination_asn: None, protocol_used: ProbeProtocol::Icmp,
socket_mode_used: SocketMode::Raw,
destination_reached: false,
total_duration: Duration::from_millis(3000),
};
let json_output = serde_json::json!({
"destination_asn": result.destination_asn.as_ref().map(|a| a.asn),
});
assert_eq!(
json_output["destination_asn"],
serde_json::Value::Null,
"destination_asn should be null when lookup fails"
);
}
}