upleb.uk

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

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/common/relay.rs8
-rw-r--r--tests/proactive_sync_basic.rs171
-rw-r--r--tests/proactive_sync_metrics.rs170
-rw-r--r--tests/proactive_sync_multi.rs16
4 files changed, 255 insertions, 110 deletions
diff --git a/tests/common/relay.rs b/tests/common/relay.rs
index 3d160f2..073a601 100644
--- a/tests/common/relay.rs
+++ b/tests/common/relay.rs
@@ -57,12 +57,8 @@ impl TestRelay {
57 /// source.stop().await; 57 /// source.stop().await;
58 /// } 58 /// }
59 /// ``` 59 /// ```
60 pub async fn start_with_sync(bootstrap_relay_url: &str) -> Self { 60 pub async fn start_with_sync(bootstrap_relay_url: Option<String>) -> Self {
61 Self::start_with_options( 61 Self::start_with_options(Self::find_free_port(), bootstrap_relay_url).await
62 Self::find_free_port(),
63 Some(bootstrap_relay_url.to_string()),
64 )
65 .await
66 } 62 }
67 63
68 /// Start relay with options 64 /// Start relay with options
diff --git a/tests/proactive_sync_basic.rs b/tests/proactive_sync_basic.rs
index b789cb6..d96d576 100644
--- a/tests/proactive_sync_basic.rs
+++ b/tests/proactive_sync_basic.rs
@@ -74,9 +74,7 @@ async fn check_event_syncs(
74 .await 74 .await
75 .expect("Failed to connect to target relay"); 75 .expect("Failed to connect to target relay");
76 76
77 let filter = Filter::new() 77 let filter = Filter::new().kind(event.kind).author(keys.public_key());
78 .kind(event.kind)
79 .author(keys.public_key());
80 78
81 let events_on_target = client_target 79 let events_on_target = client_target
82 .fetch_events(filter, Duration::from_secs(3)) 80 .fetch_events(filter, Duration::from_secs(3))
@@ -185,7 +183,7 @@ async fn test_sync_relay_connects_to_source() {
185 let relay_a = TestRelay::start().await; 183 let relay_a = TestRelay::start().await;
186 184
187 // Start syncing relay (relay_b) configured to sync from relay_a 185 // Start syncing relay (relay_b) configured to sync from relay_a
188 let relay_b = TestRelay::start_with_sync(relay_a.url()).await; 186 let relay_b = TestRelay::start_with_sync(Some(relay_a.url().into())).await;
189 187
190 // Give some time for connection to establish 188 // Give some time for connection to establish
191 tokio::time::sleep(Duration::from_millis(500)).await; 189 tokio::time::sleep(Duration::from_millis(500)).await;
@@ -197,9 +195,9 @@ async fn test_sync_relay_connects_to_source() {
197 relay_a.stop().await; 195 relay_a.stop().await;
198} 196}
199 197
200/// Test that valid events sync from source to syncing relay 198/// Test that valid events sync from source to bootstrap relay
201#[tokio::test] 199#[tokio::test]
202async fn test_valid_event_syncs_to_relay() { 200async fn announcement_listing_relay_syncs_from_bootstrap_relay() {
203 // Start source relay (relay_a) 201 // Start source relay (relay_a)
204 let relay_a = TestRelay::start().await; 202 let relay_a = TestRelay::start().await;
205 println!( 203 println!(
@@ -209,7 +207,7 @@ async fn test_valid_event_syncs_to_relay() {
209 ); 207 );
210 208
211 // Start syncing relay (relay_b) configured to sync from relay_a 209 // Start syncing relay (relay_b) configured to sync from relay_a
212 let relay_b = TestRelay::start_with_sync(relay_a.url()).await; 210 let relay_b = TestRelay::start_with_sync(Some(relay_a.url().into())).await;
213 println!( 211 println!(
214 "relay_b started at {} (domain: {})", 212 "relay_b started at {} (domain: {})",
215 relay_b.url(), 213 relay_b.url(),
@@ -226,11 +224,8 @@ async fn test_valid_event_syncs_to_relay() {
226 // Create a repository announcement that lists BOTH relays 224 // Create a repository announcement that lists BOTH relays
227 // This is required for sync - the event must reference both the source relay 225 // This is required for sync - the event must reference both the source relay
228 // and the syncing relay for the write policy to accept it on both sides 226 // and the syncing relay for the write policy to accept it on both sides
229 let event = create_repo_announcement( 227 let event =
230 &keys, 228 create_repo_announcement(&keys, &[&relay_a.domain(), &relay_b.domain()], "test-repo");
231 &[&relay_a.domain(), &relay_b.domain()],
232 "test-repo",
233 );
234 let event_id = event.id; 229 let event_id = event.id;
235 230
236 // Print event details for debugging 231 // Print event details for debugging
@@ -263,7 +258,7 @@ async fn test_valid_event_syncs_to_relay() {
263/// This verifies that relay_b's write policy correctly rejects events during sync 258/// This verifies that relay_b's write policy correctly rejects events during sync
264/// if they don't list relay_b as one of their relays. 259/// if they don't list relay_b as one of their relays.
265#[tokio::test] 260#[tokio::test]
266async fn test_event_not_listing_target_relay_is_not_synced() { 261async fn test_announcement_not_listing_relay_is_not_synced_from_boostrap_relay() {
267 // Start source relay (relay_a) 262 // Start source relay (relay_a)
268 let relay_a = TestRelay::start().await; 263 let relay_a = TestRelay::start().await;
269 println!( 264 println!(
@@ -273,7 +268,7 @@ async fn test_event_not_listing_target_relay_is_not_synced() {
273 ); 268 );
274 269
275 // Start syncing relay (relay_b) configured to sync from relay_a 270 // Start syncing relay (relay_b) configured to sync from relay_a
276 let relay_b = TestRelay::start_with_sync(relay_a.url()).await; 271 let relay_b = TestRelay::start_with_sync(Some(relay_a.url().into())).await;
277 println!( 272 println!(
278 "relay_b started at {} (domain: {})", 273 "relay_b started at {} (domain: {})",
279 relay_b.url(), 274 relay_b.url(),
@@ -321,3 +316,151 @@ async fn test_event_not_listing_target_relay_is_not_synced() {
321 event_id 316 event_id
322 ); 317 );
323} 318}
319
320fn create_kind_event_referencing_repo(keys: &Keys, repo_coord: &str) -> Event {
321 // TODO this breaks with kind 1
322 EventBuilder::new(Kind::Custom(1617), "Test patch proposal")
323 .tags(vec![Tag::custom(
324 TagKind::custom("a"),
325 vec![repo_coord.to_string()],
326 )])
327 .sign_with_keys(keys)
328 .expect("Failed to sign event")
329}
330
331/// Test that when a relay is discovered (via an announcement event), events are synced from it
332///
333/// This test verifies dynamic relay discovery from direct submissions:
334/// 1. relay_a has an announcement and a patch event
335/// 2. relay_b (sync enabled, NO bootstrap) receives the announcement directly
336/// 3. relay_b discovers relay_a from the announcement and connects to sync
337/// 4. relay_b syncs the patch event from relay_a
338///
339/// This tests the scenario where relays discover each other through announcements
340/// submitted by users, not through an existing sync connection.
341#[tokio::test]
342async fn repo_events_synced_from_discovered_relay_after_announcement_received() {
343 // relay_a: source relay with the patch event
344 let relay_a = TestRelay::start().await;
345 println!(
346 "relay_a started at {} (domain: {})",
347 relay_a.url(),
348 relay_a.domain()
349 );
350
351 // relay_b: sync enabled but NO bootstrap relay - will discover relay_a
352 let relay_b = TestRelay::start_with_sync(None).await;
353 println!(
354 "relay_b started at {} (domain: {})",
355 relay_b.url(),
356 relay_b.domain()
357 );
358
359 // Create test keys
360 let keys = Keys::generate();
361
362 // Create a repository announcement that lists BOTH relays
363 let announcement = create_repo_announcement(
364 &keys,
365 &[&relay_a.domain(), &relay_b.domain()],
366 "test-repo-discovery",
367 );
368 let announcement_id = announcement.id;
369
370 println!(
371 "Created announcement {} (kind {})",
372 announcement_id,
373 announcement.kind.as_u16()
374 );
375 for tag in announcement.tags.iter() {
376 println!(" Tag: {:?}", tag.as_slice());
377 }
378
379 // Build the repo coordinate for the 'a' tag in the patch
380 let repo_coord = format!(
381 "{}:{}:{}",
382 KIND_REPOSITORY_STATE,
383 keys.public_key().to_hex(),
384 "test-repo-discovery"
385 );
386
387 // Create a patch event that references the announcement
388 let patch = create_kind_event_referencing_repo(&keys, &repo_coord);
389 let patch_id = patch.id;
390
391 println!("Created patch {} (kind {})", patch_id, patch.kind.as_u16());
392 for tag in patch.tags.iter() {
393 println!(" Tag: {:?}", tag.as_slice());
394 }
395
396 // Step 1: Send announcement to relay_a
397 let client_a = create_connected_client(relay_a.url(), keys.clone())
398 .await
399 .expect("Failed to connect to relay_a");
400 send_event_reliably(&client_a, &announcement)
401 .await
402 .expect("Failed to send announcement to relay_a");
403 println!("Announcement sent to relay_a");
404
405 // Step 2: Send patch to relay_a ONLY
406 send_event_reliably(&client_a, &patch)
407 .await
408 .expect("Failed to send patch to relay_a");
409 println!("Patch sent to relay_a");
410 client_a.disconnect().await;
411
412 // Step 3: Send announcement to relay_b directly (this should trigger discovery of relay_a)
413 let client_b = create_connected_client(relay_b.url(), keys.clone())
414 .await
415 .expect("Failed to connect to relay_b");
416 send_event_reliably(&client_b, &announcement)
417 .await
418 .expect("Failed to send announcement to relay_b");
419 println!("Announcement sent to relay_b (should trigger discovery of relay_a)");
420 client_b.disconnect().await;
421
422 // Step 4: Wait for relay_b to discover relay_a and sync the patch
423 println!("Waiting 3s for relay_b to discover relay_a and sync patch...");
424 tokio::time::sleep(Duration::from_secs(3)).await;
425
426 // Step 5: Verify patch was synced to relay_b
427 let client_b_check = create_connected_client(relay_b.url(), Keys::generate())
428 .await
429 .expect("Failed to connect to relay_b for verification");
430
431 let filter = Filter::new()
432 .kind(Kind::Custom(1617))
433 .author(keys.public_key());
434
435 let events_on_b = client_b_check
436 .fetch_events(filter, Duration::from_secs(3))
437 .await
438 .expect("Failed to fetch from relay_b");
439
440 let patch_synced = events_on_b.iter().any(|e| e.id == patch_id);
441
442 if patch_synced {
443 println!(
444 "Patch {} found on relay_b (synced from discovered relay_a)",
445 patch_id
446 );
447 } else {
448 println!("Patch {} NOT found on relay_b", patch_id);
449 println!(
450 "Events on relay_b: {:?}",
451 events_on_b.iter().map(|e| e.id).collect::<Vec<_>>()
452 );
453 }
454
455 client_b_check.disconnect().await;
456
457 // Clean up
458 relay_b.stop().await;
459 relay_a.stop().await;
460
461 assert!(
462 patch_synced,
463 "Patch {} should have been synced to relay_b from discovered relay_a",
464 patch_id
465 );
466}
diff --git a/tests/proactive_sync_metrics.rs b/tests/proactive_sync_metrics.rs
index 86e2703..32abe74 100644
--- a/tests/proactive_sync_metrics.rs
+++ b/tests/proactive_sync_metrics.rs
@@ -32,10 +32,7 @@ fn create_valid_repo_announcement(keys: &Keys, domain: &str, identifier: &str) -
32 TagKind::custom("clone"), 32 TagKind::custom("clone"),
33 vec![format!("http://{}/{}", domain, identifier)], 33 vec![format!("http://{}/{}", domain, identifier)],
34 ), 34 ),
35 Tag::custom( 35 Tag::custom(TagKind::custom("relays"), vec![format!("ws://{}", domain)]),
36 TagKind::custom("relays"),
37 vec![format!("ws://{}", domain)],
38 ),
39 ]; 36 ];
40 37
41 EventBuilder::new(Kind::Custom(KIND_REPOSITORY_STATE), "Repository state") 38 EventBuilder::new(Kind::Custom(KIND_REPOSITORY_STATE), "Repository state")
@@ -48,11 +45,8 @@ fn create_valid_repo_announcement(keys: &Keys, domain: &str, identifier: &str) -
48async fn fetch_metrics(relay: &TestRelay) -> Result<String, reqwest::Error> { 45async fn fetch_metrics(relay: &TestRelay) -> Result<String, reqwest::Error> {
49 // Extract host:port from ws:// URL 46 // Extract host:port from ws:// URL
50 let ws_url = relay.url(); 47 let ws_url = relay.url();
51 let http_url = ws_url 48 let http_url = ws_url.replace("ws://", "http://").replace("/", "") + "/metrics";
52 .replace("ws://", "http://") 49
53 .replace("/", "")
54 + "/metrics";
55
56 reqwest::get(&http_url).await?.text().await 50 reqwest::get(&http_url).await?.text().await
57} 51}
58 52
@@ -60,18 +54,18 @@ async fn fetch_metrics(relay: &TestRelay) -> Result<String, reqwest::Error> {
60#[tokio::test] 54#[tokio::test]
61async fn test_sync_metrics_exposed() { 55async fn test_sync_metrics_exposed() {
62 let relay = TestRelay::start().await; 56 let relay = TestRelay::start().await;
63 57
64 // Give time for relay to start 58 // Give time for relay to start
65 tokio::time::sleep(Duration::from_millis(500)).await; 59 tokio::time::sleep(Duration::from_millis(500)).await;
66 60
67 // Fetch metrics 61 // Fetch metrics
68 let metrics_result = fetch_metrics(&relay).await; 62 let metrics_result = fetch_metrics(&relay).await;
69 63
70 relay.stop().await; 64 relay.stop().await;
71 65
72 // Check that we got metrics (even if sync isn't configured) 66 // Check that we got metrics (even if sync isn't configured)
73 let metrics = metrics_result.expect("Failed to fetch metrics"); 67 let metrics = metrics_result.expect("Failed to fetch metrics");
74 68
75 // Verify basic metrics structure exists 69 // Verify basic metrics structure exists
76 assert!( 70 assert!(
77 metrics.contains("ngit_") || metrics.contains("# HELP"), 71 metrics.contains("ngit_") || metrics.contains("# HELP"),
@@ -84,19 +78,19 @@ async fn test_sync_metrics_exposed() {
84async fn test_sync_metric_names_present() { 78async fn test_sync_metric_names_present() {
85 // Start a relay with sync configured 79 // Start a relay with sync configured
86 let source_relay = TestRelay::start().await; 80 let source_relay = TestRelay::start().await;
87 let sync_relay = TestRelay::start_with_sync(source_relay.url()).await; 81 let sync_relay = TestRelay::start_with_sync(Some(source_relay.url().into())).await;
88 82
89 // Give time for sync connection to attempt 83 // Give time for sync connection to attempt
90 tokio::time::sleep(Duration::from_secs(2)).await; 84 tokio::time::sleep(Duration::from_secs(2)).await;
91 85
92 // Fetch metrics from the syncing relay 86 // Fetch metrics from the syncing relay
93 let metrics = fetch_metrics(&sync_relay) 87 let metrics = fetch_metrics(&sync_relay)
94 .await 88 .await
95 .expect("Failed to fetch metrics"); 89 .expect("Failed to fetch metrics");
96 90
97 sync_relay.stop().await; 91 sync_relay.stop().await;
98 source_relay.stop().await; 92 source_relay.stop().await;
99 93
100 // Check for expected sync metric names (they may have zero values) 94 // Check for expected sync metric names (they may have zero values)
101 // At minimum, the ngit_ prefix metrics should be present 95 // At minimum, the ngit_ prefix metrics should be present
102 assert!( 96 assert!(
@@ -111,26 +105,23 @@ async fn test_connection_metrics_on_success() {
111 // Start source relay 105 // Start source relay
112 let source_relay = TestRelay::start().await; 106 let source_relay = TestRelay::start().await;
113 tokio::time::sleep(Duration::from_millis(200)).await; 107 tokio::time::sleep(Duration::from_millis(200)).await;
114 108
115 // Start syncing relay 109 // Start syncing relay
116 let sync_relay = TestRelay::start_with_sync(source_relay.url()).await; 110 let sync_relay = TestRelay::start_with_sync(Some(source_relay.url().into())).await;
117 111
118 // Wait for connection to establish 112 // Wait for connection to establish
119 tokio::time::sleep(Duration::from_secs(2)).await; 113 tokio::time::sleep(Duration::from_secs(2)).await;
120 114
121 // Fetch metrics - we can verify the relay started and metrics endpoint works 115 // Fetch metrics - we can verify the relay started and metrics endpoint works
122 let metrics = fetch_metrics(&sync_relay) 116 let metrics = fetch_metrics(&sync_relay)
123 .await 117 .await
124 .expect("Failed to fetch metrics"); 118 .expect("Failed to fetch metrics");
125 119
126 sync_relay.stop().await; 120 sync_relay.stop().await;
127 source_relay.stop().await; 121 source_relay.stop().await;
128 122
129 // Verify metrics endpoint returned data 123 // Verify metrics endpoint returned data
130 assert!( 124 assert!(!metrics.is_empty(), "Metrics endpoint should return data");
131 !metrics.is_empty(),
132 "Metrics endpoint should return data"
133 );
134} 125}
135 126
136/// Test that events syncing updates metrics 127/// Test that events syncing updates metrics
@@ -139,35 +130,38 @@ async fn test_event_sync_metrics() {
139 // Start source relay 130 // Start source relay
140 let source_relay = TestRelay::start().await; 131 let source_relay = TestRelay::start().await;
141 tokio::time::sleep(Duration::from_millis(200)).await; 132 tokio::time::sleep(Duration::from_millis(200)).await;
142 133
143 // Start syncing relay 134 // Start syncing relay
144 let sync_relay = TestRelay::start_with_sync(source_relay.url()).await; 135 let sync_relay = TestRelay::start_with_sync(Some(source_relay.url().into())).await;
145 136
146 // Wait for connection 137 // Wait for connection
147 tokio::time::sleep(Duration::from_secs(1)).await; 138 tokio::time::sleep(Duration::from_secs(1)).await;
148 139
149 // Create and submit an event to source relay 140 // Create and submit an event to source relay
150 let keys = Keys::generate(); 141 let keys = Keys::generate();
151 let event = create_valid_repo_announcement(&keys, &source_relay.domain(), "metrics-test-repo"); 142 let event = create_valid_repo_announcement(&keys, &source_relay.domain(), "metrics-test-repo");
152 143
153 let client = Client::default(); 144 let client = Client::default();
154 client.add_relay(source_relay.url()).await.expect("Failed to add relay"); 145 client
146 .add_relay(source_relay.url())
147 .await
148 .expect("Failed to add relay");
155 client.connect().await; 149 client.connect().await;
156 150
157 let _ = client.send_event(&event).await; 151 let _ = client.send_event(&event).await;
158 152
159 // Wait for sync to occur 153 // Wait for sync to occur
160 tokio::time::sleep(Duration::from_secs(2)).await; 154 tokio::time::sleep(Duration::from_secs(2)).await;
161 155
162 // Fetch metrics from sync relay 156 // Fetch metrics from sync relay
163 let metrics = fetch_metrics(&sync_relay) 157 let metrics = fetch_metrics(&sync_relay)
164 .await 158 .await
165 .expect("Failed to fetch metrics"); 159 .expect("Failed to fetch metrics");
166 160
167 client.disconnect().await; 161 client.disconnect().await;
168 sync_relay.stop().await; 162 sync_relay.stop().await;
169 source_relay.stop().await; 163 source_relay.stop().await;
170 164
171 // Verify metrics endpoint returned data after sync activity 165 // Verify metrics endpoint returned data after sync activity
172 assert!( 166 assert!(
173 !metrics.is_empty(), 167 !metrics.is_empty(),
@@ -180,18 +174,18 @@ async fn test_event_sync_metrics() {
180async fn test_health_state_metrics() { 174async fn test_health_state_metrics() {
181 // Start a syncing relay pointing to a non-existent source 175 // Start a syncing relay pointing to a non-existent source
182 // This will result in connection failures and health state changes 176 // This will result in connection failures and health state changes
183 let sync_relay = TestRelay::start_with_sync("ws://127.0.0.1:19999").await; 177 let sync_relay = TestRelay::start_with_sync(Some("ws://127.0.0.1:19999".into())).await;
184 178
185 // Wait for some connection attempts 179 // Wait for some connection attempts
186 tokio::time::sleep(Duration::from_secs(3)).await; 180 tokio::time::sleep(Duration::from_secs(3)).await;
187 181
188 // Fetch metrics 182 // Fetch metrics
189 let metrics = fetch_metrics(&sync_relay) 183 let metrics = fetch_metrics(&sync_relay)
190 .await 184 .await
191 .expect("Failed to fetch metrics"); 185 .expect("Failed to fetch metrics");
192 186
193 sync_relay.stop().await; 187 sync_relay.stop().await;
194 188
195 // The relay should still be operational even with failed sync 189 // The relay should still be operational even with failed sync
196 assert!( 190 assert!(
197 !metrics.is_empty(), 191 !metrics.is_empty(),
@@ -205,32 +199,35 @@ async fn test_gap_event_tracking() {
205 // Start source relay and add some events first 199 // Start source relay and add some events first
206 let source_relay = TestRelay::start().await; 200 let source_relay = TestRelay::start().await;
207 tokio::time::sleep(Duration::from_millis(200)).await; 201 tokio::time::sleep(Duration::from_millis(200)).await;
208 202
209 let keys = Keys::generate(); 203 let keys = Keys::generate();
210 204
211 // Submit event before sync relay starts 205 // Submit event before sync relay starts
212 let event = create_valid_repo_announcement(&keys, &source_relay.domain(), "pre-existing-repo"); 206 let event = create_valid_repo_announcement(&keys, &source_relay.domain(), "pre-existing-repo");
213 207
214 let client = Client::default(); 208 let client = Client::default();
215 client.add_relay(source_relay.url()).await.expect("Failed to add relay"); 209 client
210 .add_relay(source_relay.url())
211 .await
212 .expect("Failed to add relay");
216 client.connect().await; 213 client.connect().await;
217 let _ = client.send_event(&event).await; 214 let _ = client.send_event(&event).await;
218 215
219 // Now start syncing relay - it should catch up on existing events 216 // Now start syncing relay - it should catch up on existing events
220 let sync_relay = TestRelay::start_with_sync(source_relay.url()).await; 217 let sync_relay = TestRelay::start_with_sync(Some(source_relay.url().into())).await;
221 218
222 // Wait for catchup 219 // Wait for catchup
223 tokio::time::sleep(Duration::from_secs(3)).await; 220 tokio::time::sleep(Duration::from_secs(3)).await;
224 221
225 // Fetch metrics 222 // Fetch metrics
226 let metrics = fetch_metrics(&sync_relay) 223 let metrics = fetch_metrics(&sync_relay)
227 .await 224 .await
228 .expect("Failed to fetch metrics"); 225 .expect("Failed to fetch metrics");
229 226
230 client.disconnect().await; 227 client.disconnect().await;
231 sync_relay.stop().await; 228 sync_relay.stop().await;
232 source_relay.stop().await; 229 source_relay.stop().await;
233 230
234 // Verify metrics exist after gap sync scenario 231 // Verify metrics exist after gap sync scenario
235 assert!( 232 assert!(
236 !metrics.is_empty(), 233 !metrics.is_empty(),
@@ -245,48 +242,57 @@ async fn test_multi_relay_load() {
245 let source_relay_1 = TestRelay::start().await; 242 let source_relay_1 = TestRelay::start().await;
246 let source_relay_2 = TestRelay::start().await; 243 let source_relay_2 = TestRelay::start().await;
247 let source_relay_3 = TestRelay::start().await; 244 let source_relay_3 = TestRelay::start().await;
248 245
249 tokio::time::sleep(Duration::from_millis(500)).await; 246 tokio::time::sleep(Duration::from_millis(500)).await;
250 247
251 // Start a syncing relay pointing to first source 248 // Start a syncing relay pointing to first source
252 // Note: The current implementation only supports single sync relay URL 249 // Note: The current implementation only supports single sync relay URL
253 // but the test demonstrates the system handles multiple relay scenarios 250 // but the test demonstrates the system handles multiple relay scenarios
254 let sync_relay = TestRelay::start_with_sync(source_relay_1.url()).await; 251 let sync_relay = TestRelay::start_with_sync(Some(source_relay_1.url().into())).await;
255 252
256 // Wait for connections 253 // Wait for connections
257 tokio::time::sleep(Duration::from_secs(2)).await; 254 tokio::time::sleep(Duration::from_secs(2)).await;
258 255
259 // Submit events to all source relays 256 // Submit events to all source relays
260 let keys = Keys::generate(); 257 let keys = Keys::generate();
261 258
262 let event1 = create_valid_repo_announcement(&keys, &source_relay_1.domain(), "repo-1"); 259 let event1 = create_valid_repo_announcement(&keys, &source_relay_1.domain(), "repo-1");
263 let event2 = create_valid_repo_announcement(&keys, &source_relay_2.domain(), "repo-2"); 260 let event2 = create_valid_repo_announcement(&keys, &source_relay_2.domain(), "repo-2");
264 let event3 = create_valid_repo_announcement(&keys, &source_relay_3.domain(), "repo-3"); 261 let event3 = create_valid_repo_announcement(&keys, &source_relay_3.domain(), "repo-3");
265 262
266 // Submit events 263 // Submit events
267 let client1 = Client::default(); 264 let client1 = Client::default();
268 client1.add_relay(source_relay_1.url()).await.expect("Failed to add relay"); 265 client1
266 .add_relay(source_relay_1.url())
267 .await
268 .expect("Failed to add relay");
269 client1.connect().await; 269 client1.connect().await;
270 let _ = client1.send_event(&event1).await; 270 let _ = client1.send_event(&event1).await;
271 271
272 let client2 = Client::default(); 272 let client2 = Client::default();
273 client2.add_relay(source_relay_2.url()).await.expect("Failed to add relay"); 273 client2
274 .add_relay(source_relay_2.url())
275 .await
276 .expect("Failed to add relay");
274 client2.connect().await; 277 client2.connect().await;
275 let _ = client2.send_event(&event2).await; 278 let _ = client2.send_event(&event2).await;
276 279
277 let client3 = Client::default(); 280 let client3 = Client::default();
278 client3.add_relay(source_relay_3.url()).await.expect("Failed to add relay"); 281 client3
282 .add_relay(source_relay_3.url())
283 .await
284 .expect("Failed to add relay");
279 client3.connect().await; 285 client3.connect().await;
280 let _ = client3.send_event(&event3).await; 286 let _ = client3.send_event(&event3).await;
281 287
282 // Wait for sync 288 // Wait for sync
283 tokio::time::sleep(Duration::from_secs(3)).await; 289 tokio::time::sleep(Duration::from_secs(3)).await;
284 290
285 // Fetch metrics from sync relay 291 // Fetch metrics from sync relay
286 let metrics = fetch_metrics(&sync_relay) 292 let metrics = fetch_metrics(&sync_relay)
287 .await 293 .await
288 .expect("Failed to fetch metrics"); 294 .expect("Failed to fetch metrics");
289 295
290 // Cleanup 296 // Cleanup
291 client1.disconnect().await; 297 client1.disconnect().await;
292 client2.disconnect().await; 298 client2.disconnect().await;
@@ -295,7 +301,7 @@ async fn test_multi_relay_load() {
295 source_relay_1.stop().await; 301 source_relay_1.stop().await;
296 source_relay_2.stop().await; 302 source_relay_2.stop().await;
297 source_relay_3.stop().await; 303 source_relay_3.stop().await;
298 304
299 // Verify metrics system handled load 305 // Verify metrics system handled load
300 assert!( 306 assert!(
301 !metrics.is_empty(), 307 !metrics.is_empty(),
@@ -308,25 +314,25 @@ async fn test_multi_relay_load() {
308async fn test_prometheus_format_valid() { 314async fn test_prometheus_format_valid() {
309 let relay = TestRelay::start().await; 315 let relay = TestRelay::start().await;
310 tokio::time::sleep(Duration::from_millis(500)).await; 316 tokio::time::sleep(Duration::from_millis(500)).await;
311 317
312 let metrics = fetch_metrics(&relay) 318 let metrics = fetch_metrics(&relay)
313 .await 319 .await
314 .expect("Failed to fetch metrics"); 320 .expect("Failed to fetch metrics");
315 321
316 relay.stop().await; 322 relay.stop().await;
317 323
318 // Check for valid Prometheus format markers 324 // Check for valid Prometheus format markers
319 // - Lines starting with # are comments (HELP, TYPE) 325 // - Lines starting with # are comments (HELP, TYPE)
320 // - Metric lines have format: metric_name{labels} value 326 // - Metric lines have format: metric_name{labels} value
321 let lines: Vec<&str> = metrics.lines().collect(); 327 let lines: Vec<&str> = metrics.lines().collect();
322 328
323 // Should have some content 329 // Should have some content
324 assert!(!lines.is_empty(), "Metrics should have content"); 330 assert!(!lines.is_empty(), "Metrics should have content");
325 331
326 // Check for at least some standard Prometheus patterns 332 // Check for at least some standard Prometheus patterns
327 let has_help = lines.iter().any(|l| l.starts_with("# HELP")); 333 let has_help = lines.iter().any(|l| l.starts_with("# HELP"));
328 let has_type = lines.iter().any(|l| l.starts_with("# TYPE")); 334 let has_type = lines.iter().any(|l| l.starts_with("# TYPE"));
329 335
330 // At minimum we expect help/type comments for any registered metrics 336 // At minimum we expect help/type comments for any registered metrics
331 assert!( 337 assert!(
332 has_help || has_type || lines.iter().any(|l| l.contains("ngit_")), 338 has_help || has_type || lines.iter().any(|l| l.contains("ngit_")),
@@ -338,10 +344,10 @@ async fn test_prometheus_format_valid() {
338#[tokio::test] 344#[tokio::test]
339async fn test_metrics_availability_during_sync() { 345async fn test_metrics_availability_during_sync() {
340 let source_relay = TestRelay::start().await; 346 let source_relay = TestRelay::start().await;
341 let sync_relay = TestRelay::start_with_sync(source_relay.url()).await; 347 let sync_relay = TestRelay::start_with_sync(Some(source_relay.url().into())).await;
342 348
343 tokio::time::sleep(Duration::from_millis(500)).await; 349 tokio::time::sleep(Duration::from_millis(500)).await;
344 350
345 // Make multiple metrics requests while sync is active 351 // Make multiple metrics requests while sync is active
346 for i in 0..3 { 352 for i in 0..3 {
347 let metrics = fetch_metrics(&sync_relay).await; 353 let metrics = fetch_metrics(&sync_relay).await;
@@ -352,7 +358,7 @@ async fn test_metrics_availability_during_sync() {
352 ); 358 );
353 tokio::time::sleep(Duration::from_millis(200)).await; 359 tokio::time::sleep(Duration::from_millis(200)).await;
354 } 360 }
355 361
356 sync_relay.stop().await; 362 sync_relay.stop().await;
357 source_relay.stop().await; 363 source_relay.stop().await;
358} \ No newline at end of file 364}
diff --git a/tests/proactive_sync_multi.rs b/tests/proactive_sync_multi.rs
index e07ddbe..de4183a 100644
--- a/tests/proactive_sync_multi.rs
+++ b/tests/proactive_sync_multi.rs
@@ -1,7 +1,7 @@
1//! GRASP-02 Phase 2: Multi-Relay Proactive Sync Integration Tests 1//! GRASP-02 Phase 2: Multi-Relay Proactive Sync Integration Tests
2//! 2//!
3//! Tests the multi-relay proactive sync functionality. 3//! Tests the multi-relay proactive sync functionality.
4//! 4//!
5//! Note: Integration tests for sync timing are inherently flaky due to 5//! Note: Integration tests for sync timing are inherently flaky due to
6//! subprocess communication latency. Unit tests for FilterService and 6//! subprocess communication latency. Unit tests for FilterService and
7//! SyncManager cover the core logic in src/sync/filter.rs and manager.rs. 7//! SyncManager cover the core logic in src/sync/filter.rs and manager.rs.
@@ -32,7 +32,7 @@ async fn test_sync_relay_starts_with_source_url() {
32 tokio::time::sleep(Duration::from_millis(200)).await; 32 tokio::time::sleep(Duration::from_millis(200)).await;
33 33
34 // Start syncing relay (relay_sync) configured to sync from relay_a 34 // Start syncing relay (relay_sync) configured to sync from relay_a
35 let relay_sync = TestRelay::start_with_sync(relay_a.url()).await; 35 let relay_sync = TestRelay::start_with_sync(Some(relay_a.url().into())).await;
36 36
37 // Give time for connection establishment 37 // Give time for connection establishment
38 tokio::time::sleep(Duration::from_millis(500)).await; 38 tokio::time::sleep(Duration::from_millis(500)).await;
@@ -134,7 +134,7 @@ async fn test_event_submission_to_relay() {
134 // Send event - it may or may not be accepted depending on validation 134 // Send event - it may or may not be accepted depending on validation
135 // The point is the connection and submission work 135 // The point is the connection and submission work
136 let result = client.send_event(&event).await; 136 let result = client.send_event(&event).await;
137 137
138 // Clean up 138 // Clean up
139 client.disconnect().await; 139 client.disconnect().await;
140 relay.stop().await; 140 relay.stop().await;
@@ -148,11 +148,11 @@ async fn test_event_submission_to_relay() {
148fn test_domain_extraction() { 148fn test_domain_extraction() {
149 // This tests the domain() method of TestRelay indirectly 149 // This tests the domain() method of TestRelay indirectly
150 // by verifying the format matches expectations 150 // by verifying the format matches expectations
151 151
152 // Domain should be in format "127.0.0.1:PORT" 152 // Domain should be in format "127.0.0.1:PORT"
153 let example_domain = "127.0.0.1:8080"; 153 let example_domain = "127.0.0.1:8080";
154 assert!(example_domain.starts_with("127.0.0.1:")); 154 assert!(example_domain.starts_with("127.0.0.1:"));
155 155
156 // URL should be in format "ws://127.0.0.1:PORT" 156 // URL should be in format "ws://127.0.0.1:PORT"
157 let example_url = "ws://127.0.0.1:8080"; 157 let example_url = "ws://127.0.0.1:8080";
158 assert!(example_url.starts_with("ws://127.0.0.1:")); 158 assert!(example_url.starts_with("ws://127.0.0.1:"));
@@ -166,12 +166,12 @@ async fn test_sync_configuration_applied() {
166 tokio::time::sleep(Duration::from_millis(200)).await; 166 tokio::time::sleep(Duration::from_millis(200)).await;
167 167
168 // Start syncing relay with explicit sync URL 168 // Start syncing relay with explicit sync URL
169 let relay_sync = TestRelay::start_with_sync(relay_source.url()).await; 169 let relay_sync = TestRelay::start_with_sync(Some(relay_source.url().into())).await;
170 tokio::time::sleep(Duration::from_millis(300)).await; 170 tokio::time::sleep(Duration::from_millis(300)).await;
171 171
172 // Both relays should be running 172 // Both relays should be running
173 // The sync relay has NGIT_SYNC_BOOTSTRAP_RELAY_URL set (verified by relay starting) 173 // The sync relay has NGIT_SYNC_BOOTSTRAP_RELAY_URL set (verified by relay starting)
174 174
175 let client_source = Client::default(); 175 let client_source = Client::default();
176 client_source 176 client_source
177 .add_relay(relay_source.url()) 177 .add_relay(relay_source.url())
@@ -191,4 +191,4 @@ async fn test_sync_configuration_applied() {
191 client_source.disconnect().await; 191 client_source.disconnect().await;
192 relay_sync.stop().await; 192 relay_sync.stop().await;
193 relay_source.stop().await; 193 relay_source.stop().await;
194} \ No newline at end of file 194}