use super::*;
#[test]
fn metar_decodes_field_names_from_spec() {
let body = r#"[{
"icaoId": "KORD",
"rawOb": "KORD 041951Z 27012KT 10SM FEW250 28/14 A3002",
"obsTime": 1717530660,
"temp": 28.0,
"altim": 1016.6
}]"#;
let products = metars(body).unwrap();
assert_eq!(products.len(), 1);
assert_eq!(products[0].kind, ProductKind::Metar);
assert_eq!(products[0].location.as_deref(), Some("KORD"));
assert!(products[0].raw_text.starts_with("KORD 041951Z"));
assert_eq!(
products[0].issued_at.map(|t| t.timestamp()),
Some(1717530660)
);
}
#[test]
fn metar_without_raw_text_is_dropped_not_fatal() {
let body = r#"[{"icaoId": "KORD"}, {"icaoId": "KMDW", "rawOb": "KMDW ..."}]"#;
let products = metars(body).unwrap();
assert_eq!(products.len(), 1);
assert_eq!(products[0].location.as_deref(), Some("KMDW"));
}
#[test]
fn taf_uses_raw_taf_not_raw_ob() {
let body = r#"[{
"icaoId": "KORD",
"rawTAF": "KORD 041730Z 0418/0524 27010KT P6SM SCT250",
"issueTime": "2026-06-04 17:30:00"
}]"#;
let products = tafs(body).unwrap();
assert_eq!(products.len(), 1);
assert_eq!(products[0].kind, ProductKind::Taf);
assert!(products[0].raw_text.starts_with("KORD 041730Z"));
assert!(products[0].issued_at.is_some());
}
#[test]
fn pirep_decodes_raw_ob() {
let body = r#"[{
"rawOb": "ORD UA /OV ORD/TM 1955/FL080/TP B738/TB LGT",
"obsTime": 1717531000
}]"#;
let products = pireps(body).unwrap();
assert_eq!(products.len(), 1);
assert_eq!(products[0].kind, ProductKind::Pirep);
assert!(products[0].location.is_none());
}
#[test]
fn pirep_fractional_obs_time_does_not_fail_the_batch() {
let body = r#"[
{"rawOb": "FLOAT .0", "obsTime": 1699379880.0},
{"rawOb": "FLOAT .5", "obsTime": 1699379880.5},
{"rawOb": "INTEGER", "obsTime": 1699379880}
]"#;
let products = pireps(body).unwrap();
assert_eq!(products.len(), 3);
for product in &products {
assert_eq!(
product.issued_at.map(|t| t.timestamp()),
Some(1699379880),
"{} keeps its (truncated) timestamp",
product.raw_text
);
}
}
#[test]
fn airsigmet_carries_polygon_for_clipping() {
let body = r#"[{
"icaoId": "KKCI",
"rawAirSigmet": "WSUS31 KKCI 042000 ...",
"creationTime": "2026-06-04T19:48:24.974Z",
"coords": [{"lat": 40.0, "lon": -90.0}, {"lat": 42.0, "lon": -88.0}]
}]"#;
let entries = air_sigmets(body).unwrap();
assert_eq!(entries.len(), 1);
assert_eq!(entries[0].0.kind, ProductKind::Sigmet);
assert_eq!(entries[0].1.len(), 2);
assert!(entries[0].0.issued_at.is_some());
}
#[test]
fn clip_keeps_intersecting_and_geometryless_drops_outside() {
let inside = (
Product::new(ProductKind::Sigmet, "INSIDE"),
vec![
GeoPoint {
lat: 40.0,
lon: -90.0,
},
GeoPoint {
lat: 42.0,
lon: -88.0,
},
],
);
let outside = (
Product::new(ProductKind::Sigmet, "OUTSIDE"),
vec![
GeoPoint {
lat: 10.0,
lon: 50.0,
},
GeoPoint {
lat: 12.0,
lon: 52.0,
},
],
);
let no_geometry = (Product::new(ProductKind::Sigmet, "NO-GEOMETRY"), vec![]);
let lat_overlap_only = (
Product::new(ProductKind::Sigmet, "LAT-ONLY"),
vec![
GeoPoint {
lat: 40.0,
lon: 50.0,
},
GeoPoint {
lat: 41.0,
lon: 52.0,
},
],
);
let lon_overlap_only = (
Product::new(ProductKind::Sigmet, "LON-ONLY"),
vec![
GeoPoint {
lat: 10.0,
lon: -90.0,
},
GeoPoint {
lat: 12.0,
lon: -88.0,
},
],
);
let kept = clip_to_bbox(
vec![
inside,
outside,
no_geometry,
lat_overlap_only,
lon_overlap_only,
],
(
GeoPoint {
lat: 39.0,
lon: -91.0,
},
GeoPoint {
lat: 41.0,
lon: -87.0,
},
),
);
let texts: Vec<&str> = kept.iter().map(|p| p.raw_text.as_str()).collect();
assert_eq!(texts, ["INSIDE", "NO-GEOMETRY"]);
}
#[test]
fn awc_time_formats_parse_best_effort() {
assert!(parse_awc_time("2026-06-04T19:48:24.974Z").is_some());
assert!(parse_awc_time("2026-06-04 19:48:24.974Z").is_some());
assert!(parse_awc_time("2026-06-04 19:48:24").is_some());
assert!(parse_awc_time("not a time").is_none());
}
#[test]
fn malformed_body_is_an_error_not_a_panic() {
assert!(metars("{\"not\": \"an array\"}").is_err());
assert!(metars("").is_err());
}