use super::*; use crate::data::data_seed::types::LocationData; use solverforge_maps::{BoundingBox, Coord, NetworkConfig, RoadNetwork}; #[test] fn parses_demo_data_ids_case_insensitively() { assert!(matches!( "philadelphia".parse::(), Ok(DemoData::Philadelphia) )); assert!(matches!( "HARTFORD".parse::(), Ok(DemoData::Hartford) )); } #[test] fn generates_city_demo_plan() { for (demo, expected_stops, expected_name) in [ ( DemoData::Philadelphia, visit_count(philadelphia::VISIT_GROUPS), "Philadelphia", ), ( DemoData::Hartford, visit_count(hartford::VISIT_GROUPS), "Hartford", ), ( DemoData::Firenze, visit_count(firenze::VISIT_GROUPS), "Firenze", ), ] { let plan = generate(demo); assert_eq!(plan.name, expected_name); assert_eq!(plan.vehicles.len(), 10); assert_eq!(plan.deliveries.len(), expected_stops); assert!(plan .vehicles .iter() .all(|vehicle| vehicle.delivery_order.is_empty())); } } fn visit_count(groups: &[&[LocationData]]) -> usize { groups.iter().map(|group| group.len()).sum() } #[test] fn demo_delivery_counts_scale_with_ten_vehicles() { assert_eq!(generate(DemoData::Philadelphia).deliveries.len(), 82); assert_eq!(generate(DemoData::Hartford).deliveries.len(), 50); assert_eq!(generate(DemoData::Firenze).deliveries.len(), 80); } #[test] fn demo_plans_have_enough_vehicle_capacity() { for demo in [ DemoData::Philadelphia, DemoData::Hartford, DemoData::Firenze, ] { let plan = generate(demo); let total_capacity: i32 = plan.vehicles.iter().map(|vehicle| vehicle.capacity).sum(); let total_demand: i32 = plan.deliveries.iter().map(|delivery| delivery.demand).sum(); assert!( total_capacity >= total_demand, "{demo:?} demo should be capacity-feasible before route ordering: capacity={total_capacity}, demand={total_demand}" ); } } #[tokio::test] async fn live_demo_locations_are_mutually_reachable_when_enabled() { if std::env::var("SOLVERFORGE_RUN_LIVE_TESTS").ok().as_deref() != Some("1") { return; } for demo in [ DemoData::Philadelphia, DemoData::Hartford, DemoData::Firenze, ] { let plan = generate(demo); let mut named_coords = Vec::new(); for delivery in &plan.deliveries { named_coords.push(( format!("delivery {}", delivery.label), delivery.coord().unwrap(), )); } for vehicle in &plan.vehicles { named_coords.push(( format!("depot {}", vehicle.name), vehicle.depot_coord().unwrap(), )); } let coords: Vec = named_coords.iter().map(|(_, coord)| *coord).collect(); let bbox = BoundingBox::from_coords(&coords).expand_for_routing(&coords); let network = RoadNetwork::load_or_fetch(&bbox, &NetworkConfig::default(), None) .await .unwrap(); let matrix = network.compute_matrix(&coords, None).await; let unreachable = matrix .unreachable_pairs() .into_iter() .map(|(from_idx, to_idx)| { let from_name = &named_coords[from_idx].0; let to_name = &named_coords[to_idx].0; format!("{from_name} -> {to_name}") }) .collect::>(); assert!( unreachable.is_empty(), "{demo:?} has unreachable directed routes: {unreachable:?}" ); } }