1use crate::config::MeshConfig;
8use crate::hierarchy::HierarchyStrategy;
9use crate::routing::MeshRouter;
10use crate::transport::{MeshTransport, NodeId, TransportError, TransportManager};
11use std::fmt;
12use std::sync::{Arc, RwLock};
13use std::time::Instant;
14use tokio::sync::broadcast;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum MeshState {
21 Created,
23 Starting,
25 Running,
27 Stopping,
29 Stopped,
31}
32
33impl fmt::Display for MeshState {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 match self {
36 MeshState::Created => write!(f, "created"),
37 MeshState::Starting => write!(f, "starting"),
38 MeshState::Running => write!(f, "running"),
39 MeshState::Stopping => write!(f, "stopping"),
40 MeshState::Stopped => write!(f, "stopped"),
41 }
42 }
43}
44
45#[derive(Debug)]
49pub enum MeshError {
50 NotRunning,
52 AlreadyRunning,
54 InvalidConfig(String),
56 Transport(TransportError),
58 Other(String),
60}
61
62impl fmt::Display for MeshError {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 match self {
65 MeshError::NotRunning => write!(f, "mesh is not running"),
66 MeshError::AlreadyRunning => write!(f, "mesh is already running"),
67 MeshError::InvalidConfig(msg) => write!(f, "invalid configuration: {}", msg),
68 MeshError::Transport(err) => write!(f, "transport error: {}", err),
69 MeshError::Other(msg) => write!(f, "{}", msg),
70 }
71 }
72}
73
74impl std::error::Error for MeshError {
75 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
76 match self {
77 MeshError::Transport(err) => Some(err),
78 _ => None,
79 }
80 }
81}
82
83impl From<TransportError> for MeshError {
84 fn from(err: TransportError) -> Self {
85 MeshError::Transport(err)
86 }
87}
88
89#[derive(Debug, Clone)]
93pub enum HiveMeshEvent {
94 StateChanged(MeshState),
96 PeerJoined(NodeId),
98 PeerLeft(NodeId),
100 TopologyChanged(Box<crate::topology::TopologyEvent>),
102}
103
104#[derive(Debug, Clone)]
108pub struct MeshStatus {
109 pub state: MeshState,
111 pub peer_count: usize,
113 pub node_id: String,
115 pub uptime: std::time::Duration,
117}
118
119const EVENT_CHANNEL_CAPACITY: usize = 256;
122
123pub struct HiveMesh {
128 config: MeshConfig,
129 node_id: String,
130 state: RwLock<MeshState>,
131 transport: Option<Arc<dyn MeshTransport>>,
132 transport_manager: Option<TransportManager>,
133 hierarchy: Option<Arc<dyn HierarchyStrategy>>,
134 router: Option<MeshRouter>,
135 bandwidth: Option<crate::qos::BandwidthAllocation>,
137 preemption: Option<crate::qos::PreemptionController>,
138 device_keypair: Option<crate::security::DeviceKeypair>,
140 formation_key: Option<crate::security::FormationKey>,
141 discovery: RwLock<Option<Box<dyn crate::discovery::DiscoveryStrategy>>>,
143 beacon_broadcaster: Option<crate::beacon::BeaconBroadcaster>,
145 beacon_observer: Option<Arc<crate::beacon::BeaconObserver>>,
146 beacon_janitor: Option<crate::beacon::BeaconJanitor>,
147 topology_manager: Option<crate::topology::TopologyManager>,
149 event_tx: broadcast::Sender<HiveMeshEvent>,
150 #[cfg(feature = "broker")]
151 broker_event_tx: broadcast::Sender<crate::broker::state::MeshEvent>,
152 started_at: RwLock<Option<Instant>>,
153}
154
155impl HiveMesh {
156 pub fn new(config: MeshConfig) -> Self {
160 let node_id = config
161 .node_id
162 .clone()
163 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
164 let (event_tx, _) = broadcast::channel(EVENT_CHANNEL_CAPACITY);
165 #[cfg(feature = "broker")]
166 let (broker_event_tx, _) = broadcast::channel(EVENT_CHANNEL_CAPACITY);
167 Self {
168 config,
169 node_id,
170 state: RwLock::new(MeshState::Created),
171 transport: None,
172 transport_manager: None,
173 hierarchy: None,
174 router: None,
175 bandwidth: None,
176 preemption: None,
177 device_keypair: None,
178 formation_key: None,
179 discovery: RwLock::new(None),
180 beacon_broadcaster: None,
181 beacon_observer: None,
182 beacon_janitor: None,
183 topology_manager: None,
184 event_tx,
185 #[cfg(feature = "broker")]
186 broker_event_tx,
187 started_at: RwLock::new(None),
188 }
189 }
190
191 pub fn start(&self) -> Result<(), MeshError> {
193 let mut state = self.state.write().unwrap();
194 match *state {
195 MeshState::Created | MeshState::Stopped => {}
196 MeshState::Running | MeshState::Starting | MeshState::Stopping => {
197 return Err(MeshError::AlreadyRunning);
198 }
199 }
200
201 *state = MeshState::Starting;
202 let _ = self
203 .event_tx
204 .send(HiveMeshEvent::StateChanged(MeshState::Starting));
205
206 *state = MeshState::Running;
207 *self.started_at.write().unwrap() = Some(Instant::now());
208 let _ = self
209 .event_tx
210 .send(HiveMeshEvent::StateChanged(MeshState::Running));
211
212 #[cfg(feature = "broker")]
213 self.emit_broker_event(crate::broker::state::MeshEvent::TopologyChanged {
214 new_role: "standalone".to_string(),
215 peer_count: 0,
216 });
217
218 Ok(())
219 }
220
221 pub fn stop(&self) -> Result<(), MeshError> {
223 let mut state = self.state.write().unwrap();
224 match *state {
225 MeshState::Running => {}
226 _ => return Err(MeshError::NotRunning),
227 }
228
229 *state = MeshState::Stopping;
230 let _ = self
231 .event_tx
232 .send(HiveMeshEvent::StateChanged(MeshState::Stopping));
233
234 *state = MeshState::Stopped;
235 let _ = self
236 .event_tx
237 .send(HiveMeshEvent::StateChanged(MeshState::Stopped));
238
239 #[cfg(feature = "broker")]
240 self.emit_broker_event(crate::broker::state::MeshEvent::TopologyChanged {
241 new_role: "stopped".to_string(),
242 peer_count: 0,
243 });
244
245 Ok(())
246 }
247
248 pub fn state(&self) -> MeshState {
250 *self.state.read().unwrap()
251 }
252
253 pub fn status(&self) -> MeshStatus {
255 let state = *self.state.read().unwrap();
256 let uptime = self
257 .started_at
258 .read()
259 .unwrap()
260 .map(|t| t.elapsed())
261 .unwrap_or_default();
262 let peer_count = self.transport.as_ref().map(|t| t.peer_count()).unwrap_or(0);
263
264 MeshStatus {
265 state,
266 peer_count,
267 node_id: self.node_id.clone(),
268 uptime,
269 }
270 }
271
272 pub fn config(&self) -> &MeshConfig {
274 &self.config
275 }
276
277 pub fn node_id(&self) -> &str {
279 &self.node_id
280 }
281
282 pub fn subscribe_events(&self) -> broadcast::Receiver<HiveMeshEvent> {
284 self.event_tx.subscribe()
285 }
286
287 pub fn set_transport(&mut self, transport: Arc<dyn MeshTransport>) {
289 self.transport = Some(transport);
290 }
291
292 pub fn set_transport_manager(&mut self, tm: TransportManager) {
294 self.transport_manager = Some(tm);
295 }
296
297 pub fn transport_manager(&self) -> Option<&TransportManager> {
299 self.transport_manager.as_ref()
300 }
301
302 pub fn set_hierarchy(&mut self, hierarchy: Arc<dyn HierarchyStrategy>) {
304 self.hierarchy = Some(hierarchy);
305 }
306
307 pub fn transport(&self) -> Option<&Arc<dyn MeshTransport>> {
309 self.transport.as_ref()
310 }
311
312 pub fn hierarchy(&self) -> Option<&Arc<dyn HierarchyStrategy>> {
314 self.hierarchy.as_ref()
315 }
316
317 pub fn router(&self) -> Option<&MeshRouter> {
319 self.router.as_ref()
320 }
321
322 pub fn set_bandwidth(&mut self, bw: crate::qos::BandwidthAllocation) {
326 self.bandwidth = Some(bw);
327 }
328
329 pub fn bandwidth(&self) -> Option<&crate::qos::BandwidthAllocation> {
331 self.bandwidth.as_ref()
332 }
333
334 pub fn set_preemption(&mut self, pc: crate::qos::PreemptionController) {
336 self.preemption = Some(pc);
337 }
338
339 pub fn preemption(&self) -> Option<&crate::qos::PreemptionController> {
341 self.preemption.as_ref()
342 }
343
344 pub fn set_device_keypair(&mut self, kp: crate::security::DeviceKeypair) {
348 self.device_keypair = Some(kp);
349 }
350
351 pub fn device_keypair(&self) -> Option<&crate::security::DeviceKeypair> {
353 self.device_keypair.as_ref()
354 }
355
356 pub fn set_formation_key(&mut self, fk: crate::security::FormationKey) {
358 self.formation_key = Some(fk);
359 }
360
361 pub fn formation_key(&self) -> Option<&crate::security::FormationKey> {
363 self.formation_key.as_ref()
364 }
365
366 pub fn set_discovery(&self, strategy: Box<dyn crate::discovery::DiscoveryStrategy>) {
374 *self.discovery.write().unwrap() = Some(strategy);
375 }
376
377 pub fn discovery(&self) -> &RwLock<Option<Box<dyn crate::discovery::DiscoveryStrategy>>> {
379 &self.discovery
380 }
381
382 pub fn set_beacon_broadcaster(&mut self, bb: crate::beacon::BeaconBroadcaster) {
386 self.beacon_broadcaster = Some(bb);
387 }
388
389 pub fn beacon_broadcaster(&self) -> Option<&crate::beacon::BeaconBroadcaster> {
391 self.beacon_broadcaster.as_ref()
392 }
393
394 pub fn set_beacon_observer(&mut self, bo: Arc<crate::beacon::BeaconObserver>) {
396 self.beacon_observer = Some(bo);
397 }
398
399 pub fn beacon_observer(&self) -> Option<&Arc<crate::beacon::BeaconObserver>> {
401 self.beacon_observer.as_ref()
402 }
403
404 pub fn set_beacon_janitor(&mut self, bj: crate::beacon::BeaconJanitor) {
406 self.beacon_janitor = Some(bj);
407 }
408
409 pub fn beacon_janitor(&self) -> Option<&crate::beacon::BeaconJanitor> {
411 self.beacon_janitor.as_ref()
412 }
413
414 pub fn set_topology_manager(&mut self, tm: crate::topology::TopologyManager) {
418 self.topology_manager = Some(tm);
419 }
420
421 pub fn topology_manager(&self) -> Option<&crate::topology::TopologyManager> {
423 self.topology_manager.as_ref()
424 }
425
426 #[cfg(feature = "broker")]
428 pub fn emit_mesh_event(&self, event: crate::broker::state::MeshEvent) {
429 let _ = self.broker_event_tx.send(event);
430 }
431
432 #[cfg(feature = "broker")]
433 fn emit_broker_event(&self, event: crate::broker::state::MeshEvent) {
434 let _ = self.broker_event_tx.send(event);
435 }
436}
437
438#[cfg(feature = "broker")]
441#[async_trait::async_trait]
442impl crate::broker::state::MeshBrokerState for HiveMesh {
443 fn node_info(&self) -> crate::broker::state::MeshNodeInfo {
444 let uptime = self
445 .started_at
446 .read()
447 .unwrap()
448 .map(|t| t.elapsed().as_secs())
449 .unwrap_or(0);
450 crate::broker::state::MeshNodeInfo {
451 node_id: self.node_id.clone(),
452 uptime_secs: uptime,
453 version: env!("CARGO_PKG_VERSION").to_string(),
454 }
455 }
456
457 async fn list_peers(&self) -> Vec<crate::broker::state::PeerSummary> {
458 let Some(transport) = &self.transport else {
459 return vec![];
460 };
461 transport
462 .connected_peers()
463 .into_iter()
464 .map(|peer_id| {
465 let health = transport.get_peer_health(&peer_id);
466 crate::broker::state::PeerSummary {
467 id: peer_id.to_string(),
468 connected: true,
469 state: health
470 .as_ref()
471 .map(|h| h.state.to_string())
472 .unwrap_or_else(|| "unknown".to_string()),
473 rtt_ms: health.map(|h| h.rtt_ms as u64),
474 }
475 })
476 .collect()
477 }
478
479 async fn get_peer(&self, id: &str) -> Option<crate::broker::state::PeerSummary> {
480 let transport = self.transport.as_ref()?;
481 let node_id = NodeId::new(id.to_string());
482 if transport.is_connected(&node_id) {
483 let health = transport.get_peer_health(&node_id);
484 Some(crate::broker::state::PeerSummary {
485 id: id.to_string(),
486 connected: true,
487 state: health
488 .as_ref()
489 .map(|h| h.state.to_string())
490 .unwrap_or_else(|| "unknown".to_string()),
491 rtt_ms: health.map(|h| h.rtt_ms as u64),
492 })
493 } else {
494 None
495 }
496 }
497
498 fn topology(&self) -> crate::broker::state::TopologySummary {
499 let peer_count = self.transport.as_ref().map(|t| t.peer_count()).unwrap_or(0);
500 crate::broker::state::TopologySummary {
501 peer_count,
502 role: "standalone".to_string(),
503 hierarchy_level: 0,
504 }
505 }
506
507 fn subscribe_events(&self) -> broadcast::Receiver<crate::broker::state::MeshEvent> {
508 self.broker_event_tx.subscribe()
509 }
510}
511
512pub struct HiveMeshBuilder {
516 config: MeshConfig,
517 transport: Option<Arc<dyn MeshTransport>>,
518 transport_manager: Option<TransportManager>,
519 hierarchy: Option<Arc<dyn HierarchyStrategy>>,
520 router: Option<MeshRouter>,
521 bandwidth: Option<crate::qos::BandwidthAllocation>,
522 preemption: Option<crate::qos::PreemptionController>,
523 device_keypair: Option<crate::security::DeviceKeypair>,
524 formation_key: Option<crate::security::FormationKey>,
525 discovery: Option<Box<dyn crate::discovery::DiscoveryStrategy>>,
526 beacon_broadcaster: Option<crate::beacon::BeaconBroadcaster>,
527 beacon_observer: Option<Arc<crate::beacon::BeaconObserver>>,
528 beacon_janitor: Option<crate::beacon::BeaconJanitor>,
529 topology_manager: Option<crate::topology::TopologyManager>,
530}
531
532impl HiveMeshBuilder {
533 pub fn new(config: MeshConfig) -> Self {
535 Self {
536 config,
537 transport: None,
538 transport_manager: None,
539 hierarchy: None,
540 router: None,
541 bandwidth: None,
542 preemption: None,
543 device_keypair: None,
544 formation_key: None,
545 discovery: None,
546 beacon_broadcaster: None,
547 beacon_observer: None,
548 beacon_janitor: None,
549 topology_manager: None,
550 }
551 }
552
553 pub fn with_transport(mut self, transport: Arc<dyn MeshTransport>) -> Self {
555 self.transport = Some(transport);
556 self
557 }
558
559 pub fn with_transport_manager(mut self, tm: TransportManager) -> Self {
561 self.transport_manager = Some(tm);
562 self
563 }
564
565 pub fn with_hierarchy(mut self, hierarchy: Arc<dyn HierarchyStrategy>) -> Self {
567 self.hierarchy = Some(hierarchy);
568 self
569 }
570
571 pub fn with_router(mut self, router: MeshRouter) -> Self {
573 self.router = Some(router);
574 self
575 }
576
577 pub fn with_bandwidth(mut self, bw: crate::qos::BandwidthAllocation) -> Self {
579 self.bandwidth = Some(bw);
580 self
581 }
582
583 pub fn with_preemption(mut self, pc: crate::qos::PreemptionController) -> Self {
585 self.preemption = Some(pc);
586 self
587 }
588
589 pub fn with_device_keypair(mut self, kp: crate::security::DeviceKeypair) -> Self {
591 self.device_keypair = Some(kp);
592 self
593 }
594
595 pub fn with_device_keypair_from_seed(
599 mut self,
600 seed: &[u8],
601 context: &str,
602 ) -> Result<Self, MeshError> {
603 let kp = crate::security::DeviceKeypair::from_seed(seed, context)
604 .map_err(|e| MeshError::InvalidConfig(e.to_string()))?;
605 self.device_keypair = Some(kp);
606 Ok(self)
607 }
608
609 pub fn with_formation_key(mut self, fk: crate::security::FormationKey) -> Self {
611 self.formation_key = Some(fk);
612 self
613 }
614
615 pub fn with_discovery(
617 mut self,
618 strategy: Box<dyn crate::discovery::DiscoveryStrategy>,
619 ) -> Self {
620 self.discovery = Some(strategy);
621 self
622 }
623
624 pub fn with_beacon_broadcaster(mut self, bb: crate::beacon::BeaconBroadcaster) -> Self {
626 self.beacon_broadcaster = Some(bb);
627 self
628 }
629
630 pub fn with_beacon_observer(mut self, bo: Arc<crate::beacon::BeaconObserver>) -> Self {
632 self.beacon_observer = Some(bo);
633 self
634 }
635
636 pub fn with_beacon_janitor(mut self, bj: crate::beacon::BeaconJanitor) -> Self {
638 self.beacon_janitor = Some(bj);
639 self
640 }
641
642 pub fn with_topology_manager(mut self, tm: crate::topology::TopologyManager) -> Self {
644 self.topology_manager = Some(tm);
645 self
646 }
647
648 pub fn build(self) -> HiveMesh {
650 let node_id = self
651 .config
652 .node_id
653 .clone()
654 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
655 let (event_tx, _) = broadcast::channel(EVENT_CHANNEL_CAPACITY);
656 #[cfg(feature = "broker")]
657 let (broker_event_tx, _) = broadcast::channel(EVENT_CHANNEL_CAPACITY);
658
659 HiveMesh {
660 config: self.config,
661 node_id,
662 state: RwLock::new(MeshState::Created),
663 transport: self.transport,
664 transport_manager: self.transport_manager,
665 hierarchy: self.hierarchy,
666 router: self.router,
667 bandwidth: self.bandwidth,
668 preemption: self.preemption,
669 device_keypair: self.device_keypair,
670 formation_key: self.formation_key,
671 discovery: RwLock::new(self.discovery),
672 beacon_broadcaster: self.beacon_broadcaster,
673 beacon_observer: self.beacon_observer,
674 beacon_janitor: self.beacon_janitor,
675 topology_manager: self.topology_manager,
676 event_tx,
677 #[cfg(feature = "broker")]
678 broker_event_tx,
679 started_at: RwLock::new(None),
680 }
681 }
682}
683
684#[cfg(test)]
687mod tests {
688 use super::*;
689 use crate::config::MeshDiscoveryConfig;
690 use crate::transport::PeerEventReceiver;
691 use async_trait::async_trait;
692 use std::time::Duration;
693
694 struct MockTransport {
697 peers: Vec<NodeId>,
698 }
699
700 impl MockTransport {
701 fn new(peers: Vec<NodeId>) -> Self {
702 Self { peers }
703 }
704
705 fn empty() -> Self {
706 Self { peers: vec![] }
707 }
708 }
709
710 #[async_trait]
711 impl MeshTransport for MockTransport {
712 async fn start(&self) -> crate::transport::Result<()> {
713 Ok(())
714 }
715 async fn stop(&self) -> crate::transport::Result<()> {
716 Ok(())
717 }
718 async fn connect(
719 &self,
720 _peer_id: &NodeId,
721 ) -> crate::transport::Result<Box<dyn crate::transport::MeshConnection>> {
722 Err(TransportError::NotStarted)
723 }
724 async fn disconnect(&self, _peer_id: &NodeId) -> crate::transport::Result<()> {
725 Ok(())
726 }
727 fn get_connection(
728 &self,
729 _peer_id: &NodeId,
730 ) -> Option<Box<dyn crate::transport::MeshConnection>> {
731 None
732 }
733 fn peer_count(&self) -> usize {
734 self.peers.len()
735 }
736 fn connected_peers(&self) -> Vec<NodeId> {
737 self.peers.clone()
738 }
739 fn subscribe_peer_events(&self) -> PeerEventReceiver {
740 let (_tx, rx) = tokio::sync::mpsc::channel(1);
741 rx
742 }
743 }
744
745 #[test]
748 fn test_new_with_default_config() {
749 let mesh = HiveMesh::new(MeshConfig::default());
750 assert_eq!(mesh.state(), MeshState::Created);
751 assert!(!mesh.node_id().is_empty());
752 }
753
754 #[test]
755 fn test_new_with_explicit_node_id() {
756 let cfg = MeshConfig {
757 node_id: Some("my-node".to_string()),
758 ..Default::default()
759 };
760 let mesh = HiveMesh::new(cfg);
761 assert_eq!(mesh.node_id(), "my-node");
762 }
763
764 #[test]
765 fn test_new_auto_generates_uuid_node_id() {
766 let mesh = HiveMesh::new(MeshConfig::default());
767 assert_eq!(mesh.node_id().len(), 36);
769 assert_eq!(mesh.node_id().chars().filter(|&c| c == '-').count(), 4);
770 }
771
772 #[test]
775 fn test_start_transitions_to_running() {
776 let mesh = HiveMesh::new(MeshConfig::default());
777 assert!(mesh.start().is_ok());
778 assert_eq!(mesh.state(), MeshState::Running);
779 }
780
781 #[test]
782 fn test_start_when_already_running_returns_error() {
783 let mesh = HiveMesh::new(MeshConfig::default());
784 mesh.start().unwrap();
785 let err = mesh.start().unwrap_err();
786 assert!(matches!(err, MeshError::AlreadyRunning));
787 }
788
789 #[test]
790 fn test_stop_transitions_to_stopped() {
791 let mesh = HiveMesh::new(MeshConfig::default());
792 mesh.start().unwrap();
793 assert!(mesh.stop().is_ok());
794 assert_eq!(mesh.state(), MeshState::Stopped);
795 }
796
797 #[test]
798 fn test_stop_when_not_running_returns_error() {
799 let mesh = HiveMesh::new(MeshConfig::default());
800 let err = mesh.stop().unwrap_err();
801 assert!(matches!(err, MeshError::NotRunning));
802 }
803
804 #[test]
805 fn test_restart_after_stop() {
806 let mesh = HiveMesh::new(MeshConfig::default());
807 mesh.start().unwrap();
808 mesh.stop().unwrap();
809 assert!(mesh.start().is_ok());
810 assert_eq!(mesh.state(), MeshState::Running);
811 }
812
813 #[test]
814 fn test_stop_when_created_returns_error() {
815 let mesh = HiveMesh::new(MeshConfig::default());
816 assert!(matches!(mesh.stop().unwrap_err(), MeshError::NotRunning));
817 }
818
819 #[test]
820 fn test_stop_when_already_stopped_returns_error() {
821 let mesh = HiveMesh::new(MeshConfig::default());
822 mesh.start().unwrap();
823 mesh.stop().unwrap();
824 assert!(matches!(mesh.stop().unwrap_err(), MeshError::NotRunning));
825 }
826
827 #[test]
830 fn test_status_before_start() {
831 let cfg = MeshConfig {
832 node_id: Some("status-node".to_string()),
833 ..Default::default()
834 };
835 let mesh = HiveMesh::new(cfg);
836 let status = mesh.status();
837 assert_eq!(status.state, MeshState::Created);
838 assert_eq!(status.peer_count, 0);
839 assert_eq!(status.node_id, "status-node");
840 assert_eq!(status.uptime, Duration::ZERO);
841 }
842
843 #[test]
844 fn test_status_while_running() {
845 let mesh = HiveMesh::new(MeshConfig {
846 node_id: Some("running-node".to_string()),
847 ..Default::default()
848 });
849 mesh.start().unwrap();
850 let status = mesh.status();
851 assert_eq!(status.state, MeshState::Running);
852 assert_eq!(status.node_id, "running-node");
853 assert!(status.uptime <= Duration::from_secs(1));
855 }
856
857 #[test]
858 fn test_status_peer_count_with_transport() {
859 let peers = vec![NodeId::new("p1".into()), NodeId::new("p2".into())];
860 let mut mesh = HiveMesh::new(MeshConfig::default());
861 mesh.set_transport(Arc::new(MockTransport::new(peers)));
862 let status = mesh.status();
863 assert_eq!(status.peer_count, 2);
864 }
865
866 #[test]
869 fn test_config_accessor() {
870 let cfg = MeshConfig {
871 node_id: Some("cfg-test".to_string()),
872 discovery: MeshDiscoveryConfig {
873 mdns_enabled: false,
874 ..Default::default()
875 },
876 ..Default::default()
877 };
878 let mesh = HiveMesh::new(cfg);
879 assert_eq!(mesh.config().node_id.as_deref(), Some("cfg-test"));
880 assert!(!mesh.config().discovery.mdns_enabled);
881 }
882
883 #[test]
886 fn test_subscribe_events_receives_state_changes() {
887 let mesh = HiveMesh::new(MeshConfig::default());
888 let mut rx = mesh.subscribe_events();
889
890 mesh.start().unwrap();
891
892 let evt1 = rx.try_recv().unwrap();
894 assert!(matches!(
895 evt1,
896 HiveMeshEvent::StateChanged(MeshState::Starting)
897 ));
898 let evt2 = rx.try_recv().unwrap();
899 assert!(matches!(
900 evt2,
901 HiveMeshEvent::StateChanged(MeshState::Running)
902 ));
903 }
904
905 #[test]
906 fn test_subscribe_events_receives_stop_events() {
907 let mesh = HiveMesh::new(MeshConfig::default());
908 let mut rx = mesh.subscribe_events();
909
910 mesh.start().unwrap();
911 let _ = rx.try_recv();
913 let _ = rx.try_recv();
914
915 mesh.stop().unwrap();
916
917 let evt1 = rx.try_recv().unwrap();
918 assert!(matches!(
919 evt1,
920 HiveMeshEvent::StateChanged(MeshState::Stopping)
921 ));
922 let evt2 = rx.try_recv().unwrap();
923 assert!(matches!(
924 evt2,
925 HiveMeshEvent::StateChanged(MeshState::Stopped)
926 ));
927 }
928
929 #[test]
930 fn test_multiple_subscribers() {
931 let mesh = HiveMesh::new(MeshConfig::default());
932 let mut rx1 = mesh.subscribe_events();
933 let mut rx2 = mesh.subscribe_events();
934
935 mesh.start().unwrap();
936
937 assert!(rx1.try_recv().is_ok());
939 assert!(rx2.try_recv().is_ok());
940 }
941
942 #[test]
945 fn test_set_transport() {
946 let mut mesh = HiveMesh::new(MeshConfig::default());
947 assert!(mesh.transport().is_none());
948
949 mesh.set_transport(Arc::new(MockTransport::empty()));
950 assert!(mesh.transport().is_some());
951 }
952
953 #[test]
954 fn test_set_hierarchy() {
955 use crate::beacon::HierarchyLevel;
956 use crate::hierarchy::{NodeRole, StaticHierarchyStrategy};
957
958 let mut mesh = HiveMesh::new(MeshConfig::default());
959 assert!(mesh.hierarchy().is_none());
960
961 let strategy = StaticHierarchyStrategy {
962 assigned_level: HierarchyLevel::Platoon,
963 assigned_role: NodeRole::Leader,
964 };
965 mesh.set_hierarchy(Arc::new(strategy));
966 assert!(mesh.hierarchy().is_some());
967 }
968
969 #[test]
970 fn test_router_initially_none() {
971 let mesh = HiveMesh::new(MeshConfig::default());
972 assert!(mesh.router().is_none());
973 }
974
975 #[test]
978 fn test_mesh_state_display() {
979 assert_eq!(MeshState::Created.to_string(), "created");
980 assert_eq!(MeshState::Starting.to_string(), "starting");
981 assert_eq!(MeshState::Running.to_string(), "running");
982 assert_eq!(MeshState::Stopping.to_string(), "stopping");
983 assert_eq!(MeshState::Stopped.to_string(), "stopped");
984 }
985
986 #[test]
987 fn test_mesh_state_equality() {
988 assert_eq!(MeshState::Created, MeshState::Created);
989 assert_ne!(MeshState::Created, MeshState::Running);
990 }
991
992 #[test]
993 fn test_mesh_state_clone_copy() {
994 let s = MeshState::Running;
995 let copied = s;
996 assert_eq!(s, copied);
998 }
999
1000 #[test]
1001 fn test_mesh_state_debug() {
1002 let debug = format!("{:?}", MeshState::Running);
1003 assert!(debug.contains("Running"));
1004 }
1005
1006 #[test]
1009 fn test_mesh_error_display_not_running() {
1010 let err = MeshError::NotRunning;
1011 assert_eq!(err.to_string(), "mesh is not running");
1012 }
1013
1014 #[test]
1015 fn test_mesh_error_display_already_running() {
1016 let err = MeshError::AlreadyRunning;
1017 assert_eq!(err.to_string(), "mesh is already running");
1018 }
1019
1020 #[test]
1021 fn test_mesh_error_display_invalid_config() {
1022 let err = MeshError::InvalidConfig("bad value".to_string());
1023 assert_eq!(err.to_string(), "invalid configuration: bad value");
1024 }
1025
1026 #[test]
1027 fn test_mesh_error_display_transport() {
1028 let terr = TransportError::NotStarted;
1029 let err = MeshError::Transport(terr);
1030 assert!(err.to_string().contains("Transport not started"));
1031 }
1032
1033 #[test]
1034 fn test_mesh_error_display_other() {
1035 let err = MeshError::Other("something went wrong".to_string());
1036 assert_eq!(err.to_string(), "something went wrong");
1037 }
1038
1039 #[test]
1040 fn test_mesh_error_source_transport() {
1041 use std::error::Error;
1042 let terr = TransportError::ConnectionFailed("timeout".into());
1043 let err = MeshError::Transport(terr);
1044 assert!(err.source().is_some());
1045 }
1046
1047 #[test]
1048 fn test_mesh_error_source_none_for_others() {
1049 use std::error::Error;
1050 assert!(MeshError::NotRunning.source().is_none());
1051 assert!(MeshError::AlreadyRunning.source().is_none());
1052 assert!(MeshError::InvalidConfig("x".into()).source().is_none());
1053 assert!(MeshError::Other("x".into()).source().is_none());
1054 }
1055
1056 #[test]
1057 fn test_mesh_error_from_transport_error() {
1058 let terr = TransportError::NotStarted;
1059 let err: MeshError = terr.into();
1060 assert!(matches!(err, MeshError::Transport(_)));
1061 }
1062
1063 #[test]
1064 fn test_mesh_error_debug() {
1065 let err = MeshError::NotRunning;
1066 let debug = format!("{:?}", err);
1067 assert!(debug.contains("NotRunning"));
1068 }
1069
1070 #[test]
1073 fn test_event_state_changed() {
1074 let evt = HiveMeshEvent::StateChanged(MeshState::Running);
1075 let debug = format!("{:?}", evt);
1076 assert!(debug.contains("Running"));
1077 }
1078
1079 #[test]
1080 fn test_event_peer_joined() {
1081 let evt = HiveMeshEvent::PeerJoined(NodeId::new("peer-1".into()));
1082 let cloned = evt.clone();
1083 let debug = format!("{:?}", cloned);
1084 assert!(debug.contains("peer-1"));
1085 }
1086
1087 #[test]
1088 fn test_event_peer_left() {
1089 let evt = HiveMeshEvent::PeerLeft(NodeId::new("peer-2".into()));
1090 let cloned = evt.clone();
1091 let debug = format!("{:?}", cloned);
1092 assert!(debug.contains("peer-2"));
1093 }
1094
1095 #[test]
1096 fn test_event_topology_changed() {
1097 let topo_evt = crate::topology::TopologyEvent::PeerLost {
1098 lost_peer_id: "gone".to_string(),
1099 };
1100 let evt = HiveMeshEvent::TopologyChanged(Box::new(topo_evt));
1101 let cloned = evt.clone();
1102 let debug = format!("{:?}", cloned);
1103 assert!(debug.contains("gone"));
1104 }
1105
1106 #[test]
1109 fn test_mesh_status_debug() {
1110 let status = MeshStatus {
1111 state: MeshState::Running,
1112 peer_count: 5,
1113 node_id: "n1".to_string(),
1114 uptime: Duration::from_secs(120),
1115 };
1116 let debug = format!("{:?}", status);
1117 assert!(debug.contains("Running"));
1118 assert!(debug.contains("n1"));
1119 }
1120
1121 #[test]
1122 fn test_mesh_status_clone() {
1123 let status = MeshStatus {
1124 state: MeshState::Stopped,
1125 peer_count: 0,
1126 node_id: "n2".to_string(),
1127 uptime: Duration::ZERO,
1128 };
1129 let cloned = status.clone();
1130 assert_eq!(cloned.state, MeshState::Stopped);
1131 assert_eq!(cloned.node_id, "n2");
1132 }
1133
1134 #[test]
1137 fn test_builder_minimal() {
1138 let mesh = HiveMeshBuilder::new(MeshConfig::default()).build();
1139 assert_eq!(mesh.state(), MeshState::Created);
1140 assert!(mesh.transport().is_none());
1141 assert!(mesh.hierarchy().is_none());
1142 assert!(mesh.router().is_none());
1143 }
1144
1145 #[test]
1146 fn test_builder_with_node_id() {
1147 let cfg = MeshConfig {
1148 node_id: Some("builder-node".to_string()),
1149 ..Default::default()
1150 };
1151 let mesh = HiveMeshBuilder::new(cfg).build();
1152 assert_eq!(mesh.node_id(), "builder-node");
1153 }
1154
1155 #[test]
1156 fn test_builder_with_transport() {
1157 let mesh = HiveMeshBuilder::new(MeshConfig::default())
1158 .with_transport(Arc::new(MockTransport::empty()))
1159 .build();
1160 assert!(mesh.transport().is_some());
1161 }
1162
1163 #[test]
1164 fn test_builder_with_hierarchy() {
1165 use crate::beacon::HierarchyLevel;
1166 use crate::hierarchy::{NodeRole, StaticHierarchyStrategy};
1167
1168 let strategy = StaticHierarchyStrategy {
1169 assigned_level: HierarchyLevel::Squad,
1170 assigned_role: NodeRole::Member,
1171 };
1172 let mesh = HiveMeshBuilder::new(MeshConfig::default())
1173 .with_hierarchy(Arc::new(strategy))
1174 .build();
1175 assert!(mesh.hierarchy().is_some());
1176 }
1177
1178 #[test]
1179 fn test_builder_with_router() {
1180 let router = MeshRouter::with_node_id("test");
1181 let mesh = HiveMeshBuilder::new(MeshConfig::default())
1182 .with_router(router)
1183 .build();
1184 assert!(mesh.router().is_some());
1185 }
1186
1187 #[test]
1188 fn test_builder_all_subsystems() {
1189 use crate::beacon::HierarchyLevel;
1190 use crate::hierarchy::{NodeRole, StaticHierarchyStrategy};
1191
1192 let strategy = StaticHierarchyStrategy {
1193 assigned_level: HierarchyLevel::Platoon,
1194 assigned_role: NodeRole::Leader,
1195 };
1196 let peers = vec![NodeId::new("p1".into())];
1197 let router = MeshRouter::with_node_id("full");
1198
1199 let mesh = HiveMeshBuilder::new(MeshConfig {
1200 node_id: Some("full-node".to_string()),
1201 ..Default::default()
1202 })
1203 .with_transport(Arc::new(MockTransport::new(peers)))
1204 .with_hierarchy(Arc::new(strategy))
1205 .with_router(router)
1206 .build();
1207
1208 assert_eq!(mesh.node_id(), "full-node");
1209 assert!(mesh.transport().is_some());
1210 assert!(mesh.hierarchy().is_some());
1211 assert!(mesh.router().is_some());
1212 assert_eq!(mesh.status().peer_count, 1);
1213 }
1214
1215 #[test]
1216 fn test_builder_lifecycle() {
1217 let mesh = HiveMeshBuilder::new(MeshConfig::default()).build();
1218 assert!(mesh.start().is_ok());
1219 assert_eq!(mesh.state(), MeshState::Running);
1220 assert!(mesh.stop().is_ok());
1221 assert_eq!(mesh.state(), MeshState::Stopped);
1222 }
1223
1224 #[test]
1227 fn test_transport_manager_initially_none() {
1228 let mesh = HiveMesh::new(MeshConfig::default());
1229 assert!(mesh.transport_manager().is_none());
1230 }
1231
1232 #[test]
1233 fn test_set_transport_manager() {
1234 use crate::transport::TransportManagerConfig;
1235 let mut mesh = HiveMesh::new(MeshConfig::default());
1236 let tm = TransportManager::new(TransportManagerConfig::default());
1237 mesh.set_transport_manager(tm);
1238 assert!(mesh.transport_manager().is_some());
1239 }
1240
1241 #[test]
1242 fn test_builder_with_transport_manager() {
1243 use crate::transport::TransportManagerConfig;
1244 let tm = TransportManager::new(TransportManagerConfig::default());
1245 let mesh = HiveMeshBuilder::new(MeshConfig::default())
1246 .with_transport_manager(tm)
1247 .build();
1248 assert!(mesh.transport_manager().is_some());
1249 }
1250
1251 #[test]
1252 fn test_builder_full_with_transport_manager() {
1253 use crate::beacon::HierarchyLevel;
1254 use crate::hierarchy::{NodeRole, StaticHierarchyStrategy};
1255 use crate::transport::TransportManagerConfig;
1256
1257 let strategy = StaticHierarchyStrategy {
1258 assigned_level: HierarchyLevel::Platoon,
1259 assigned_role: NodeRole::Leader,
1260 };
1261 let peers = vec![NodeId::new("p1".into())];
1262 let router = MeshRouter::with_node_id("full");
1263 let tm = TransportManager::new(TransportManagerConfig::default());
1264
1265 let mesh = HiveMeshBuilder::new(MeshConfig {
1266 node_id: Some("full-tm-node".to_string()),
1267 ..Default::default()
1268 })
1269 .with_transport(Arc::new(MockTransport::new(peers)))
1270 .with_transport_manager(tm)
1271 .with_hierarchy(Arc::new(strategy))
1272 .with_router(router)
1273 .build();
1274
1275 assert_eq!(mesh.node_id(), "full-tm-node");
1276 assert!(mesh.transport().is_some());
1277 assert!(mesh.transport_manager().is_some());
1278 assert!(mesh.hierarchy().is_some());
1279 assert!(mesh.router().is_some());
1280 }
1281
1282 #[test]
1285 fn test_bandwidth_initially_none() {
1286 let mesh = HiveMesh::new(MeshConfig::default());
1287 assert!(mesh.bandwidth().is_none());
1288 }
1289
1290 #[test]
1291 fn test_set_bandwidth() {
1292 let mut mesh = HiveMesh::new(MeshConfig::default());
1293 mesh.set_bandwidth(crate::qos::BandwidthAllocation::new(1_000_000));
1294 assert!(mesh.bandwidth().is_some());
1295 }
1296
1297 #[test]
1298 fn test_preemption_initially_none() {
1299 let mesh = HiveMesh::new(MeshConfig::default());
1300 assert!(mesh.preemption().is_none());
1301 }
1302
1303 #[test]
1304 fn test_set_preemption() {
1305 let mut mesh = HiveMesh::new(MeshConfig::default());
1306 mesh.set_preemption(crate::qos::PreemptionController::new());
1307 assert!(mesh.preemption().is_some());
1308 }
1309
1310 #[test]
1311 fn test_builder_with_bandwidth() {
1312 let mesh = HiveMeshBuilder::new(MeshConfig::default())
1313 .with_bandwidth(crate::qos::BandwidthAllocation::default_tactical())
1314 .build();
1315 assert!(mesh.bandwidth().is_some());
1316 }
1317
1318 #[test]
1319 fn test_builder_with_preemption() {
1320 let mesh = HiveMeshBuilder::new(MeshConfig::default())
1321 .with_preemption(crate::qos::PreemptionController::new())
1322 .build();
1323 assert!(mesh.preemption().is_some());
1324 }
1325
1326 #[test]
1329 fn test_device_keypair_initially_none() {
1330 let mesh = HiveMesh::new(MeshConfig::default());
1331 assert!(mesh.device_keypair().is_none());
1332 }
1333
1334 #[test]
1335 fn test_set_device_keypair() {
1336 let mut mesh = HiveMesh::new(MeshConfig::default());
1337 mesh.set_device_keypair(crate::security::DeviceKeypair::generate());
1338 assert!(mesh.device_keypair().is_some());
1339 }
1340
1341 #[test]
1342 fn test_formation_key_initially_none() {
1343 let mesh = HiveMesh::new(MeshConfig::default());
1344 assert!(mesh.formation_key().is_none());
1345 }
1346
1347 #[test]
1348 fn test_set_formation_key() {
1349 let mut mesh = HiveMesh::new(MeshConfig::default());
1350 mesh.set_formation_key(crate::security::FormationKey::new(
1351 "test-formation",
1352 &[0u8; 32],
1353 ));
1354 assert!(mesh.formation_key().is_some());
1355 }
1356
1357 #[test]
1358 fn test_builder_with_device_keypair() {
1359 let mesh = HiveMeshBuilder::new(MeshConfig::default())
1360 .with_device_keypair(crate::security::DeviceKeypair::generate())
1361 .build();
1362 assert!(mesh.device_keypair().is_some());
1363 }
1364
1365 #[test]
1366 fn test_builder_with_device_keypair_from_seed() {
1367 let mesh = HiveMeshBuilder::new(MeshConfig::default())
1368 .with_device_keypair_from_seed(b"k8s-secret", "pod-1")
1369 .unwrap()
1370 .build();
1371 assert!(mesh.device_keypair().is_some());
1372
1373 let mesh2 = HiveMeshBuilder::new(MeshConfig::default())
1375 .with_device_keypair_from_seed(b"k8s-secret", "pod-1")
1376 .unwrap()
1377 .build();
1378 assert_eq!(
1379 mesh.device_keypair().unwrap().device_id(),
1380 mesh2.device_keypair().unwrap().device_id()
1381 );
1382 }
1383
1384 #[test]
1385 fn test_builder_with_formation_key() {
1386 let mesh = HiveMeshBuilder::new(MeshConfig::default())
1387 .with_formation_key(crate::security::FormationKey::new("f1", &[1u8; 32]))
1388 .build();
1389 assert!(mesh.formation_key().is_some());
1390 }
1391
1392 #[test]
1395 fn test_discovery_initially_none() {
1396 let mesh = HiveMesh::new(MeshConfig::default());
1397 assert!(mesh.discovery().read().unwrap().is_none());
1398 }
1399
1400 #[test]
1401 fn test_set_discovery() {
1402 let mesh = HiveMesh::new(MeshConfig::default());
1403 let strategy = crate::discovery::HybridDiscovery::new();
1404 mesh.set_discovery(Box::new(strategy));
1405 assert!(mesh.discovery().read().unwrap().is_some());
1406 }
1407
1408 #[test]
1409 fn test_builder_with_discovery() {
1410 let strategy = crate::discovery::HybridDiscovery::new();
1411 let mesh = HiveMeshBuilder::new(MeshConfig::default())
1412 .with_discovery(Box::new(strategy))
1413 .build();
1414 assert!(mesh.discovery().read().unwrap().is_some());
1415 }
1416
1417 fn mock_storage() -> Arc<dyn crate::beacon::BeaconStorage> {
1420 Arc::new(crate::beacon::MockBeaconStorage::new())
1421 }
1422
1423 #[test]
1424 fn test_beacon_broadcaster_initially_none() {
1425 let mesh = HiveMesh::new(MeshConfig::default());
1426 assert!(mesh.beacon_broadcaster().is_none());
1427 }
1428
1429 #[test]
1430 fn test_set_beacon_broadcaster() {
1431 use crate::beacon::{BeaconBroadcaster, GeoPosition, HierarchyLevel};
1432
1433 let mut mesh = HiveMesh::new(MeshConfig::default());
1434 let bb = BeaconBroadcaster::new(
1435 mock_storage(),
1436 "test-node".to_string(),
1437 GeoPosition {
1438 lat: 0.0,
1439 lon: 0.0,
1440 alt: None,
1441 },
1442 HierarchyLevel::Squad,
1443 None,
1444 Duration::from_secs(5),
1445 );
1446 mesh.set_beacon_broadcaster(bb);
1447 assert!(mesh.beacon_broadcaster().is_some());
1448 }
1449
1450 #[test]
1451 fn test_beacon_observer_initially_none() {
1452 let mesh = HiveMesh::new(MeshConfig::default());
1453 assert!(mesh.beacon_observer().is_none());
1454 }
1455
1456 #[test]
1457 fn test_set_beacon_observer() {
1458 use crate::beacon::BeaconObserver;
1459
1460 let mut mesh = HiveMesh::new(MeshConfig::default());
1461 let bo = Arc::new(BeaconObserver::new(mock_storage(), "s00000".to_string()));
1462 mesh.set_beacon_observer(bo);
1463 assert!(mesh.beacon_observer().is_some());
1464 }
1465
1466 #[test]
1467 fn test_beacon_janitor_initially_none() {
1468 let mesh = HiveMesh::new(MeshConfig::default());
1469 assert!(mesh.beacon_janitor().is_none());
1470 }
1471
1472 #[test]
1473 fn test_set_beacon_janitor() {
1474 use crate::beacon::BeaconJanitor;
1475 use std::collections::HashMap;
1476
1477 let mut mesh = HiveMesh::new(MeshConfig::default());
1478 let nearby = Arc::new(tokio::sync::RwLock::new(HashMap::new()));
1479 let bj = BeaconJanitor::new(nearby, Duration::from_secs(60), Duration::from_secs(10));
1480 mesh.set_beacon_janitor(bj);
1481 assert!(mesh.beacon_janitor().is_some());
1482 }
1483
1484 #[test]
1485 fn test_builder_with_beacon_broadcaster() {
1486 use crate::beacon::{BeaconBroadcaster, GeoPosition, HierarchyLevel};
1487
1488 let bb = BeaconBroadcaster::new(
1489 mock_storage(),
1490 "builder-node".to_string(),
1491 GeoPosition {
1492 lat: 1.0,
1493 lon: 2.0,
1494 alt: None,
1495 },
1496 HierarchyLevel::Platoon,
1497 None,
1498 Duration::from_secs(5),
1499 );
1500 let mesh = HiveMeshBuilder::new(MeshConfig::default())
1501 .with_beacon_broadcaster(bb)
1502 .build();
1503 assert!(mesh.beacon_broadcaster().is_some());
1504 }
1505
1506 #[test]
1507 fn test_builder_with_beacon_observer() {
1508 use crate::beacon::BeaconObserver;
1509
1510 let bo = Arc::new(BeaconObserver::new(mock_storage(), "s00000".to_string()));
1511 let mesh = HiveMeshBuilder::new(MeshConfig::default())
1512 .with_beacon_observer(bo)
1513 .build();
1514 assert!(mesh.beacon_observer().is_some());
1515 }
1516
1517 #[test]
1518 fn test_builder_with_beacon_janitor() {
1519 use crate::beacon::BeaconJanitor;
1520 use std::collections::HashMap;
1521
1522 let nearby = Arc::new(tokio::sync::RwLock::new(HashMap::new()));
1523 let bj = BeaconJanitor::new(nearby, Duration::from_secs(60), Duration::from_secs(10));
1524 let mesh = HiveMeshBuilder::new(MeshConfig::default())
1525 .with_beacon_janitor(bj)
1526 .build();
1527 assert!(mesh.beacon_janitor().is_some());
1528 }
1529
1530 #[test]
1533 fn test_topology_manager_initially_none() {
1534 let mesh = HiveMesh::new(MeshConfig::default());
1535 assert!(mesh.topology_manager().is_none());
1536 }
1537
1538 #[test]
1539 fn test_set_topology_manager() {
1540 use crate::beacon::{BeaconObserver, GeoPosition, HierarchyLevel};
1541 use crate::topology::{TopologyBuilder, TopologyConfig, TopologyManager};
1542
1543 let mut mesh = HiveMesh::new(MeshConfig::default());
1544 let observer = Arc::new(BeaconObserver::new(mock_storage(), "s00000".to_string()));
1545 let builder = TopologyBuilder::new(
1546 TopologyConfig::default(),
1547 "topo-node".to_string(),
1548 GeoPosition {
1549 lat: 0.0,
1550 lon: 0.0,
1551 alt: None,
1552 },
1553 HierarchyLevel::Squad,
1554 None,
1555 observer,
1556 );
1557 let transport: Arc<dyn MeshTransport> = Arc::new(MockTransport::empty());
1558 let tm = TopologyManager::new(builder, transport);
1559 mesh.set_topology_manager(tm);
1560 assert!(mesh.topology_manager().is_some());
1561 }
1562
1563 #[test]
1564 fn test_builder_with_topology_manager() {
1565 use crate::beacon::{BeaconObserver, GeoPosition, HierarchyLevel};
1566 use crate::topology::{TopologyBuilder, TopologyConfig, TopologyManager};
1567
1568 let observer = Arc::new(BeaconObserver::new(mock_storage(), "s00000".to_string()));
1569 let builder = TopologyBuilder::new(
1570 TopologyConfig::default(),
1571 "topo-builder-node".to_string(),
1572 GeoPosition {
1573 lat: 0.0,
1574 lon: 0.0,
1575 alt: None,
1576 },
1577 HierarchyLevel::Squad,
1578 None,
1579 observer,
1580 );
1581 let transport: Arc<dyn MeshTransport> = Arc::new(MockTransport::empty());
1582 let tm = TopologyManager::new(builder, transport);
1583 let mesh = HiveMeshBuilder::new(MeshConfig::default())
1584 .with_topology_manager(tm)
1585 .build();
1586 assert!(mesh.topology_manager().is_some());
1587 }
1588}
1589
1590#[cfg(all(test, feature = "broker"))]
1593mod broker_tests {
1594 use super::*;
1595 use crate::broker::state::MeshBrokerState;
1596 use crate::config::MeshConfig;
1597
1598 #[test]
1599 fn test_broker_node_info() {
1600 let mesh = HiveMesh::new(MeshConfig {
1601 node_id: Some("broker-node".to_string()),
1602 ..Default::default()
1603 });
1604 let info = mesh.node_info();
1605 assert_eq!(info.node_id, "broker-node");
1606 assert_eq!(info.uptime_secs, 0);
1607 assert!(!info.version.is_empty());
1608 }
1609
1610 #[test]
1611 fn test_broker_node_info_with_uptime() {
1612 let mesh = HiveMesh::new(MeshConfig {
1613 node_id: Some("uptime-node".to_string()),
1614 ..Default::default()
1615 });
1616 mesh.start().unwrap();
1617 let info = mesh.node_info();
1618 assert_eq!(info.node_id, "uptime-node");
1619 }
1621
1622 #[tokio::test]
1623 async fn test_broker_list_peers_no_transport() {
1624 let mesh = HiveMesh::new(MeshConfig::default());
1625 let peers = mesh.list_peers().await;
1626 assert!(peers.is_empty());
1627 }
1628
1629 #[tokio::test]
1630 async fn test_broker_get_peer_no_transport() {
1631 let mesh = HiveMesh::new(MeshConfig::default());
1632 let peer = mesh.get_peer("unknown").await;
1633 assert!(peer.is_none());
1634 }
1635
1636 #[test]
1637 fn test_broker_topology() {
1638 let mesh = HiveMesh::new(MeshConfig::default());
1639 let topo = mesh.topology();
1640 assert_eq!(topo.peer_count, 0);
1641 assert_eq!(topo.role, "standalone");
1642 assert_eq!(topo.hierarchy_level, 0);
1643 }
1644
1645 #[test]
1646 fn test_broker_subscribe_events() {
1647 let mesh = HiveMesh::new(MeshConfig::default());
1648 let _rx = MeshBrokerState::subscribe_events(&mesh);
1649 }
1651
1652 #[test]
1653 fn test_broker_event_bridge() {
1654 use crate::broker::state::MeshEvent;
1655
1656 let mesh = HiveMesh::new(MeshConfig::default());
1657 let mut rx = MeshBrokerState::subscribe_events(&mesh);
1658
1659 mesh.emit_mesh_event(MeshEvent::PeerConnected {
1661 peer_id: "test-peer".into(),
1662 });
1663
1664 let event = rx.try_recv().unwrap();
1666 assert!(matches!(
1667 event,
1668 MeshEvent::PeerConnected { ref peer_id } if peer_id == "test-peer"
1669 ));
1670 }
1671
1672 #[test]
1673 fn test_broker_event_bridge_start_emits_topology() {
1674 use crate::broker::state::MeshEvent;
1675
1676 let mesh = HiveMesh::new(MeshConfig::default());
1677 let mut rx = MeshBrokerState::subscribe_events(&mesh);
1678
1679 mesh.start().unwrap();
1680
1681 let event = rx.try_recv().unwrap();
1682 assert!(matches!(
1683 event,
1684 MeshEvent::TopologyChanged { ref new_role, peer_count: 0 } if new_role == "standalone"
1685 ));
1686 }
1687
1688 #[test]
1689 fn test_broker_event_bridge_stop_emits_topology() {
1690 use crate::broker::state::MeshEvent;
1691
1692 let mesh = HiveMesh::new(MeshConfig::default());
1693 mesh.start().unwrap();
1694
1695 let mut rx = MeshBrokerState::subscribe_events(&mesh);
1696 mesh.stop().unwrap();
1697
1698 let event = rx.try_recv().unwrap();
1699 assert!(matches!(
1700 event,
1701 MeshEvent::TopologyChanged { ref new_role, peer_count: 0 } if new_role == "stopped"
1702 ));
1703 }
1704}