1use std::collections::HashMap;
21
22#[inline]
28pub fn xorshift64(state: &mut u64) -> u64 {
29 let mut x = *state;
30 x ^= x << 13;
31 x ^= x >> 7;
32 x ^= x << 17;
33 *state = x;
34 x
35}
36
37#[derive(Clone, Debug, PartialEq, Eq, Hash)]
43pub enum PdpDiscoveryMethod {
44 Bootstrap(String),
46 Mdns {
48 service_name: String,
50 },
51 Dht {
53 query_key: String,
55 },
56 PeerExchange {
58 source_peer: String,
60 },
61 Static(String),
63 Rendezvous {
65 namespace: String,
67 },
68}
69
70impl PdpDiscoveryMethod {
71 pub fn variant_name(&self) -> &'static str {
74 match self {
75 PdpDiscoveryMethod::Bootstrap(_) => "bootstrap",
76 PdpDiscoveryMethod::Mdns { .. } => "mdns",
77 PdpDiscoveryMethod::Dht { .. } => "dht",
78 PdpDiscoveryMethod::PeerExchange { .. } => "peer_exchange",
79 PdpDiscoveryMethod::Static(_) => "static",
80 PdpDiscoveryMethod::Rendezvous { .. } => "rendezvous",
81 }
82 }
83}
84
85#[derive(Clone, Debug)]
92pub struct PdpDiscoveredPeer {
93 pub id: String,
95 pub addresses: Vec<String>,
97 pub discovered_at: u64,
99 pub discovery_method: PdpDiscoveryMethod,
101 pub ttl_us: u64,
103 pub verified: bool,
105 pub capabilities: Vec<String>,
107 pub(crate) verified_at: u64,
110 pub(crate) expires_at: u64,
113}
114
115impl PdpDiscoveredPeer {
116 pub fn new(
118 id: impl Into<String>,
119 addresses: Vec<String>,
120 discovered_at: u64,
121 discovery_method: PdpDiscoveryMethod,
122 ttl_us: u64,
123 capabilities: Vec<String>,
124 ) -> Self {
125 let expires_at = discovered_at.saturating_add(ttl_us);
126 Self {
127 id: id.into(),
128 addresses,
129 discovered_at,
130 discovery_method,
131 ttl_us,
132 verified: false,
133 capabilities,
134 verified_at: 0,
135 expires_at,
136 }
137 }
138
139 #[inline]
141 pub fn is_expired(&self, current_ts: u64) -> bool {
142 self.expires_at < current_ts
144 }
145}
146
147#[derive(Clone, Debug)]
153pub struct PdpDiscoveryConfig {
154 pub max_peers: usize,
156 pub peer_ttl_us: u64,
159 pub enable_mdns: bool,
161 pub enable_dht: bool,
163 pub enable_peer_exchange: bool,
165 pub enable_rendezvous: bool,
167 pub bootstrap_peers: Vec<String>,
169 pub refresh_interval_us: u64,
172}
173
174impl Default for PdpDiscoveryConfig {
175 fn default() -> Self {
176 Self {
177 max_peers: 1024,
178 peer_ttl_us: 3_600_000_000, enable_mdns: true,
180 enable_dht: true,
181 enable_peer_exchange: true,
182 enable_rendezvous: true,
183 bootstrap_peers: Vec::new(),
184 refresh_interval_us: 60_000_000, }
186 }
187}
188
189#[derive(Clone, Debug)]
195pub enum PdpDiscoveryEvent {
196 PeerDiscovered(PdpDiscoveredPeer),
198 PeerExpired(String),
200 PeerVerified(String),
202 DiscoveryMethodFailed {
204 method: String,
206 reason: String,
208 },
209 PeerTableFull(usize),
211}
212
213#[derive(Clone, Debug, Default)]
219pub struct PdpDiscoveryStats {
220 pub total_discovered: u64,
222 pub active_peers: usize,
224 pub expired_peers: u64,
226 pub verifications_performed: u64,
228 pub by_method: Vec<(String, u64)>,
230}
231
232#[derive(Clone, Debug, PartialEq, Eq)]
238pub enum PdpDiscoveryError {
239 PeerNotFound(String),
241 MaxPeersExceeded,
243 InvalidAddress(String),
245 DuplicatePeer(String),
248 ConfigurationError(String),
250}
251
252impl std::fmt::Display for PdpDiscoveryError {
253 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
254 match self {
255 PdpDiscoveryError::PeerNotFound(id) => write!(f, "peer not found: {id}"),
256 PdpDiscoveryError::MaxPeersExceeded => write!(f, "peer table is full"),
257 PdpDiscoveryError::InvalidAddress(addr) => write!(f, "invalid address: {addr}"),
258 PdpDiscoveryError::DuplicatePeer(id) => write!(f, "duplicate peer: {id}"),
259 PdpDiscoveryError::ConfigurationError(msg) => {
260 write!(f, "configuration error: {msg}")
261 }
262 }
263 }
264}
265
266impl std::error::Error for PdpDiscoveryError {}
267
268#[derive(Debug, Default)]
274struct MethodCounts {
275 counts: HashMap<String, u64>,
276}
277
278impl MethodCounts {
279 fn increment(&mut self, name: &str) {
280 *self.counts.entry(name.to_string()).or_insert(0) += 1;
281 }
282
283 fn to_vec(&self) -> Vec<(String, u64)> {
284 let mut v: Vec<(String, u64)> = self.counts.iter().map(|(k, v)| (k.clone(), *v)).collect();
285 v.sort_by(|a, b| a.0.cmp(&b.0));
286 v
287 }
288}
289
290#[derive(Debug)]
300pub struct PeerDiscoveryProtocol {
301 peers: HashMap<String, PdpDiscoveredPeer>,
303 config: PdpDiscoveryConfig,
305 events: Vec<PdpDiscoveryEvent>,
307 total_discovered: u64,
309 expired_peers: u64,
311 verifications_performed: u64,
313 method_counts: MethodCounts,
315}
316
317impl PeerDiscoveryProtocol {
318 pub fn new(config: PdpDiscoveryConfig) -> Result<Self, PdpDiscoveryError> {
325 if config.max_peers == 0 {
326 return Err(PdpDiscoveryError::ConfigurationError(
327 "max_peers must be > 0".into(),
328 ));
329 }
330 if config.peer_ttl_us == 0 {
331 return Err(PdpDiscoveryError::ConfigurationError(
332 "peer_ttl_us must be > 0".into(),
333 ));
334 }
335 Ok(Self {
336 peers: HashMap::new(),
337 config,
338 events: Vec::new(),
339 total_discovered: 0,
340 expired_peers: 0,
341 verifications_performed: 0,
342 method_counts: MethodCounts::default(),
343 })
344 }
345
346 pub fn with_defaults() -> Self {
348 Self::new(PdpDiscoveryConfig::default()).expect("default config is always valid")
350 }
351
352 pub fn add_peer(
367 &mut self,
368 peer: PdpDiscoveredPeer,
369 ) -> Result<PdpDiscoveryEvent, PdpDiscoveryError> {
370 let method_name = peer.discovery_method.variant_name().to_string();
371
372 if let Some(existing) = self.peers.get_mut(&peer.id) {
373 for addr in &peer.addresses {
375 if !existing.addresses.contains(addr) {
376 existing.addresses.push(addr.clone());
377 }
378 }
379 existing.ttl_us = peer.ttl_us;
381 existing.expires_at = existing.discovered_at.saturating_add(peer.ttl_us);
382 for cap in &peer.capabilities {
384 if !existing.capabilities.contains(cap) {
385 existing.capabilities.push(cap.clone());
386 }
387 }
388 self.total_discovered += 1;
389 self.method_counts.increment(&method_name);
390
391 let event = PdpDiscoveryEvent::PeerDiscovered(existing.clone());
392 self.events.push(event.clone());
393 return Ok(event);
394 }
395
396 if self.peers.len() >= self.config.max_peers {
398 let active = self.peers.len();
399 let full_event = PdpDiscoveryEvent::PeerTableFull(active);
400 self.events.push(full_event);
401 return Err(PdpDiscoveryError::MaxPeersExceeded);
402 }
403
404 self.total_discovered += 1;
405 self.method_counts.increment(&method_name);
406
407 let event = PdpDiscoveryEvent::PeerDiscovered(peer.clone());
408 self.events.push(event.clone());
409 self.peers.insert(peer.id.clone(), peer);
410 Ok(event)
411 }
412
413 pub fn remove_peer(&mut self, id: &str) -> Result<(), PdpDiscoveryError> {
419 self.peers
420 .remove(id)
421 .map(|_| ())
422 .ok_or_else(|| PdpDiscoveryError::PeerNotFound(id.to_string()))
423 }
424
425 pub fn verify_peer(
433 &mut self,
434 id: &str,
435 current_ts: u64,
436 ) -> Result<PdpDiscoveryEvent, PdpDiscoveryError> {
437 let peer = self
438 .peers
439 .get_mut(id)
440 .ok_or_else(|| PdpDiscoveryError::PeerNotFound(id.to_string()))?;
441 peer.verified = true;
442 peer.verified_at = current_ts;
443 self.verifications_performed += 1;
444 let event = PdpDiscoveryEvent::PeerVerified(id.to_string());
445 self.events.push(event.clone());
446 Ok(event)
447 }
448
449 pub fn expire_peers(&mut self, current_ts: u64) -> Vec<PdpDiscoveryEvent> {
453 let expired_ids: Vec<String> = self
454 .peers
455 .iter()
456 .filter(|(_, p)| p.is_expired(current_ts))
457 .map(|(id, _)| id.clone())
458 .collect();
459
460 let mut events = Vec::with_capacity(expired_ids.len());
461 for id in &expired_ids {
462 self.peers.remove(id);
463 self.expired_peers += 1;
464 let ev = PdpDiscoveryEvent::PeerExpired(id.clone());
465 self.events.push(ev.clone());
466 events.push(ev);
467 }
468 events
469 }
470
471 pub fn refresh_peer(&mut self, id: &str, current_ts: u64) -> Result<(), PdpDiscoveryError> {
477 let peer = self
478 .peers
479 .get_mut(id)
480 .ok_or_else(|| PdpDiscoveryError::PeerNotFound(id.to_string()))?;
481 peer.discovered_at = current_ts;
482 peer.ttl_us = self.config.peer_ttl_us;
483 peer.expires_at = current_ts.saturating_add(self.config.peer_ttl_us);
484 Ok(())
485 }
486
487 pub fn get_peer(&self, id: &str) -> Result<&PdpDiscoveredPeer, PdpDiscoveryError> {
497 self.peers
498 .get(id)
499 .ok_or_else(|| PdpDiscoveryError::PeerNotFound(id.to_string()))
500 }
501
502 pub fn peers_by_method(&self, method: &str) -> Vec<&PdpDiscoveredPeer> {
507 self.peers
508 .values()
509 .filter(|p| p.discovery_method.variant_name() == method)
510 .collect()
511 }
512
513 pub fn peers_with_capability(&self, capability: &str) -> Vec<&PdpDiscoveredPeer> {
515 self.peers
516 .values()
517 .filter(|p| p.capabilities.iter().any(|c| c == capability))
518 .collect()
519 }
520
521 pub fn random_peers(&self, n: usize, seed: u64) -> Vec<&PdpDiscoveredPeer> {
527 let mut indices: Vec<usize> = (0..self.peers.len()).collect();
528 let peers_vec: Vec<&PdpDiscoveredPeer> = self.peers.values().collect();
529 let mut state = if seed == 0 {
530 0xDEAD_BEEF_CAFE_1337
531 } else {
532 seed
533 };
534
535 let take = n.min(indices.len());
537 for i in 0..take {
538 let rand_val = xorshift64(&mut state);
539 let j = i + (rand_val as usize % (indices.len() - i));
540 indices.swap(i, j);
541 }
542
543 indices
544 .into_iter()
545 .take(take)
546 .map(|i| peers_vec[i])
547 .collect()
548 }
549
550 pub fn merge_peer_table(
564 &mut self,
565 other: Vec<PdpDiscoveredPeer>,
566 current_ts: u64,
567 ) -> Vec<PdpDiscoveryEvent> {
568 let mut events = Vec::new();
569 for peer in other {
570 if peer.is_expired(current_ts) {
572 continue;
573 }
574 match self.add_peer(peer) {
575 Ok(ev) => events.push(ev),
576 Err(PdpDiscoveryError::MaxPeersExceeded) => {
577 if let Some(ev) = self.events.last().cloned() {
580 events.push(ev);
581 }
582 break;
584 }
585 Err(_) => {
586 }
588 }
589 }
590 events
591 }
592
593 pub fn stats(&self) -> PdpDiscoveryStats {
599 PdpDiscoveryStats {
600 total_discovered: self.total_discovered,
601 active_peers: self.peers.len(),
602 expired_peers: self.expired_peers,
603 verifications_performed: self.verifications_performed,
604 by_method: self.method_counts.to_vec(),
605 }
606 }
607
608 pub fn drain_events(&mut self) -> Vec<PdpDiscoveryEvent> {
612 std::mem::take(&mut self.events)
613 }
614
615 #[inline]
617 pub fn peer_count(&self) -> usize {
618 self.peers.len()
619 }
620
621 pub fn all_peers(&self) -> impl Iterator<Item = &PdpDiscoveredPeer> {
623 self.peers.values()
624 }
625
626 pub fn record_method_failure(&mut self, method: impl Into<String>, reason: impl Into<String>) {
629 let ev = PdpDiscoveryEvent::DiscoveryMethodFailed {
630 method: method.into(),
631 reason: reason.into(),
632 };
633 self.events.push(ev);
634 }
635}
636
637pub type DiscoveredPeer = PdpDiscoveredPeer;
643
644pub type DiscoveryConfig = PdpDiscoveryConfig;
646
647pub type DiscoveryStats = PdpDiscoveryStats;
649
650pub type DiscoveryEvent = PdpDiscoveryEvent;
652
653pub type DiscoveryError = PdpDiscoveryError;
655
656#[cfg(test)]
661mod tests {
662 use super::*;
663
664 fn make_ts(offset_us: u64) -> u64 {
669 1_000_000_000_000 + offset_us
670 }
671
672 fn default_config() -> PdpDiscoveryConfig {
673 PdpDiscoveryConfig {
674 max_peers: 16,
675 peer_ttl_us: 3_600_000_000,
676 ..PdpDiscoveryConfig::default()
677 }
678 }
679
680 fn make_peer(id: &str, method: PdpDiscoveryMethod) -> PdpDiscoveredPeer {
681 PdpDiscoveredPeer::new(
682 id,
683 vec![format!("/ip4/127.0.0.1/tcp/{}", id.len() * 1000)],
684 make_ts(0),
685 method,
686 3_600_000_000,
687 vec!["bitswap/1.2.0".into()],
688 )
689 }
690
691 fn make_peer_with_caps(id: &str, caps: Vec<&str>) -> PdpDiscoveredPeer {
692 PdpDiscoveredPeer::new(
693 id,
694 vec!["/ip4/10.0.0.1/tcp/4001".into()],
695 make_ts(0),
696 PdpDiscoveryMethod::Bootstrap("http://boot.example.com".into()),
697 3_600_000_000,
698 caps.into_iter().map(String::from).collect(),
699 )
700 }
701
702 fn make_protocol() -> PeerDiscoveryProtocol {
703 PeerDiscoveryProtocol::new(default_config()).expect("valid config")
704 }
705
706 #[test]
711 fn test_new_with_defaults() {
712 let p = PeerDiscoveryProtocol::with_defaults();
713 assert_eq!(p.peer_count(), 0);
714 }
715
716 #[test]
717 fn test_new_with_valid_config() {
718 let cfg = PdpDiscoveryConfig {
719 max_peers: 100,
720 peer_ttl_us: 60_000_000,
721 ..PdpDiscoveryConfig::default()
722 };
723 let p = PeerDiscoveryProtocol::new(cfg);
724 assert!(p.is_ok());
725 }
726
727 #[test]
728 fn test_new_zero_max_peers_fails() {
729 let cfg = PdpDiscoveryConfig {
730 max_peers: 0,
731 ..PdpDiscoveryConfig::default()
732 };
733 let err =
734 PeerDiscoveryProtocol::new(cfg).expect_err("test: zero max_peers config should fail");
735 assert!(matches!(err, PdpDiscoveryError::ConfigurationError(_)));
736 }
737
738 #[test]
739 fn test_new_zero_ttl_fails() {
740 let cfg = PdpDiscoveryConfig {
741 peer_ttl_us: 0,
742 ..PdpDiscoveryConfig::default()
743 };
744 let err = PeerDiscoveryProtocol::new(cfg).expect_err("test: zero ttl config should fail");
745 assert!(matches!(err, PdpDiscoveryError::ConfigurationError(_)));
746 }
747
748 #[test]
753 fn test_add_peer_basic() {
754 let mut p = make_protocol();
755 let peer = make_peer("peer-1", PdpDiscoveryMethod::Bootstrap("url".into()));
756 let result = p.add_peer(peer);
757 assert!(result.is_ok());
758 assert_eq!(p.peer_count(), 1);
759 }
760
761 #[test]
762 fn test_add_peer_emits_discovered_event() {
763 let mut p = make_protocol();
764 let peer = make_peer("peer-1", PdpDiscoveryMethod::Bootstrap("url".into()));
765 let ev = p
766 .add_peer(peer)
767 .expect("test: add_peer should succeed for new peer");
768 assert!(matches!(ev, PdpDiscoveryEvent::PeerDiscovered(_)));
769 }
770
771 #[test]
772 fn test_add_peer_duplicate_merges_addresses() {
773 let mut p = make_protocol();
774 let peer1 = PdpDiscoveredPeer::new(
775 "dup-peer",
776 vec!["/ip4/1.2.3.4/tcp/4001".into()],
777 make_ts(0),
778 PdpDiscoveryMethod::Bootstrap("url".into()),
779 3_600_000_000,
780 vec![],
781 );
782 let peer2 = PdpDiscoveredPeer::new(
783 "dup-peer",
784 vec!["/ip4/5.6.7.8/tcp/4001".into()],
785 make_ts(10),
786 PdpDiscoveryMethod::Bootstrap("url".into()),
787 3_600_000_000,
788 vec![],
789 );
790 p.add_peer(peer1)
791 .expect("test: add first peer should succeed");
792 p.add_peer(peer2)
793 .expect("test: add duplicate peer should succeed (merge)");
794 let stored = p
795 .get_peer("dup-peer")
796 .expect("test: get_peer should find dup-peer");
797 assert_eq!(stored.addresses.len(), 2);
798 assert_eq!(p.peer_count(), 1);
799 }
800
801 #[test]
802 fn test_add_peer_duplicate_deduplicates_addresses() {
803 let mut p = make_protocol();
804 let addr = "/ip4/1.2.3.4/tcp/4001";
805 let peer1 = PdpDiscoveredPeer::new(
806 "dup2",
807 vec![addr.into()],
808 make_ts(0),
809 PdpDiscoveryMethod::Mdns {
810 service_name: "svc".into(),
811 },
812 3_600_000_000,
813 vec![],
814 );
815 let peer2 = PdpDiscoveredPeer::new(
816 "dup2",
817 vec![addr.into()], make_ts(1),
819 PdpDiscoveryMethod::Mdns {
820 service_name: "svc".into(),
821 },
822 3_600_000_000,
823 vec![],
824 );
825 p.add_peer(peer1)
826 .expect("test: add first dup2 peer should succeed");
827 p.add_peer(peer2)
828 .expect("test: add duplicate dup2 peer should succeed (merge)");
829 let stored = p.get_peer("dup2").expect("test: get_peer should find dup2");
830 assert_eq!(stored.addresses.len(), 1);
831 }
832
833 #[test]
834 fn test_add_peer_table_full_error() {
835 let cfg = PdpDiscoveryConfig {
836 max_peers: 2,
837 peer_ttl_us: 3_600_000_000,
838 ..PdpDiscoveryConfig::default()
839 };
840 let mut p = PeerDiscoveryProtocol::new(cfg)
841 .expect("test: valid config with max_peers=2 should succeed");
842 p.add_peer(make_peer("a", PdpDiscoveryMethod::Static("a".into())))
843 .expect("test: add peer a should succeed");
844 p.add_peer(make_peer("b", PdpDiscoveryMethod::Static("b".into())))
845 .expect("test: add peer b should succeed");
846 let err = p
847 .add_peer(make_peer("c", PdpDiscoveryMethod::Static("c".into())))
848 .expect_err("test: add peer c beyond max_peers should fail");
849 assert!(matches!(err, PdpDiscoveryError::MaxPeersExceeded));
850 }
851
852 #[test]
853 fn test_add_peer_table_full_emits_event() {
854 let cfg = PdpDiscoveryConfig {
855 max_peers: 1,
856 peer_ttl_us: 3_600_000_000,
857 ..PdpDiscoveryConfig::default()
858 };
859 let mut p = PeerDiscoveryProtocol::new(cfg)
860 .expect("test: valid config with max_peers=1 should succeed");
861 p.add_peer(make_peer("a", PdpDiscoveryMethod::Static("a".into())))
862 .expect("test: add peer a should succeed");
863 let _ = p.add_peer(make_peer("b", PdpDiscoveryMethod::Static("b".into())));
864 let events = p.drain_events();
865 assert!(events
867 .iter()
868 .any(|e| matches!(e, PdpDiscoveryEvent::PeerTableFull(_))));
869 }
870
871 #[test]
872 fn test_add_peer_multiple_methods() {
873 let mut p = make_protocol();
874 p.add_peer(make_peer("p1", PdpDiscoveryMethod::Bootstrap("b".into())))
875 .expect("test: add p1 bootstrap peer should succeed");
876 p.add_peer(make_peer(
877 "p2",
878 PdpDiscoveryMethod::Mdns {
879 service_name: "s".into(),
880 },
881 ))
882 .expect("test: add p2 mdns peer should succeed");
883 p.add_peer(make_peer(
884 "p3",
885 PdpDiscoveryMethod::Dht {
886 query_key: "k".into(),
887 },
888 ))
889 .expect("test: add p3 dht peer should succeed");
890 p.add_peer(make_peer(
891 "p4",
892 PdpDiscoveryMethod::PeerExchange {
893 source_peer: "src".into(),
894 },
895 ))
896 .expect("test: add p4 peer_exchange peer should succeed");
897 p.add_peer(make_peer("p5", PdpDiscoveryMethod::Static("addr".into())))
898 .expect("test: add p5 static peer should succeed");
899 p.add_peer(make_peer(
900 "p6",
901 PdpDiscoveryMethod::Rendezvous {
902 namespace: "ns".into(),
903 },
904 ))
905 .expect("test: add p6 rendezvous peer should succeed");
906 assert_eq!(p.peer_count(), 6);
907 }
908
909 #[test]
914 fn test_remove_peer_success() {
915 let mut p = make_protocol();
916 p.add_peer(make_peer("r1", PdpDiscoveryMethod::Static("x".into())))
917 .expect("test: add r1 peer should succeed");
918 assert!(p.remove_peer("r1").is_ok());
919 assert_eq!(p.peer_count(), 0);
920 }
921
922 #[test]
923 fn test_remove_peer_not_found() {
924 let mut p = make_protocol();
925 let err = p
926 .remove_peer("ghost")
927 .expect_err("test: remove nonexistent peer should fail");
928 assert!(matches!(err, PdpDiscoveryError::PeerNotFound(_)));
929 }
930
931 #[test]
932 fn test_remove_then_add_same_id() {
933 let mut p = make_protocol();
934 p.add_peer(make_peer("x", PdpDiscoveryMethod::Static("a".into())))
935 .expect("test: add peer x should succeed");
936 p.remove_peer("x")
937 .expect("test: remove peer x should succeed");
938 p.add_peer(make_peer("x", PdpDiscoveryMethod::Static("a".into())))
940 .expect("test: re-add peer x after removal should succeed");
941 assert_eq!(p.peer_count(), 1);
942 }
943
944 #[test]
949 fn test_verify_peer_success() {
950 let mut p = make_protocol();
951 p.add_peer(make_peer("v1", PdpDiscoveryMethod::Bootstrap("u".into())))
952 .expect("test: add v1 peer should succeed");
953 let ev = p
954 .verify_peer("v1", make_ts(1000))
955 .expect("test: verify_peer v1 should succeed");
956 assert!(matches!(ev, PdpDiscoveryEvent::PeerVerified(_)));
957 assert!(
958 p.get_peer("v1")
959 .expect("test: get_peer v1 should succeed after verify")
960 .verified
961 );
962 }
963
964 #[test]
965 fn test_verify_peer_not_found() {
966 let mut p = make_protocol();
967 let err = p
968 .verify_peer("ghost", make_ts(0))
969 .expect_err("test: verify nonexistent peer should fail");
970 assert!(matches!(err, PdpDiscoveryError::PeerNotFound(_)));
971 }
972
973 #[test]
974 fn test_verify_peer_updates_verified_at() {
975 let mut p = make_protocol();
976 p.add_peer(make_peer("v2", PdpDiscoveryMethod::Bootstrap("u".into())))
977 .expect("test: add v2 peer should succeed");
978 let ts = make_ts(500_000);
979 p.verify_peer("v2", ts)
980 .expect("test: verify_peer v2 should succeed");
981 assert_eq!(
982 p.get_peer("v2")
983 .expect("test: get_peer v2 should succeed after verify")
984 .verified_at,
985 ts
986 );
987 }
988
989 #[test]
990 fn test_verify_peer_increments_stats() {
991 let mut p = make_protocol();
992 p.add_peer(make_peer("v3", PdpDiscoveryMethod::Bootstrap("u".into())))
993 .expect("test: add v3 peer should succeed");
994 p.verify_peer("v3", make_ts(0))
995 .expect("test: first verify_peer v3 should succeed");
996 p.verify_peer("v3", make_ts(1))
997 .expect("test: second verify_peer v3 should succeed");
998 assert_eq!(p.stats().verifications_performed, 2);
999 }
1000
1001 #[test]
1006 fn test_expire_peers_removes_stale() {
1007 let mut p = make_protocol();
1008 let ttl = 1_000_000; let added_at = make_ts(0);
1010 let peer = PdpDiscoveredPeer::new(
1011 "exp-1",
1012 vec![],
1013 added_at,
1014 PdpDiscoveryMethod::Bootstrap("b".into()),
1015 ttl,
1016 vec![],
1017 );
1018 p.add_peer(peer)
1019 .expect("test: add exp-1 peer should succeed");
1020 let evs = p.expire_peers(added_at + ttl + 1);
1022 assert_eq!(evs.len(), 1);
1023 assert!(matches!(&evs[0], PdpDiscoveryEvent::PeerExpired(id) if id == "exp-1"));
1024 assert_eq!(p.peer_count(), 0);
1025 }
1026
1027 #[test]
1028 fn test_expire_peers_keeps_live() {
1029 let mut p = make_protocol();
1030 let ttl = 1_000_000;
1031 let added_at = make_ts(0);
1032 let peer = PdpDiscoveredPeer::new(
1033 "live-1",
1034 vec![],
1035 added_at,
1036 PdpDiscoveryMethod::Static("x".into()),
1037 ttl,
1038 vec![],
1039 );
1040 p.add_peer(peer)
1041 .expect("test: add live-1 peer should succeed");
1042 let evs = p.expire_peers(added_at + ttl - 1);
1044 assert_eq!(evs.len(), 0);
1045 assert_eq!(p.peer_count(), 1);
1046 }
1047
1048 #[test]
1049 fn test_expire_peers_increments_expired_count() {
1050 let mut p = make_protocol();
1051 let ttl = 500;
1052 let t0 = make_ts(0);
1053 for i in 0..3u64 {
1054 let peer = PdpDiscoveredPeer::new(
1055 format!("ep-{i}"),
1056 vec![],
1057 t0,
1058 PdpDiscoveryMethod::Dht {
1059 query_key: "k".into(),
1060 },
1061 ttl,
1062 vec![],
1063 );
1064 p.add_peer(peer)
1065 .expect("test: add ep-{i} peer should succeed");
1066 }
1067 p.expire_peers(t0 + ttl + 1);
1068 assert_eq!(p.stats().expired_peers, 3);
1069 }
1070
1071 #[test]
1072 fn test_expire_peers_emits_events_in_buffer() {
1073 let mut p = make_protocol();
1074 let ttl = 100;
1075 let t0 = make_ts(0);
1076 let peer = PdpDiscoveredPeer::new(
1077 "buf-exp",
1078 vec![],
1079 t0,
1080 PdpDiscoveryMethod::Static("x".into()),
1081 ttl,
1082 vec![],
1083 );
1084 p.add_peer(peer)
1085 .expect("test: add buf-exp peer should succeed");
1086 p.expire_peers(t0 + ttl + 1);
1087 let events = p.drain_events();
1088 assert!(events
1089 .iter()
1090 .any(|e| matches!(e, PdpDiscoveryEvent::PeerExpired(_))));
1091 }
1092
1093 #[test]
1094 fn test_expire_only_expired_not_live() {
1095 let mut p = make_protocol();
1096 let t0 = make_ts(0);
1097 let short_ttl = 1_000;
1098 let long_ttl = 3_600_000_000;
1099 let short_peer = PdpDiscoveredPeer::new(
1100 "short",
1101 vec![],
1102 t0,
1103 PdpDiscoveryMethod::Static("x".into()),
1104 short_ttl,
1105 vec![],
1106 );
1107 let long_peer = PdpDiscoveredPeer::new(
1108 "long",
1109 vec![],
1110 t0,
1111 PdpDiscoveryMethod::Static("y".into()),
1112 long_ttl,
1113 vec![],
1114 );
1115 p.add_peer(short_peer)
1116 .expect("test: add short-ttl peer should succeed");
1117 p.add_peer(long_peer)
1118 .expect("test: add long-ttl peer should succeed");
1119 let evs = p.expire_peers(t0 + short_ttl + 1);
1120 assert_eq!(evs.len(), 1);
1121 assert!(matches!(&evs[0], PdpDiscoveryEvent::PeerExpired(id) if id == "short"));
1122 assert_eq!(p.peer_count(), 1);
1123 assert!(p.get_peer("long").is_ok());
1124 }
1125
1126 #[test]
1131 fn test_refresh_peer_extends_ttl() {
1132 let mut p = make_protocol();
1133 let short_ttl = 100;
1134 let t0 = make_ts(0);
1135 let peer = PdpDiscoveredPeer::new(
1136 "ref-1",
1137 vec![],
1138 t0,
1139 PdpDiscoveryMethod::Bootstrap("b".into()),
1140 short_ttl,
1141 vec![],
1142 );
1143 p.add_peer(peer)
1144 .expect("test: add ref-1 peer should succeed");
1145 let t1 = t0 + 50;
1147 p.refresh_peer("ref-1", t1)
1148 .expect("test: refresh_peer ref-1 should succeed");
1149 let evs = p.expire_peers(t0 + short_ttl + 1);
1151 assert_eq!(evs.len(), 0);
1152 }
1153
1154 #[test]
1155 fn test_refresh_peer_not_found() {
1156 let mut p = make_protocol();
1157 let err = p
1158 .refresh_peer("ghost", make_ts(0))
1159 .expect_err("test: refresh nonexistent peer should fail");
1160 assert!(matches!(err, PdpDiscoveryError::PeerNotFound(_)));
1161 }
1162
1163 #[test]
1164 fn test_refresh_then_expire() {
1165 let mut p = make_protocol();
1166 let t0 = make_ts(0);
1167 let peer = PdpDiscoveredPeer::new(
1168 "re-exp",
1169 vec![],
1170 t0,
1171 PdpDiscoveryMethod::Static("x".into()),
1172 3_600_000_000,
1173 vec![],
1174 );
1175 p.add_peer(peer)
1176 .expect("test: add re-exp peer should succeed");
1177 p.refresh_peer("re-exp", t0)
1179 .expect("test: refresh_peer re-exp should succeed");
1180 let evs = p.expire_peers(t0 + 3_600_000_001);
1182 assert_eq!(evs.len(), 1);
1183 }
1184
1185 #[test]
1190 fn test_get_peer_success() {
1191 let mut p = make_protocol();
1192 p.add_peer(make_peer("g1", PdpDiscoveryMethod::Static("a".into())))
1193 .expect("test: add g1 peer should succeed");
1194 assert!(p.get_peer("g1").is_ok());
1195 }
1196
1197 #[test]
1198 fn test_get_peer_not_found() {
1199 let p = make_protocol();
1200 assert!(matches!(
1201 p.get_peer("none"),
1202 Err(PdpDiscoveryError::PeerNotFound(_))
1203 ));
1204 }
1205
1206 #[test]
1211 fn test_peers_by_method_bootstrap() {
1212 let mut p = make_protocol();
1213 p.add_peer(make_peer("b1", PdpDiscoveryMethod::Bootstrap("url".into())))
1214 .expect("test: add b1 bootstrap peer should succeed");
1215 p.add_peer(make_peer(
1216 "m1",
1217 PdpDiscoveryMethod::Mdns {
1218 service_name: "s".into(),
1219 },
1220 ))
1221 .expect("test: add m1 mdns peer should succeed");
1222 let boots = p.peers_by_method("bootstrap");
1223 assert_eq!(boots.len(), 1);
1224 assert_eq!(boots[0].id, "b1");
1225 }
1226
1227 #[test]
1228 fn test_peers_by_method_mdns() {
1229 let mut p = make_protocol();
1230 p.add_peer(make_peer(
1231 "m2",
1232 PdpDiscoveryMethod::Mdns {
1233 service_name: "svc".into(),
1234 },
1235 ))
1236 .expect("test: add m2 mdns peer should succeed");
1237 p.add_peer(make_peer(
1238 "m3",
1239 PdpDiscoveryMethod::Mdns {
1240 service_name: "svc".into(),
1241 },
1242 ))
1243 .expect("test: add m3 mdns peer should succeed");
1244 assert_eq!(p.peers_by_method("mdns").len(), 2);
1245 }
1246
1247 #[test]
1248 fn test_peers_by_method_dht() {
1249 let mut p = make_protocol();
1250 p.add_peer(make_peer(
1251 "d1",
1252 PdpDiscoveryMethod::Dht {
1253 query_key: "k".into(),
1254 },
1255 ))
1256 .expect("test: add d1 dht peer should succeed");
1257 assert_eq!(p.peers_by_method("dht").len(), 1);
1258 }
1259
1260 #[test]
1261 fn test_peers_by_method_peer_exchange() {
1262 let mut p = make_protocol();
1263 p.add_peer(make_peer(
1264 "pe1",
1265 PdpDiscoveryMethod::PeerExchange {
1266 source_peer: "src".into(),
1267 },
1268 ))
1269 .expect("test: add pe1 peer_exchange peer should succeed");
1270 assert_eq!(p.peers_by_method("peer_exchange").len(), 1);
1271 }
1272
1273 #[test]
1274 fn test_peers_by_method_rendezvous() {
1275 let mut p = make_protocol();
1276 p.add_peer(make_peer(
1277 "rv1",
1278 PdpDiscoveryMethod::Rendezvous {
1279 namespace: "ns".into(),
1280 },
1281 ))
1282 .expect("test: add rv1 rendezvous peer should succeed");
1283 assert_eq!(p.peers_by_method("rendezvous").len(), 1);
1284 }
1285
1286 #[test]
1287 fn test_peers_by_method_no_match() {
1288 let mut p = make_protocol();
1289 p.add_peer(make_peer("x", PdpDiscoveryMethod::Static("a".into())))
1290 .expect("test: add x peer for method filter test should succeed");
1291 assert_eq!(p.peers_by_method("dht").len(), 0);
1292 }
1293
1294 #[test]
1299 fn test_peers_with_capability_found() {
1300 let mut p = make_protocol();
1301 p.add_peer(make_peer_with_caps("c1", vec!["relay/v2", "bitswap"]))
1302 .expect("test: add c1 peer with caps should succeed");
1303 p.add_peer(make_peer_with_caps("c2", vec!["bitswap"]))
1304 .expect("test: add c2 peer with caps should succeed");
1305 let relay_peers = p.peers_with_capability("relay/v2");
1306 assert_eq!(relay_peers.len(), 1);
1307 assert_eq!(relay_peers[0].id, "c1");
1308 }
1309
1310 #[test]
1311 fn test_peers_with_capability_not_found() {
1312 let mut p = make_protocol();
1313 p.add_peer(make_peer_with_caps("c3", vec!["bitswap"]))
1314 .expect("test: add c3 peer with caps should succeed");
1315 assert_eq!(p.peers_with_capability("relay/v2").len(), 0);
1316 }
1317
1318 #[test]
1319 fn test_peers_with_capability_multiple() {
1320 let mut p = make_protocol();
1321 for i in 0..5 {
1322 p.add_peer(make_peer_with_caps(
1323 &format!("peer-cap-{i}"),
1324 vec!["dht/1.0"],
1325 ))
1326 .expect("test: add peer with dht/1.0 capability should succeed");
1327 }
1328 assert_eq!(p.peers_with_capability("dht/1.0").len(), 5);
1329 }
1330
1331 #[test]
1332 fn test_peers_with_capability_empty_table() {
1333 let p = make_protocol();
1334 assert_eq!(p.peers_with_capability("anything").len(), 0);
1335 }
1336
1337 #[test]
1342 fn test_random_peers_count() {
1343 let mut p = make_protocol();
1344 for i in 0..10u32 {
1345 p.add_peer(make_peer(
1346 &format!("rp-{i}"),
1347 PdpDiscoveryMethod::Static("a".into()),
1348 ))
1349 .expect("test: add random peer should succeed");
1350 }
1351 let sample = p.random_peers(5, 0xDEAD);
1352 assert_eq!(sample.len(), 5);
1353 }
1354
1355 #[test]
1356 fn test_random_peers_fewer_than_requested() {
1357 let mut p = make_protocol();
1358 for i in 0..3u32 {
1359 p.add_peer(make_peer(
1360 &format!("rp2-{i}"),
1361 PdpDiscoveryMethod::Static("a".into()),
1362 ))
1363 .expect("test: add rp2 peer should succeed");
1364 }
1365 let sample = p.random_peers(10, 1234);
1366 assert_eq!(sample.len(), 3);
1367 }
1368
1369 #[test]
1370 fn test_random_peers_zero_n() {
1371 let mut p = make_protocol();
1372 p.add_peer(make_peer("z", PdpDiscoveryMethod::Static("a".into())))
1373 .expect("test: add z peer for random_peers test should succeed");
1374 let sample = p.random_peers(0, 42);
1375 assert_eq!(sample.len(), 0);
1376 }
1377
1378 #[test]
1379 fn test_random_peers_no_duplicates() {
1380 let mut p = make_protocol();
1381 for i in 0..8u32 {
1382 p.add_peer(make_peer(
1383 &format!("rp3-{i}"),
1384 PdpDiscoveryMethod::Static("a".into()),
1385 ))
1386 .expect("test: add rp3 peer should succeed");
1387 }
1388 let sample = p.random_peers(8, 0xCAFE);
1389 let mut ids: Vec<&str> = sample.iter().map(|pr| pr.id.as_str()).collect();
1390 ids.sort_unstable();
1391 ids.dedup();
1392 assert_eq!(ids.len(), 8);
1393 }
1394
1395 #[test]
1396 fn test_random_peers_different_seeds_different_order() {
1397 let mut p = make_protocol();
1398 for i in 0..10u32 {
1399 p.add_peer(make_peer(
1400 &format!("seed-{i}"),
1401 PdpDiscoveryMethod::Static("a".into()),
1402 ))
1403 .expect("test: add seed peer should succeed");
1404 }
1405 let s1: Vec<String> = p.random_peers(10, 1).iter().map(|p| p.id.clone()).collect();
1406 let s2: Vec<String> = p
1407 .random_peers(10, 99999)
1408 .iter()
1409 .map(|p| p.id.clone())
1410 .collect();
1411 assert_ne!(s1, s2);
1414 }
1415
1416 #[test]
1421 fn test_merge_peer_table_basic() {
1422 let mut p = make_protocol();
1423 let t0 = make_ts(0);
1424 let peers = vec![
1425 make_peer("mp1", PdpDiscoveryMethod::Static("a".into())),
1426 make_peer("mp2", PdpDiscoveryMethod::Static("b".into())),
1427 ];
1428 let events = p.merge_peer_table(peers, t0);
1429 assert_eq!(events.len(), 2);
1430 assert_eq!(p.peer_count(), 2);
1431 }
1432
1433 #[test]
1434 fn test_merge_peer_table_skips_expired() {
1435 let mut p = make_protocol();
1436 let t0 = make_ts(0);
1437 let ttl = 100;
1438 let expired = PdpDiscoveredPeer::new(
1439 "old-peer",
1440 vec![],
1441 t0,
1442 PdpDiscoveryMethod::Static("x".into()),
1443 ttl,
1444 vec![],
1445 );
1446 let events = p.merge_peer_table(vec![expired], t0 + ttl + 1);
1448 assert_eq!(events.len(), 0);
1449 assert_eq!(p.peer_count(), 0);
1450 }
1451
1452 #[test]
1453 fn test_merge_peer_table_deduplicates() {
1454 let mut p = make_protocol();
1455 let t0 = make_ts(0);
1456 p.add_peer(make_peer("dup", PdpDiscoveryMethod::Static("a".into())))
1457 .expect("test: add dup peer before merge should succeed");
1458 let import = vec![make_peer("dup", PdpDiscoveryMethod::Static("a".into()))];
1459 p.merge_peer_table(import, t0);
1460 assert_eq!(p.peer_count(), 1);
1461 }
1462
1463 #[test]
1464 fn test_merge_peer_table_stops_at_capacity() {
1465 let cfg = PdpDiscoveryConfig {
1466 max_peers: 2,
1467 peer_ttl_us: 3_600_000_000,
1468 ..PdpDiscoveryConfig::default()
1469 };
1470 let mut p = PeerDiscoveryProtocol::new(cfg)
1471 .expect("test: valid config with max_peers=2 should succeed");
1472 let t0 = make_ts(0);
1473 let peers: Vec<_> = (0..5)
1474 .map(|i| make_peer(&format!("mp-{i}"), PdpDiscoveryMethod::Static("a".into())))
1475 .collect();
1476 p.merge_peer_table(peers, t0);
1477 assert!(p.peer_count() <= 2);
1479 }
1480
1481 #[test]
1482 fn test_merge_peer_table_returns_events() {
1483 let mut p = make_protocol();
1484 let t0 = make_ts(0);
1485 let peers: Vec<_> = (0..3)
1486 .map(|i| {
1487 make_peer(
1488 &format!("evt-{i}"),
1489 PdpDiscoveryMethod::Bootstrap("b".into()),
1490 )
1491 })
1492 .collect();
1493 let events = p.merge_peer_table(peers, t0);
1494 assert_eq!(events.len(), 3);
1495 }
1496
1497 #[test]
1502 fn test_stats_total_discovered() {
1503 let mut p = make_protocol();
1504 for i in 0..3u32 {
1505 p.add_peer(make_peer(
1506 &format!("st-{i}"),
1507 PdpDiscoveryMethod::Bootstrap("b".into()),
1508 ))
1509 .expect("test: add st peer should succeed");
1510 }
1511 assert_eq!(p.stats().total_discovered, 3);
1512 }
1513
1514 #[test]
1515 fn test_stats_active_peers() {
1516 let mut p = make_protocol();
1517 for i in 0..4u32 {
1518 p.add_peer(make_peer(
1519 &format!("ap-{i}"),
1520 PdpDiscoveryMethod::Static("a".into()),
1521 ))
1522 .expect("test: add ap peer should succeed");
1523 }
1524 assert_eq!(p.stats().active_peers, 4);
1525 }
1526
1527 #[test]
1528 fn test_stats_expired_peers() {
1529 let mut p = make_protocol();
1530 let t0 = make_ts(0);
1531 let short = PdpDiscoveredPeer::new(
1532 "s",
1533 vec![],
1534 t0,
1535 PdpDiscoveryMethod::Static("x".into()),
1536 100,
1537 vec![],
1538 );
1539 p.add_peer(short)
1540 .expect("test: add short-lived peer should succeed");
1541 p.expire_peers(t0 + 200);
1542 assert_eq!(p.stats().expired_peers, 1);
1543 }
1544
1545 #[test]
1546 fn test_stats_by_method() {
1547 let mut p = make_protocol();
1548 p.add_peer(make_peer("s1", PdpDiscoveryMethod::Bootstrap("b".into())))
1549 .expect("test: add s1 bootstrap peer should succeed");
1550 p.add_peer(make_peer("s2", PdpDiscoveryMethod::Bootstrap("b".into())))
1551 .expect("test: add s2 bootstrap peer should succeed");
1552 p.add_peer(make_peer(
1553 "s3",
1554 PdpDiscoveryMethod::Mdns {
1555 service_name: "s".into(),
1556 },
1557 ))
1558 .expect("test: add s3 mdns peer should succeed");
1559 let stats = p.stats();
1560 let boot_count = stats
1561 .by_method
1562 .iter()
1563 .find(|(m, _)| m == "bootstrap")
1564 .map(|(_, c)| *c)
1565 .unwrap_or(0);
1566 let mdns_count = stats
1567 .by_method
1568 .iter()
1569 .find(|(m, _)| m == "mdns")
1570 .map(|(_, c)| *c)
1571 .unwrap_or(0);
1572 assert_eq!(boot_count, 2);
1573 assert_eq!(mdns_count, 1);
1574 }
1575
1576 #[test]
1577 fn test_stats_verifications() {
1578 let mut p = make_protocol();
1579 p.add_peer(make_peer("vv1", PdpDiscoveryMethod::Static("a".into())))
1580 .expect("test: add vv1 peer should succeed");
1581 p.add_peer(make_peer("vv2", PdpDiscoveryMethod::Static("b".into())))
1582 .expect("test: add vv2 peer should succeed");
1583 p.verify_peer("vv1", make_ts(0))
1584 .expect("test: verify_peer vv1 should succeed");
1585 p.verify_peer("vv2", make_ts(1))
1586 .expect("test: verify_peer vv2 should succeed");
1587 assert_eq!(p.stats().verifications_performed, 2);
1588 }
1589
1590 #[test]
1595 fn test_drain_events_clears_buffer() {
1596 let mut p = make_protocol();
1597 p.add_peer(make_peer("ev1", PdpDiscoveryMethod::Static("a".into())))
1598 .expect("test: add ev1 peer should succeed");
1599 let first = p.drain_events();
1600 assert!(!first.is_empty());
1601 let second = p.drain_events();
1602 assert!(second.is_empty());
1603 }
1604
1605 #[test]
1606 fn test_drain_events_accumulates() {
1607 let mut p = make_protocol();
1608 p.add_peer(make_peer("e1", PdpDiscoveryMethod::Static("a".into())))
1609 .expect("test: add e1 peer should succeed");
1610 p.add_peer(make_peer("e2", PdpDiscoveryMethod::Static("b".into())))
1611 .expect("test: add e2 peer should succeed");
1612 let evs = p.drain_events();
1613 assert_eq!(evs.len(), 2);
1614 }
1615
1616 #[test]
1617 fn test_drain_events_after_expiry() {
1618 let mut p = make_protocol();
1619 let t0 = make_ts(0);
1620 let peer = PdpDiscoveredPeer::new(
1621 "de1",
1622 vec![],
1623 t0,
1624 PdpDiscoveryMethod::Static("x".into()),
1625 100,
1626 vec![],
1627 );
1628 p.add_peer(peer).expect("test: add de1 peer should succeed");
1629 p.drain_events(); p.expire_peers(t0 + 200);
1631 let evs = p.drain_events();
1632 assert_eq!(evs.len(), 1);
1633 assert!(matches!(&evs[0], PdpDiscoveryEvent::PeerExpired(_)));
1634 }
1635
1636 #[test]
1641 fn test_error_display_peer_not_found() {
1642 let err = PdpDiscoveryError::PeerNotFound("abc".into());
1643 assert!(err.to_string().contains("abc"));
1644 }
1645
1646 #[test]
1647 fn test_error_display_max_peers() {
1648 let err = PdpDiscoveryError::MaxPeersExceeded;
1649 assert!(!err.to_string().is_empty());
1650 }
1651
1652 #[test]
1653 fn test_error_display_invalid_address() {
1654 let err = PdpDiscoveryError::InvalidAddress("bad-addr".into());
1655 assert!(err.to_string().contains("bad-addr"));
1656 }
1657
1658 #[test]
1659 fn test_error_display_duplicate_peer() {
1660 let err = PdpDiscoveryError::DuplicatePeer("dup".into());
1661 assert!(err.to_string().contains("dup"));
1662 }
1663
1664 #[test]
1665 fn test_error_display_config_error() {
1666 let err = PdpDiscoveryError::ConfigurationError("bad".into());
1667 assert!(err.to_string().contains("bad"));
1668 }
1669
1670 #[test]
1675 fn test_xorshift64_not_zero() {
1676 let mut state = 12345;
1677 let val = xorshift64(&mut state);
1678 assert_ne!(val, 0);
1679 }
1680
1681 #[test]
1682 fn test_xorshift64_sequence_is_different() {
1683 let mut state = 1;
1684 let v1 = xorshift64(&mut state);
1685 let v2 = xorshift64(&mut state);
1686 assert_ne!(v1, v2);
1687 }
1688
1689 #[test]
1690 fn test_xorshift64_deterministic() {
1691 let mut s1 = 42u64;
1692 let mut s2 = 42u64;
1693 for _ in 0..100 {
1694 assert_eq!(xorshift64(&mut s1), xorshift64(&mut s2));
1695 }
1696 }
1697
1698 #[test]
1703 fn test_variant_names() {
1704 assert_eq!(
1705 PdpDiscoveryMethod::Bootstrap("u".into()).variant_name(),
1706 "bootstrap"
1707 );
1708 assert_eq!(
1709 PdpDiscoveryMethod::Mdns {
1710 service_name: "s".into()
1711 }
1712 .variant_name(),
1713 "mdns"
1714 );
1715 assert_eq!(
1716 PdpDiscoveryMethod::Dht {
1717 query_key: "k".into()
1718 }
1719 .variant_name(),
1720 "dht"
1721 );
1722 assert_eq!(
1723 PdpDiscoveryMethod::PeerExchange {
1724 source_peer: "p".into()
1725 }
1726 .variant_name(),
1727 "peer_exchange"
1728 );
1729 assert_eq!(
1730 PdpDiscoveryMethod::Static("a".into()).variant_name(),
1731 "static"
1732 );
1733 assert_eq!(
1734 PdpDiscoveryMethod::Rendezvous {
1735 namespace: "n".into()
1736 }
1737 .variant_name(),
1738 "rendezvous"
1739 );
1740 }
1741
1742 #[test]
1747 fn test_record_method_failure_emits_event() {
1748 let mut p = make_protocol();
1749 p.record_method_failure("dht", "timeout");
1750 let evs = p.drain_events();
1751 assert_eq!(evs.len(), 1);
1752 assert!(matches!(
1753 &evs[0],
1754 PdpDiscoveryEvent::DiscoveryMethodFailed { method, reason }
1755 if method == "dht" && reason == "timeout"
1756 ));
1757 }
1758
1759 #[test]
1764 fn test_all_peers_iterator() {
1765 let mut p = make_protocol();
1766 for i in 0..5u32 {
1767 p.add_peer(make_peer(
1768 &format!("it-{i}"),
1769 PdpDiscoveryMethod::Static("a".into()),
1770 ))
1771 .expect("test: add it peer should succeed");
1772 }
1773 assert_eq!(p.all_peers().count(), 5);
1774 }
1775
1776 #[test]
1781 fn test_type_aliases_are_usable() {
1782 let _: DiscoveredPeer = PdpDiscoveredPeer::new(
1783 "alias-test",
1784 vec![],
1785 0,
1786 PdpDiscoveryMethod::Static("x".into()),
1787 1000,
1788 vec![],
1789 );
1790 let _: DiscoveryConfig = PdpDiscoveryConfig::default();
1791 let _: DiscoveryError = PdpDiscoveryError::MaxPeersExceeded;
1792 }
1793}