diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-11-19 17:01:36 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-11-19 17:01:36 +0000 |
| commit | bf7f4d5381203d5c27b2811d62c5b1781533aa2b (patch) | |
| tree | 26903bbf535d83abd7242370d8b6932eb80e3389 /grasp-audit/src/specs/grasp01/nip01_smoke.rs | |
| parent | fa065ad128882755f2a988d6203b59a2ab5e38ff (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.rs | 112 |
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 { | |||
| 287 | mod tests { | 293 | mod 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 | } |