upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/grasp-audit/src/specs/grasp01/nip01_smoke.rs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-11-19 17:01:36 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-11-19 17:01:36 +0000
commitbf7f4d5381203d5c27b2811d62c5b1781533aa2b (patch)
tree26903bbf535d83abd7242370d8b6932eb80e3389 /grasp-audit/src/specs/grasp01/nip01_smoke.rs
parentfa065ad128882755f2a988d6203b59a2ab5e38ff (diff)
fix some clippy fmt warnings
Diffstat (limited to 'grasp-audit/src/specs/grasp01/nip01_smoke.rs')
-rw-r--r--grasp-audit/src/specs/grasp01/nip01_smoke.rs112
1 files changed, 59 insertions, 53 deletions
diff --git a/grasp-audit/src/specs/grasp01/nip01_smoke.rs b/grasp-audit/src/specs/grasp01/nip01_smoke.rs
index 9ed0f56..204ee60 100644
--- a/grasp-audit/src/specs/grasp01/nip01_smoke.rs
+++ b/grasp-audit/src/specs/grasp01/nip01_smoke.rs
@@ -13,7 +13,7 @@ impl Nip01SmokeTests {
13 /// Run all NIP-01 smoke tests 13 /// Run all NIP-01 smoke tests
14 pub async fn run_all(client: &AuditClient) -> AuditResult { 14 pub async fn run_all(client: &AuditClient) -> AuditResult {
15 let mut results = AuditResult::new("NIP-01 Smoke Tests"); 15 let mut results = AuditResult::new("NIP-01 Smoke Tests");
16 16
17 // Run tests sequentially to avoid future type issues 17 // Run tests sequentially to avoid future type issues
18 results.add(Self::test_websocket_connection(client).await); 18 results.add(Self::test_websocket_connection(client).await);
19 results.add(Self::test_send_receive_event(client).await); 19 results.add(Self::test_send_receive_event(client).await);
@@ -21,10 +21,10 @@ impl Nip01SmokeTests {
21 results.add(Self::test_close_subscription(client).await); 21 results.add(Self::test_close_subscription(client).await);
22 results.add(Self::test_reject_invalid_signature(client).await); 22 results.add(Self::test_reject_invalid_signature(client).await);
23 results.add(Self::test_reject_invalid_event_id(client).await); 23 results.add(Self::test_reject_invalid_event_id(client).await);
24 24
25 results 25 results
26 } 26 }
27 27
28 /// Test 1: Can establish WebSocket connection 28 /// Test 1: Can establish WebSocket connection
29 /// 29 ///
30 /// Spec: NIP-01 basic requirement 30 /// Spec: NIP-01 basic requirement
@@ -39,17 +39,17 @@ impl Nip01SmokeTests {
39 if !client.is_connected().await { 39 if !client.is_connected().await {
40 return Err("Failed to connect to relay".to_string()); 40 return Err("Failed to connect to relay".to_string());
41 } 41 }
42 42
43 Ok(()) 43 Ok(())
44 }) 44 })
45 .await 45 .await
46 } 46 }
47 47
48 /// Test 2: Can send EVENT and receive OK response 48 /// Test 2: Can send EVENT and receive OK response
49 /// 49 ///
50 /// Spec: NIP-01 EVENT message 50 /// Spec: NIP-01 EVENT message
51 /// Requirement: Relay MUST accept valid EVENT messages 51 /// Requirement: Relay MUST accept valid EVENT messages
52 /// 52 ///
53 /// For GRASP servers, we send a NIP-34 repository announcement that lists 53 /// For GRASP servers, we send a NIP-34 repository announcement that lists
54 /// the GRASP server in clone and relays tags (required for acceptance). 54 /// the GRASP server in clone and relays tags (required for acceptance).
55 async fn test_send_receive_event(client: &AuditClient) -> TestResult { 55 async fn test_send_receive_event(client: &AuditClient) -> TestResult {
@@ -60,15 +60,17 @@ impl Nip01SmokeTests {
60 ) 60 )
61 .run(|| async { 61 .run(|| async {
62 // Create a NIP-34 announcement event 62 // Create a NIP-34 announcement event
63 let event = client.create_repo_announcement("send_receive_event").await 63 let event = client
64 .create_repo_announcement("send_receive_event")
65 .await
64 .map_err(|e| format!("Failed to create announcement: {}", e))?; 66 .map_err(|e| format!("Failed to create announcement: {}", e))?;
65 67
66 // Send event 68 // Send event
67 let event_id = client 69 let event_id = client
68 .send_event(event.clone()) 70 .send_event(event.clone())
69 .await 71 .await
70 .map_err(|e| format!("Failed to send event: {}", e))?; 72 .map_err(|e| format!("Failed to send event: {}", e))?;
71 73
72 // Verify we got an event ID back 74 // Verify we got an event ID back
73 if event_id != event.id { 75 if event_id != event.id {
74 return Err(format!( 76 return Err(format!(
@@ -76,43 +78,47 @@ impl Nip01SmokeTests {
76 event.id, event_id 78 event.id, event_id
77 )); 79 ));
78 } 80 }
79 81
80 // Wait a bit for event to be indexed 82 // Wait a bit for event to be indexed
81 tokio::time::sleep(std::time::Duration::from_millis(100)).await; 83 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
82 84
83 // Try to query it back 85 // Try to query it back
84 let filter = Filter::new() 86 let filter = Filter::new().kind(Kind::Custom(30617)).id(event_id);
85 .kind(Kind::Custom(30617)) 87
86 .id(event_id);
87
88 let events = client 88 let events = client
89 .query(filter) 89 .query(filter)
90 .await 90 .await
91 .map_err(|e| format!("Failed to query event: {}", e))?; 91 .map_err(|e| format!("Failed to query event: {}", e))?;
92 92
93 if events.is_empty() { 93 if events.is_empty() {
94 // Debug: try querying without audit client filtering 94 // Debug: try querying without audit client filtering
95 eprintln!("Event not found with audit client query, trying direct client query..."); 95 eprintln!("Event not found with audit client query, trying direct client query...");
96 let direct_filter = Filter::new().kind(Kind::Custom(30617)).id(event_id); 96 let direct_filter = Filter::new().kind(Kind::Custom(30617)).id(event_id);
97 let direct_events = client.client().fetch_events(direct_filter, std::time::Duration::from_secs(5)).await 97 let direct_events = client
98 .client()
99 .fetch_events(direct_filter, std::time::Duration::from_secs(5))
100 .await
98 .map_err(|e| format!("Direct query failed: {}", e))?; 101 .map_err(|e| format!("Direct query failed: {}", e))?;
99 let direct_vec: Vec<Event> = direct_events.into_iter().collect(); 102 let direct_vec: Vec<Event> = direct_events.into_iter().collect();
100 eprintln!("Direct query found {} events", direct_vec.len()); 103 eprintln!("Direct query found {} events", direct_vec.len());
101 if !direct_vec.is_empty() { 104 if !direct_vec.is_empty() {
102 eprintln!("Event tags: {:?}", direct_vec[0].tags); 105 eprintln!("Event tags: {:?}", direct_vec[0].tags);
103 } 106 }
104 return Err(format!("Event not found after sending (direct query found {})", direct_vec.len())); 107 return Err(format!(
108 "Event not found after sending (direct query found {})",
109 direct_vec.len()
110 ));
105 } 111 }
106 112
107 if events[0].id != event_id { 113 if events[0].id != event_id {
108 return Err("Retrieved event has different ID".to_string()); 114 return Err("Retrieved event has different ID".to_string());
109 } 115 }
110 116
111 Ok(()) 117 Ok(())
112 }) 118 })
113 .await 119 .await
114 } 120 }
115 121
116 /// Test 3: Can create subscription with REQ 122 /// Test 3: Can create subscription with REQ
117 /// 123 ///
118 /// Spec: NIP-01 REQ message 124 /// Spec: NIP-01 REQ message
@@ -125,34 +131,36 @@ impl Nip01SmokeTests {
125 ) 131 )
126 .run(|| async { 132 .run(|| async {
127 // Create a NIP-34 announcement event (accepted by GRASP relays) 133 // Create a NIP-34 announcement event (accepted by GRASP relays)
128 let event = client.create_repo_announcement("create_subscription").await 134 let event = client
135 .create_repo_announcement("create_subscription")
136 .await
129 .map_err(|e| format!("Failed to create announcement: {}", e))?; 137 .map_err(|e| format!("Failed to create announcement: {}", e))?;
130 138
131 let event_id = client 139 let _event_id = client
132 .send_event(event.clone()) 140 .send_event(event.clone())
133 .await 141 .await
134 .map_err(|e| format!("Failed to send event: {}", e))?; 142 .map_err(|e| format!("Failed to send event: {}", e))?;
135 143
136 // Subscribe to NIP-34 announcements from this author 144 // Subscribe to NIP-34 announcements from this author
137 let filter = Filter::new() 145 let filter = Filter::new()
138 .kind(Kind::Custom(30617)) 146 .kind(Kind::Custom(30617))
139 .author(client.public_key()); 147 .author(client.public_key());
140 148
141 let events = client 149 let events = client
142 .subscribe(vec![filter], Some(std::time::Duration::from_secs(5))) 150 .subscribe(vec![filter], Some(std::time::Duration::from_secs(5)))
143 .await 151 .await
144 .map_err(|e| format!("Failed to subscribe: {}", e))?; 152 .map_err(|e| format!("Failed to subscribe: {}", e))?;
145 153
146 // Should have at least our event 154 // Should have at least our event
147 if events.is_empty() { 155 if events.is_empty() {
148 return Err("No events received from subscription".to_string()); 156 return Err("No events received from subscription".to_string());
149 } 157 }
150 158
151 Ok(()) 159 Ok(())
152 }) 160 })
153 .await 161 .await
154 } 162 }
155 163
156 /// Test 4: Can close subscription with CLOSE 164 /// Test 4: Can close subscription with CLOSE
157 /// 165 ///
158 /// Spec: NIP-01 CLOSE message 166 /// Spec: NIP-01 CLOSE message
@@ -167,22 +175,20 @@ impl Nip01SmokeTests {
167 // For now, we just verify we can query events 175 // For now, we just verify we can query events
168 // Full subscription management with CLOSE would require 176 // Full subscription management with CLOSE would require
169 // lower-level WebSocket access 177 // lower-level WebSocket access
170 178
171 let filter = Filter::new() 179 let filter = Filter::new().kind(Kind::TextNote).limit(1);
172 .kind(Kind::TextNote) 180
173 .limit(1);
174
175 let _events = client 181 let _events = client
176 .subscribe(vec![filter], Some(std::time::Duration::from_secs(2))) 182 .subscribe(vec![filter], Some(std::time::Duration::from_secs(2)))
177 .await 183 .await
178 .map_err(|e| format!("Failed to subscribe: {}", e))?; 184 .map_err(|e| format!("Failed to subscribe: {}", e))?;
179 185
180 // If we got here, subscription worked 186 // If we got here, subscription worked
181 Ok(()) 187 Ok(())
182 }) 188 })
183 .await 189 .await
184 } 190 }
185 191
186 /// Test 5: Rejects events with invalid signatures 192 /// Test 5: Rejects events with invalid signatures
187 /// 193 ///
188 /// Spec: NIP-01 event validation 194 /// Spec: NIP-01 event validation
@@ -199,7 +205,7 @@ impl Nip01SmokeTests {
199 .event_builder(Kind::TextNote, "Invalid signature test") 205 .event_builder(Kind::TextNote, "Invalid signature test")
200 .build(client.keys()) 206 .build(client.keys())
201 .map_err(|e| format!("Failed to build event: {}", e))?; 207 .map_err(|e| format!("Failed to build event: {}", e))?;
202 208
203 // Corrupt the signature by creating a new event with wrong sig 209 // Corrupt the signature by creating a new event with wrong sig
204 // We'll use a different key to sign, creating an invalid signature 210 // We'll use a different key to sign, creating an invalid signature
205 let wrong_keys = Keys::generate(); 211 let wrong_keys = Keys::generate();
@@ -207,7 +213,7 @@ impl Nip01SmokeTests {
207 .tags(event.tags.clone()) 213 .tags(event.tags.clone())
208 .sign_with_keys(&wrong_keys) 214 .sign_with_keys(&wrong_keys)
209 .map_err(|e| format!("Failed to build wrong event: {}", e))?; 215 .map_err(|e| format!("Failed to build wrong event: {}", e))?;
210 216
211 // Create event JSON with mismatched pubkey and signature 217 // Create event JSON with mismatched pubkey and signature
212 // This should be rejected by the relay 218 // This should be rejected by the relay
213 let invalid_event_json = serde_json::json!({ 219 let invalid_event_json = serde_json::json!({
@@ -219,24 +225,24 @@ impl Nip01SmokeTests {
219 "content": event.content, 225 "content": event.content,
220 "sig": wrong_event.sig.to_string(), // Wrong signature! 226 "sig": wrong_event.sig.to_string(), // Wrong signature!
221 }); 227 });
222 228
223 // Parse it back to an Event 229 // Parse it back to an Event
224 let invalid_event: Event = serde_json::from_value(invalid_event_json) 230 let invalid_event: Event = serde_json::from_value(invalid_event_json)
225 .map_err(|e| format!("Failed to create invalid event: {}", e))?; 231 .map_err(|e| format!("Failed to create invalid event: {}", e))?;
226 232
227 // Try to send the invalid event 233 // Try to send the invalid event
228 let result = client.send_event(invalid_event).await; 234 let result = client.send_event(invalid_event).await;
229 235
230 // We expect this to fail 236 // We expect this to fail
231 if result.is_ok() { 237 if result.is_ok() {
232 return Err("Relay accepted event with invalid signature".to_string()); 238 return Err("Relay accepted event with invalid signature".to_string());
233 } 239 }
234 240
235 Ok(()) 241 Ok(())
236 }) 242 })
237 .await 243 .await
238 } 244 }
239 245
240 /// Test 6: Rejects events with invalid event IDs 246 /// Test 6: Rejects events with invalid event IDs
241 /// 247 ///
242 /// Spec: NIP-01 event ID validation 248 /// Spec: NIP-01 event ID validation
@@ -253,7 +259,7 @@ impl Nip01SmokeTests {
253 .event_builder(Kind::TextNote, "Invalid ID test") 259 .event_builder(Kind::TextNote, "Invalid ID test")
254 .build(client.keys()) 260 .build(client.keys())
255 .map_err(|e| format!("Failed to build event: {}", e))?; 261 .map_err(|e| format!("Failed to build event: {}", e))?;
256 262
257 // Create event JSON with corrupted ID 263 // Create event JSON with corrupted ID
258 let invalid_event_json = serde_json::json!({ 264 let invalid_event_json = serde_json::json!({
259 "id": EventId::all_zeros().to_hex(), // Wrong ID! 265 "id": EventId::all_zeros().to_hex(), // Wrong ID!
@@ -264,19 +270,19 @@ impl Nip01SmokeTests {
264 "content": event.content, 270 "content": event.content,
265 "sig": event.sig.to_string(), 271 "sig": event.sig.to_string(),
266 }); 272 });
267 273
268 // Parse it back to an Event 274 // Parse it back to an Event
269 let invalid_event: Event = serde_json::from_value(invalid_event_json) 275 let invalid_event: Event = serde_json::from_value(invalid_event_json)
270 .map_err(|e| format!("Failed to create invalid event: {}", e))?; 276 .map_err(|e| format!("Failed to create invalid event: {}", e))?;
271 277
272 // Try to send the invalid event 278 // Try to send the invalid event
273 let result = client.send_event(invalid_event).await; 279 let result = client.send_event(invalid_event).await;
274 280
275 // We expect this to fail 281 // We expect this to fail
276 if result.is_ok() { 282 if result.is_ok() {
277 return Err("Relay accepted event with invalid ID".to_string()); 283 return Err("Relay accepted event with invalid ID".to_string());
278 } 284 }
279 285
280 Ok(()) 286 Ok(())
281 }) 287 })
282 .await 288 .await
@@ -287,25 +293,25 @@ impl Nip01SmokeTests {
287mod tests { 293mod tests {
288 use super::*; 294 use super::*;
289 use crate::AuditConfig; 295 use crate::AuditConfig;
290 296
291 // Note: These tests require a running relay 297 // Note: These tests require a running relay
292 // They are integration tests, not unit tests 298 // They are integration tests, not unit tests
293 299
294 #[tokio::test] 300 #[tokio::test]
295 #[ignore] // Ignore by default since it needs a running relay 301 #[ignore] // Ignore by default since it needs a running relay
296 async fn test_smoke_tests_against_relay() { 302 async fn test_smoke_tests_against_relay() {
297 // RELAY_URL env var must be set - no default fallback 303 // RELAY_URL env var must be set - no default fallback
298 let relay_url = std::env::var("RELAY_URL") 304 let relay_url = std::env::var("RELAY_URL")
299 .expect("RELAY_URL environment variable must be set for integration tests"); 305 .expect("RELAY_URL environment variable must be set for integration tests");
300 306
301 let config = AuditConfig::ci(); 307 let config = AuditConfig::ci();
302 let client = AuditClient::new(&relay_url, config) 308 let client = AuditClient::new(&relay_url, config)
303 .await 309 .await
304 .expect("Failed to connect to relay"); 310 .expect("Failed to connect to relay");
305 311
306 let results = Nip01SmokeTests::run_all(&client).await; 312 let results = Nip01SmokeTests::run_all(&client).await;
307 results.print_report(); 313 results.print_report();
308 314
309 assert!(results.all_passed(), "Some smoke tests failed"); 315 assert!(results.all_passed(), "Some smoke tests failed");
310 } 316 }
311} 317}