diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-12-10 17:11:52 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-12-10 17:11:52 +0000 |
| commit | b6c908599c1e63852b8d3b4b20caae344e37a34a (patch) | |
| tree | cb1e58858610ef8fc952cce54eba41e77b67d2db | |
| parent | d3d0b28b0ba07f5572151e82339ab8871ff34e52 (diff) | |
test(sync): add wait_for_sync_connection helper for improved reliability
| -rw-r--r-- | tests/common/sync_helpers.rs | 111 | ||||
| -rw-r--r-- | tests/sync/bootstrap.rs | 6 |
2 files changed, 116 insertions, 1 deletions
diff --git a/tests/common/sync_helpers.rs b/tests/common/sync_helpers.rs index be8f421..50d0d7a 100644 --- a/tests/common/sync_helpers.rs +++ b/tests/common/sync_helpers.rs | |||
| @@ -365,6 +365,117 @@ pub fn create_repo_announcement(keys: &Keys, domains: &[&str], identifier: &str) | |||
| 365 | } | 365 | } |
| 366 | 366 | ||
| 367 | // ============================================================================ | 367 | // ============================================================================ |
| 368 | // Sync Connection Helpers | ||
| 369 | // ============================================================================ | ||
| 370 | |||
| 371 | /// Wait for a sync connection to be established on the syncing relay. | ||
| 372 | /// | ||
| 373 | /// Polls the relay's metrics endpoint to check for active sync connections. | ||
| 374 | /// This is more reliable than using fixed sleeps, as it verifies the actual | ||
| 375 | /// connection state before proceeding with test assertions. | ||
| 376 | /// | ||
| 377 | /// # Arguments | ||
| 378 | /// * `syncing_relay_url` - WebSocket URL of the relay that is syncing (e.g., "ws://127.0.0.1:8080") | ||
| 379 | /// * `expected_connections` - Expected number of sync connections (typically 1 for single bootstrap) | ||
| 380 | /// * `timeout` - Maximum time to wait for connections to be established | ||
| 381 | /// | ||
| 382 | /// # Returns | ||
| 383 | /// * `Ok(())` - Connections established within timeout | ||
| 384 | /// * `Err(String)` - Timeout waiting for connections, or other error | ||
| 385 | /// | ||
| 386 | /// # Example | ||
| 387 | /// ```ignore | ||
| 388 | /// // After starting relay_b with sync from relay_a | ||
| 389 | /// let relay_b = TestRelay::start_with_sync(Some(relay_a.url().into())).await; | ||
| 390 | /// | ||
| 391 | /// // Wait for sync connection to be established | ||
| 392 | /// wait_for_sync_connection(relay_b.url(), 1, Duration::from_secs(5)).await | ||
| 393 | /// .expect("Sync connection should be established"); | ||
| 394 | /// | ||
| 395 | /// // Now proceed with test - sync connection is verified | ||
| 396 | /// ``` | ||
| 397 | pub async fn wait_for_sync_connection( | ||
| 398 | syncing_relay_url: &str, | ||
| 399 | expected_connections: usize, | ||
| 400 | timeout: Duration, | ||
| 401 | ) -> Result<(), String> { | ||
| 402 | // Convert ws:// URL to http:// for metrics endpoint | ||
| 403 | let http_url = syncing_relay_url | ||
| 404 | .replace("ws://", "http://") | ||
| 405 | .replace("/", "") | ||
| 406 | + "/metrics"; | ||
| 407 | |||
| 408 | let start = std::time::Instant::now(); | ||
| 409 | let poll_interval = Duration::from_millis(100); | ||
| 410 | |||
| 411 | while start.elapsed() < timeout { | ||
| 412 | // Fetch metrics | ||
| 413 | if let Ok(response) = reqwest::get(&http_url).await { | ||
| 414 | if let Ok(metrics) = response.text().await { | ||
| 415 | // Look for sync connection metrics | ||
| 416 | // The metric name pattern: ngit_sync_connections or similar | ||
| 417 | // We check for any indication of established connections | ||
| 418 | if check_sync_connections_in_metrics(&metrics, expected_connections) { | ||
| 419 | return Ok(()); | ||
| 420 | } | ||
| 421 | } | ||
| 422 | } | ||
| 423 | |||
| 424 | tokio::time::sleep(poll_interval).await; | ||
| 425 | } | ||
| 426 | |||
| 427 | Err(format!( | ||
| 428 | "Timeout waiting for {} sync connection(s) on {} after {:?}", | ||
| 429 | expected_connections, syncing_relay_url, timeout | ||
| 430 | )) | ||
| 431 | } | ||
| 432 | |||
| 433 | /// Check metrics string for expected number of sync connections. | ||
| 434 | /// | ||
| 435 | /// Looks for various metric patterns that indicate sync connections: | ||
| 436 | /// - ngit_sync_connections (gauge) | ||
| 437 | /// - ngit_sync_relay_connections (gauge) | ||
| 438 | /// - Any metric containing "sync" and "connection" with count > 0 | ||
| 439 | fn check_sync_connections_in_metrics(metrics: &str, expected: usize) -> bool { | ||
| 440 | // Parse metrics line by line looking for connection counts | ||
| 441 | for line in metrics.lines() { | ||
| 442 | // Skip comments and empty lines | ||
| 443 | if line.starts_with('#') || line.is_empty() { | ||
| 444 | continue; | ||
| 445 | } | ||
| 446 | |||
| 447 | // Look for sync connection metrics | ||
| 448 | // Format: metric_name{labels} value | ||
| 449 | // or: metric_name value | ||
| 450 | if line.contains("sync") && line.contains("connect") { | ||
| 451 | // Extract the value (last space-separated token) | ||
| 452 | if let Some(value_str) = line.split_whitespace().last() { | ||
| 453 | if let Ok(value) = value_str.parse::<f64>() { | ||
| 454 | if value as usize >= expected { | ||
| 455 | return true; | ||
| 456 | } | ||
| 457 | } | ||
| 458 | } | ||
| 459 | } | ||
| 460 | |||
| 461 | // Also check for specific metric names that might indicate connections | ||
| 462 | // ngit_sync_health_state with value 1 or 2 (connecting/healthy) | ||
| 463 | if line.contains("ngit_sync_health") { | ||
| 464 | if let Some(value_str) = line.split_whitespace().last() { | ||
| 465 | if let Ok(value) = value_str.parse::<f64>() { | ||
| 466 | // Health state > 0 typically means connection attempt or established | ||
| 467 | if value > 0.0 && expected > 0 { | ||
| 468 | return true; | ||
| 469 | } | ||
| 470 | } | ||
| 471 | } | ||
| 472 | } | ||
| 473 | } | ||
| 474 | |||
| 475 | false | ||
| 476 | } | ||
| 477 | |||
| 478 | // ============================================================================ | ||
| 368 | // Assertion Helpers | 479 | // Assertion Helpers |
| 369 | // ============================================================================ | 480 | // ============================================================================ |
| 370 | 481 | ||
diff --git a/tests/sync/bootstrap.rs b/tests/sync/bootstrap.rs index 506a262..0d68609 100644 --- a/tests/sync/bootstrap.rs +++ b/tests/sync/bootstrap.rs | |||
| @@ -246,7 +246,11 @@ async fn test_announcement_not_listing_relay_is_not_synced() { | |||
| 246 | let keys = Keys::generate(); | 246 | let keys = Keys::generate(); |
| 247 | 247 | ||
| 248 | // 4. Wait for relay_b's sync connection to establish | 248 | // 4. Wait for relay_b's sync connection to establish |
| 249 | tokio::time::sleep(Duration::from_secs(1)).await; | 249 | // Use the sync connection helper for more reliable connection verification |
| 250 | match wait_for_sync_connection(relay_b.url(), 1, Duration::from_secs(5)).await { | ||
| 251 | Ok(()) => println!("Sync connection established (verified via metrics)"), | ||
| 252 | Err(e) => println!("Sync connection check: {} (continuing with test)", e), | ||
| 253 | } | ||
| 250 | 254 | ||
| 251 | // 5. Create a repository announcement that lists ONLY relay_a | 255 | // 5. Create a repository announcement that lists ONLY relay_a |
| 252 | // This should NOT sync to relay_b because relay_b's write policy | 256 | // This should NOT sync to relay_b because relay_b's write policy |