2cb633e99d
Clean-room reproduction of PFPX: route generation + IFPS validation + OFP. Independent design — official ICAO/EUROCONTROL data only (RAD, FRA points, IFPUV oracle); no community FPL sources. Workspace crates: core (routing/discover/rad/navdata/perf/export), cli, server, rad (Annex parser), gui (Tauri v2 + React + MapLibre). Route discovery: oracle-in-the-loop repair against Eurocontrol IFPUV. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
90 lines
3.1 KiB
Rust
90 lines
3.1 KiB
Rust
//! End-to-end step-3 test: import the fixtures, then route LFPG→EGLL through the
|
|
//! hand-made airway network and check the result is sensible.
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use flightplanner_core::routing::graph::RouteGraph;
|
|
use flightplanner_core::{db, routing};
|
|
use rusqlite::Connection;
|
|
|
|
fn fixtures_dir() -> PathBuf {
|
|
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures")
|
|
}
|
|
|
|
fn fixtures_db() -> Connection {
|
|
let mut conn = Connection::open_in_memory().unwrap();
|
|
db::import_navdata(&mut conn, &fixtures_dir()).unwrap();
|
|
conn
|
|
}
|
|
|
|
#[test]
|
|
fn routes_lfpg_to_egll_via_airways() {
|
|
let conn = fixtures_db();
|
|
|
|
let route = routing::plan_route(&conn, "LFPG", "EGLL", None).unwrap();
|
|
|
|
assert!(route.via_airways, "expected an airway route, got {route:?}");
|
|
assert_eq!(route.legs.first().unwrap().from, "LFPG");
|
|
assert_eq!(route.legs.last().unwrap().to, "EGLL");
|
|
|
|
// The fixture network forces travel over the T100/U200 airways.
|
|
let airways: Vec<&str> = route.legs.iter().map(|l| l.airway.as_str()).collect();
|
|
assert!(
|
|
airways.iter().any(|a| *a != "DCT"),
|
|
"route used no airway: {airways:?}"
|
|
);
|
|
|
|
// Sanity on total distance (direct LFPG-EGLL ≈ 188 nm; via fixtures a bit more).
|
|
assert!(
|
|
route.total_nm > 150.0 && route.total_nm < 400.0,
|
|
"total = {} nm",
|
|
route.total_nm
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_airport_is_reported() {
|
|
let conn = fixtures_db();
|
|
let err = routing::plan_route(&conn, "LFPG", "ZZZZ", None).unwrap_err();
|
|
assert!(err.to_string().contains("ZZZZ"), "{err}");
|
|
}
|
|
|
|
#[test]
|
|
fn graph_filters_airways_by_flight_level() {
|
|
let conn = fixtures_db();
|
|
// Fixtures: T100 (low, FL35..245) links ABEAM/BEACN/CROSS,
|
|
// U200 (high, FL245..460) links CROSS/DOVER/ENTRY.
|
|
assert_eq!(RouteGraph::build(&conn, None).unwrap().node_count(), 5);
|
|
assert_eq!(RouteGraph::build(&conn, Some(200)).unwrap().node_count(), 3); // T100 only
|
|
assert_eq!(RouteGraph::build(&conn, Some(300)).unwrap().node_count(), 3); // U200 only
|
|
assert_eq!(RouteGraph::build(&conn, Some(500)).unwrap().node_count(), 0); // above both
|
|
}
|
|
|
|
#[test]
|
|
fn fra_produces_a_dct_chain() {
|
|
let conn = fixtures_db();
|
|
let route = routing::fra::plan_fra(&conn, "LFPG", "EGLL", &[], &[], 360, None).unwrap();
|
|
assert!(!route.via_airways, "FRA is DCT-based: {route:?}");
|
|
assert!(route.legs.iter().all(|l| l.airway == "DCT"), "{route:?}");
|
|
assert_eq!(route.legs.first().unwrap().from, "LFPG");
|
|
assert_eq!(route.legs.last().unwrap().to, "EGLL");
|
|
assert!(
|
|
route.total_nm > 150.0 && route.total_nm < 400.0,
|
|
"total = {} nm",
|
|
route.total_nm
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn connects_departure_via_given_fix() {
|
|
let conn = fixtures_db();
|
|
// Force LFPG to join the network at CROSS (a SID exit fix, say) rather than
|
|
// the nearest airway point.
|
|
let route =
|
|
routing::plan_route_conn(&conn, "LFPG", "EGLL", None, &["CROSS".to_string()], &[]).unwrap();
|
|
assert!(route.via_airways, "{route:?}");
|
|
let first = route.legs.first().unwrap();
|
|
assert_eq!(first.from, "LFPG");
|
|
assert_eq!(first.to, "CROSS", "should connect via the given fix: {route:?}");
|
|
}
|