1#![warn(
2 clippy::all,
3 clippy::style,
4 clippy::unseparated_literal_suffix,
5 clippy::pedantic,
6 clippy::nursery
7)]
8pub mod errors;
9mod pool;
10mod relay;
11pub extern crate nostro2;
12pub use pool::NostrPool;
13pub use relay::{NostrRelay, ReconnectConfig};
14
15#[cfg(test)]
16mod tests {
17
18 #[tokio::test]
19 async fn test_relay() {
20 let relay = super::relay::NostrRelay::new("wss://relay.illuminodes.com")
21 .await
22 .expect("Failed to create relay");
23 let filter = nostro2::NostrSubscription {
24 kinds: vec![1].into(),
25 limit: Some(100),
26 ..Default::default()
27 };
28 relay.send(filter).expect("Failed to send filter");
29 let msg = relay.recv().await;
30 assert!(msg.is_some());
31 }
32 #[tokio::test]
33 async fn notes_deduped_correctly() {
34 let pool =
35 super::pool::NostrPool::new(&["wss://relay.illuminodes.com", "wss://freespeech.casa"]);
36 let mut seen = std::collections::HashSet::new();
37
38 let pool_clone = pool.clone();
39 let filter = nostro2::NostrSubscription {
40 kinds: vec![24442].into(),
41 ..Default::default()
42 };
43 pool.send(&filter).expect("Failed to send filter");
44
45 let mut test_note = nostro2::NostrNote {
46 content: "test".to_string(),
47 kind: 24442,
48 ..Default::default()
49 };
50 let test_key = nostro2_signer::NostrKeypair::new_extractable();
51 test_key
52 .sign_note(&mut test_note)
53 .expect("Failed to sign note");
54
55 let mut test_note_2 = nostro2::NostrNote {
56 content: "test-2".to_string(),
57 kind: 24442,
58 ..Default::default()
59 };
60 test_key
61 .sign_note(&mut test_note_2)
62 .expect("Failed to sign note");
63 pool.send(test_note).expect("Failed to send note");
64 pool.send(test_note_2).expect("Failed to send note");
65 while let Some(msg) = pool_clone.recv().await {
66 println!("{msg:#?}");
67 if let nostro2::NostrRelayEvent::NewNote(.., ref note) = msg {
68 assert!(seen.insert(note.id.clone()));
69 if seen.len() == 2 {
70 break;
71 }
72 }
73 }
74 }
75 #[tokio::test]
76 async fn test_relay_pool_count() {
77 let pool = super::pool::NostrPool::new(&[
78 "wss://relay.illuminodes.com",
79 "wss://relay.arrakis.lat",
80 "wss://frens.nostr1.com",
81 "wss://bitcoiner.social",
82 "wss://bouncer.minibolt.info",
83 "wss://freespeech.casa",
84 "wss://junxingwang.org",
85 "wss://nostr.0x7e.xyz",
86 ]);
87 let filter = nostro2::NostrSubscription {
88 kinds: vec![1].into(),
89 limit: Some(10),
90 ..Default::default()
91 };
92 pool.send(&filter).expect("Failed to send filter");
93 let mut count = 0;
94 let mut eose = 0;
95 while let Some(msg) = pool.recv().await {
96 match msg {
97 nostro2::NostrRelayEvent::NewNote(..) => {
98 println!("{msg:?}");
99 count += 1;
100 }
101 nostro2::NostrRelayEvent::EndOfSubscription(_, _) => {
102 println!("{msg:?}");
103 eose += 1;
104 }
105 _ => {}
106 }
107 if eose == 2 {
108 break;
109 }
110 }
111 assert!(count > 10);
112 }
113
114 #[tokio::test]
115 async fn test_pool() {
116 let time_spent = std::time::Instant::now();
117 let pool = super::pool::NostrPool::new(&[
118 "wss://relay.illuminodes.com",
119 "wss://relay.arrakis.lat",
120 "wss://frens.nostr1.com",
121 "wss://bitcoiner.social",
122 "wss://bouncer.minibolt.info",
123 "wss://freespeech.casa",
124 "wss://junxingwang.org",
125 "wss://nostr.0x7e.xyz",
126 ]);
127 println!("Connected in: {:?}", time_spent.elapsed());
128 let filter = nostro2::NostrSubscription {
129 kinds: vec![1].into(),
130 limit: Some(20),
131 ..Default::default()
132 };
133 pool.send(&filter).expect("Failed to send filter");
134 println!("Sent filter in: {:?}", time_spent.elapsed());
135 let mut count = 0;
136 while let Some(msg) = pool.recv().await {
137 let nostro2::NostrRelayEvent::EndOfSubscription(_, _) = msg else {
138 continue;
139 };
140 println!("{msg:?}");
141 println!("Received in: {:?}", time_spent.elapsed());
142 count += 1;
143 if count >= 3 {
144 break;
145 }
146 }
147 assert!(count >= 3);
148 println!("Done in: {:?}", time_spent.elapsed());
149 }
150}