use peat_protocol::discovery::capability_query::{CapabilityQuery, CapabilityQueryEngine};
use peat_protocol::discovery::geographic::{GeographicBeacon, GeographicDiscovery, MIN_SQUAD_SIZE};
use peat_protocol::discovery::GeoCoordinate;
use peat_protocol::models::capability::{Capability, CapabilityType};
use peat_protocol::models::node::NodeConfig;
use peat_protocol::models::{CapabilityExt, NodeConfigExt};
use peat_protocol::testing::e2e_harness::E2EHarness;
use std::time::Duration;
use tokio::time::sleep;
#[tokio::test]
async fn test_e2e_geographic_discovery_sync() {
let _harness = E2EHarness::new("geographic_discovery_test");
let sf_position = GeoCoordinate::new(37.7749, -122.4194, 100.0).unwrap();
let mut discovery1 = GeographicDiscovery::new("peer_1".to_string());
let mut discovery2 = GeographicDiscovery::new("peer_2".to_string());
let mut discovery3 = GeographicDiscovery::new("peer_3".to_string());
let beacon1 = GeographicBeacon::new("peer_1".to_string(), sf_position, vec![]);
let beacon2 = GeographicBeacon::new("peer_2".to_string(), sf_position, vec![]);
let beacon3 = GeographicBeacon::new("peer_3".to_string(), sf_position, vec![]);
discovery1.process_beacon(beacon1.clone());
discovery1.process_beacon(beacon2.clone());
discovery1.process_beacon(beacon3.clone());
discovery2.process_beacon(beacon1.clone());
discovery2.process_beacon(beacon2.clone());
discovery2.process_beacon(beacon3.clone());
discovery3.process_beacon(beacon1);
discovery3.process_beacon(beacon2);
discovery3.process_beacon(beacon3);
sleep(Duration::from_millis(100)).await;
assert_eq!(
discovery1.total_platforms(),
3,
"Peer 1 should see 3 platforms"
);
assert_eq!(
discovery2.total_platforms(),
3,
"Peer 2 should see 3 platforms"
);
assert_eq!(
discovery3.total_platforms(),
3,
"Peer 3 should see 3 platforms"
);
assert_eq!(
discovery1.cluster_count(),
1,
"Should form single geographic cluster"
);
assert_eq!(discovery2.cluster_count(), 1);
assert_eq!(discovery3.cluster_count(), 1);
let formable1 = discovery1.find_formable_squads(MIN_SQUAD_SIZE);
assert_eq!(formable1.len(), 1, "Should have 1 formable squad");
assert!(
formable1[0].can_form_squad(MIN_SQUAD_SIZE),
"Cluster should meet minimum squad size"
);
assert!(
discovery1.should_initiate_squad_formation(),
"peer_1 should be leader (lowest ID)"
);
assert!(
!discovery2.should_initiate_squad_formation(),
"peer_2 should not be leader"
);
assert!(
!discovery3.should_initiate_squad_formation(),
"peer_3 should not be leader"
);
let members1 = discovery1.get_squad_members(5).unwrap();
let members2 = discovery2.get_squad_members(5).unwrap();
let members3 = discovery3.get_squad_members(5).unwrap();
assert_eq!(members1, members2, "Squad members should be consistent");
assert_eq!(members2, members3, "Squad members should be consistent");
assert_eq!(members1.len(), 3, "Should have all 3 members");
}
#[tokio::test]
async fn test_e2e_capability_based_discovery() {
let _harness = E2EHarness::new("capability_discovery_test");
let mut node1 = NodeConfig::new("UAV".to_string());
node1.id = "uav_1".to_string();
node1.add_capability(Capability::new(
"sensor1".to_string(),
"EO/IR Sensor".to_string(),
CapabilityType::Sensor,
0.95,
));
node1.add_capability(Capability::new(
"comms1".to_string(),
"Radio Link".to_string(),
CapabilityType::Communication,
0.90,
));
let mut node2 = NodeConfig::new("UAV".to_string());
node2.id = "uav_2".to_string();
node2.add_capability(Capability::new(
"sensor2".to_string(),
"SAR Sensor".to_string(),
CapabilityType::Sensor,
0.85,
));
let mut node3 = NodeConfig::new("Ground Station".to_string());
node3.id = "gs_1".to_string();
node3.add_capability(Capability::new(
"compute3".to_string(),
"Edge Compute".to_string(),
CapabilityType::Compute,
0.92,
));
node3.add_capability(Capability::new(
"comms3".to_string(),
"Satcom Link".to_string(),
CapabilityType::Communication,
0.88,
));
let mut node4 = NodeConfig::new("UAV".to_string());
node4.id = "uav_3".to_string();
node4.add_capability(Capability::new(
"sensor4".to_string(),
"EO Sensor".to_string(),
CapabilityType::Sensor,
0.98,
));
node4.add_capability(Capability::new(
"comms4".to_string(),
"Mesh Radio".to_string(),
CapabilityType::Communication,
0.94,
));
node4.add_capability(Capability::new(
"compute4".to_string(),
"Onboard Compute".to_string(),
CapabilityType::Compute,
0.80,
));
let nodes = vec![node1, node2, node3, node4];
let engine = CapabilityQueryEngine::new();
let query_sensors = CapabilityQuery::builder()
.require_type(CapabilityType::Sensor)
.min_confidence(0.80)
.build();
let sensor_matches = engine.query_platforms(&query_sensors, &nodes);
assert_eq!(sensor_matches.len(), 3, "Should find 3 nodes with sensors");
assert_eq!(sensor_matches[0].entity.id, "uav_3");
assert!(sensor_matches[0].score > sensor_matches[1].score);
let query_sensor_comms = CapabilityQuery::builder()
.require_type(CapabilityType::Sensor)
.require_type(CapabilityType::Communication)
.min_confidence(0.85)
.build();
let combo_matches = engine.query_platforms(&query_sensor_comms, &nodes);
assert_eq!(
combo_matches.len(),
2,
"Should find 2 nodes with both capabilities"
);
let ids: Vec<String> = combo_matches.iter().map(|m| m.entity.id.clone()).collect();
assert!(ids.contains(&"uav_1".to_string()));
assert!(ids.contains(&"uav_3".to_string()));
let query_complex = CapabilityQuery::builder()
.require_type(CapabilityType::Sensor)
.prefer_type(CapabilityType::Communication)
.prefer_type(CapabilityType::Compute)
.min_confidence(0.80)
.limit(2)
.build();
let complex_matches = engine.query_platforms(&query_complex, &nodes);
assert_eq!(complex_matches.len(), 2, "Should limit results to 2");
assert_eq!(complex_matches[0].entity.id, "uav_3");
let query_min_count = CapabilityQuery::builder().min_capability_count(2).build();
let min_count_matches = engine.query_platforms(&query_min_count, &nodes);
assert_eq!(
min_count_matches.len(),
3,
"Should find 3 nodes with 2+ capabilities"
);
}
#[tokio::test]
async fn test_e2e_multi_region_discovery() {
let _harness = E2EHarness::new("multi_region_test");
let sf_position = GeoCoordinate::new(37.7749, -122.4194, 100.0).unwrap();
let la_position = GeoCoordinate::new(34.0522, -118.2437, 100.0).unwrap();
let mut discovery = GeographicDiscovery::new("observer".to_string());
let beacon_sf1 = GeographicBeacon::new("sf_node_1".to_string(), sf_position, vec![]);
let beacon_sf2 = GeographicBeacon::new("sf_node_2".to_string(), sf_position, vec![]);
let beacon_la1 = GeographicBeacon::new("la_node_1".to_string(), la_position, vec![]);
let beacon_la2 = GeographicBeacon::new("la_node_2".to_string(), la_position, vec![]);
discovery.process_beacon(beacon_sf1);
discovery.process_beacon(beacon_sf2);
discovery.process_beacon(beacon_la1);
discovery.process_beacon(beacon_la2);
sleep(Duration::from_millis(100)).await;
assert_eq!(
discovery.total_platforms(),
4,
"Should discover all 4 platforms"
);
assert_eq!(
discovery.cluster_count(),
2,
"Should form 2 geographic clusters (SF and LA)"
);
let formable_squads = discovery.find_formable_squads(MIN_SQUAD_SIZE);
assert_eq!(
formable_squads.len(),
2,
"Both regions should be able to form squads"
);
for cluster in formable_squads {
assert_eq!(
cluster.platforms.len(),
2,
"Each region should have 2 platforms"
);
}
}
#[tokio::test]
async fn test_e2e_beacon_expiration_cleanup() {
let _harness = E2EHarness::new("beacon_expiration_test");
let position = GeoCoordinate::new(37.7749, -122.4194, 100.0).unwrap();
let mut discovery = GeographicDiscovery::new("node_1".to_string());
let mut old_beacon = GeographicBeacon::new("node_2".to_string(), position, vec![]);
old_beacon.timestamp = 0;
let fresh_beacon = GeographicBeacon::new("node_3".to_string(), position, vec![]);
discovery.process_beacon(old_beacon);
discovery.process_beacon(fresh_beacon);
assert_eq!(
discovery.total_platforms(),
2,
"Should have 2 platforms before cleanup"
);
discovery.cleanup_expired();
assert_eq!(
discovery.total_platforms(),
1,
"Should have 1 platform after cleanup"
);
let formable = discovery.find_formable_squads(MIN_SQUAD_SIZE);
assert_eq!(formable.len(), 0, "Should not be able to form squad");
}
#[tokio::test]
async fn test_e2e_capability_statistics() {
let _harness = E2EHarness::new("capability_stats_test");
let mut nodes = Vec::new();
for i in 1..=5 {
let mut node = NodeConfig::new("UAV".to_string());
node.id = format!("node_{}", i);
node.add_capability(Capability::new(
format!("sensor_{}", i),
"Sensor".to_string(),
CapabilityType::Sensor,
0.7 + (i as f32 * 0.05),
));
if i <= 3 {
node.add_capability(Capability::new(
format!("comms_{}", i),
"Comms".to_string(),
CapabilityType::Communication,
0.8 + (i as f32 * 0.03),
));
}
if i <= 2 {
node.add_capability(Capability::new(
format!("compute_{}", i),
"Compute".to_string(),
CapabilityType::Compute,
0.75 + (i as f32 * 0.05),
));
}
nodes.push(node);
}
let engine = CapabilityQueryEngine::new();
let stats = engine.platform_capability_stats(&nodes);
let sensor_stats = stats.get(&CapabilityType::Sensor).unwrap();
assert_eq!(sensor_stats.count, 5, "All nodes should have sensors");
assert_eq!(sensor_stats.min_confidence, 0.75);
assert_eq!(sensor_stats.max_confidence, 0.95);
assert!((sensor_stats.avg_confidence - 0.85).abs() < 0.01);
let comms_stats = stats.get(&CapabilityType::Communication).unwrap();
assert_eq!(comms_stats.count, 3);
assert_eq!(comms_stats.min_confidence, 0.83);
assert_eq!(comms_stats.max_confidence, 0.89);
let compute_stats = stats.get(&CapabilityType::Compute).unwrap();
assert_eq!(compute_stats.count, 2);
assert_eq!(compute_stats.min_confidence, 0.8);
assert_eq!(compute_stats.max_confidence, 0.85);
}