use super::*;
fn approx(a: f64, b: f64) -> bool {
(a - b).abs() < 1e-9
}
#[test]
fn bbox_area_encloses_itself() {
let area = Area::BoundingBox {
south_west: GeoPoint {
lat: 30.0,
lon: -100.0,
},
north_east: GeoPoint {
lat: 40.0,
lon: -90.0,
},
};
let (sw, ne) = area.enclosing_bbox().unwrap();
assert!(approx(sw.lat, 30.0) && approx(sw.lon, -100.0));
assert!(approx(ne.lat, 40.0) && approx(ne.lon, -90.0));
}
#[test]
fn point_radius_bbox_scales_longitude_by_latitude() {
let area = Area::PointRadius {
center: GeoPoint {
lat: 60.0,
lon: 10.0,
},
radius_nm: 60.0,
};
let (sw, ne) = area.enclosing_bbox().unwrap();
assert!(approx(sw.lat, 59.0) && approx(ne.lat, 61.0));
assert!(approx(sw.lon, 8.0) && approx(ne.lon, 12.0));
}
#[test]
fn point_radius_bbox_clamps_latitude_at_pole() {
let area = Area::PointRadius {
center: GeoPoint {
lat: 89.5,
lon: 0.0,
},
radius_nm: 120.0,
};
let (sw, ne) = area.enclosing_bbox().unwrap();
assert!(approx(ne.lat, 90.0));
assert!(sw.lat < 89.5);
assert!(approx(ne.lon - sw.lon, 360.0));
}
#[test]
fn location_radius_needs_provider_resolution() {
let area = Area::LocationRadius {
ident: "KSFO".to_owned(),
radius_nm: 50.0,
};
assert!(area.enclosing_bbox().is_none());
}
#[test]
fn request_round_trips_through_serde() {
let request = AreaBriefingRequest::new(Area::PointRadius {
center: GeoPoint {
lat: 37.6,
lon: -122.4,
},
radius_nm: 100.0,
})
.with_products(vec![
ProductKind::Metar,
ProductKind::Other("synopsis".to_owned()),
])
.with_lookback_hours(Some(2))
.with_departure_at("2026-06-05T14:30:00Z".parse().ok());
let json = serde_json::to_string(&request).unwrap();
let back: AreaBriefingRequest = serde_json::from_str(&json).unwrap();
assert_eq!(back, request);
}
#[test]
fn polygon_contains_and_bbox() {
let square = Area::Polygon {
vertices: vec![
GeoPoint {
lat: 37.0,
lon: -123.0,
},
GeoPoint {
lat: 38.5,
lon: -123.0,
},
GeoPoint {
lat: 38.5,
lon: -121.5,
},
GeoPoint {
lat: 37.0,
lon: -121.5,
},
],
};
let ksfo = GeoPoint {
lat: 37.62,
lon: -122.37,
};
let kden = GeoPoint {
lat: 39.86,
lon: -104.67,
};
assert_eq!(square.contains(ksfo), Some(true));
assert_eq!(square.contains(kden), Some(false));
let (sw, ne) = square.enclosing_bbox().unwrap();
assert!(approx(sw.lat, 37.0) && approx(ne.lat, 38.5));
assert!(approx(sw.lon, -123.0) && approx(ne.lon, -121.5));
}
#[test]
fn antimeridian_polygon_stays_contiguous() {
let ring = Area::Polygon {
vertices: vec![
GeoPoint {
lat: 51.0,
lon: 178.0,
},
GeoPoint {
lat: 53.0,
lon: 178.0,
},
GeoPoint {
lat: 53.0,
lon: -178.0,
},
GeoPoint {
lat: 51.0,
lon: -178.0,
},
],
};
let (sw, ne) = ring.enclosing_bbox().unwrap();
assert!(
(ne.lon - sw.lon) < 10.0,
"bbox spans the crossing, not the globe: {} .. {}",
sw.lon,
ne.lon
);
assert_eq!(
ring.contains(GeoPoint {
lat: 52.0,
lon: 179.5,
}),
Some(true)
);
assert_eq!(
ring.contains(GeoPoint {
lat: 52.0,
lon: 170.0,
}),
Some(false)
);
}
#[test]
fn location_radius_contains_is_undecidable() {
let area = Area::LocationRadius {
ident: "KSFO".to_owned(),
radius_nm: 25.0,
};
assert_eq!(area.contains(GeoPoint { lat: 0.0, lon: 0.0 }), None);
}