diff options
| -rw-r--r-- | tests/common/sync_helpers.rs | 31 | ||||
| -rw-r--r-- | tests/sync.rs | 2 | ||||
| -rw-r--r-- | tests/sync/metrics.rs (renamed from tests/proactive_sync_metrics.rs) | 72 | ||||
| -rw-r--r-- | tests/sync/mod.rs | 4 |
4 files changed, 58 insertions, 51 deletions
diff --git a/tests/common/sync_helpers.rs b/tests/common/sync_helpers.rs index 50d0d7a..7788783 100644 --- a/tests/common/sync_helpers.rs +++ b/tests/common/sync_helpers.rs | |||
| @@ -561,6 +561,37 @@ pub fn repo_coord(keys: &Keys, identifier: &str) -> String { | |||
| 561 | ) | 561 | ) |
| 562 | } | 562 | } |
| 563 | 563 | ||
| 564 | // ============================================================================ | ||
| 565 | // Metrics Helpers | ||
| 566 | // ============================================================================ | ||
| 567 | |||
| 568 | /// Fetch Prometheus metrics from a relay's `/metrics` endpoint. | ||
| 569 | /// | ||
| 570 | /// Converts the WebSocket URL to HTTP and fetches the metrics endpoint. | ||
| 571 | /// Useful for verifying sync-related metrics in tests. | ||
| 572 | /// | ||
| 573 | /// # Arguments | ||
| 574 | /// * `relay_url` - WebSocket URL of the relay (e.g., "ws://127.0.0.1:8080") | ||
| 575 | /// | ||
| 576 | /// # Returns | ||
| 577 | /// * `Ok(String)` - The metrics text in Prometheus format | ||
| 578 | /// * `Err(reqwest::Error)` - If the request fails | ||
| 579 | /// | ||
| 580 | /// # Example | ||
| 581 | /// ```ignore | ||
| 582 | /// let metrics = fetch_metrics("ws://127.0.0.1:8080").await?; | ||
| 583 | /// assert!(metrics.contains("ngit_sync_")); | ||
| 584 | /// ``` | ||
| 585 | pub async fn fetch_metrics(relay_url: &str) -> Result<String, reqwest::Error> { | ||
| 586 | // Convert ws:// URL to http:// for metrics endpoint | ||
| 587 | let http_url = relay_url | ||
| 588 | .replace("ws://", "http://") | ||
| 589 | .replace("/", "") | ||
| 590 | + "/metrics"; | ||
| 591 | |||
| 592 | reqwest::get(&http_url).await?.text().await | ||
| 593 | } | ||
| 594 | |||
| 564 | #[cfg(test)] | 595 | #[cfg(test)] |
| 565 | mod tests { | 596 | mod tests { |
| 566 | use super::*; | 597 | use super::*; |
diff --git a/tests/sync.rs b/tests/sync.rs index 2836d8d..5b6b752 100644 --- a/tests/sync.rs +++ b/tests/sync.rs | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | //! - `live_sync` - Tests for real-time sync after connection established | 8 | //! - `live_sync` - Tests for real-time sync after connection established |
| 9 | //! - `tag_variations` - Tests for different Layer 2/3 tag types | 9 | //! - `tag_variations` - Tests for different Layer 2/3 tag types |
| 10 | //! - `catchup` - Tests for catchup sync after disconnect (not yet implemented) | 10 | //! - `catchup` - Tests for catchup sync after disconnect (not yet implemented) |
| 11 | //! - `metrics` - Tests for Prometheus metrics integration | ||
| 11 | //! | 12 | //! |
| 12 | //! # Running Tests | 13 | //! # Running Tests |
| 13 | //! | 14 | //! |
| @@ -34,5 +35,6 @@ mod sync { | |||
| 34 | pub mod catchup; | 35 | pub mod catchup; |
| 35 | pub mod discovery; | 36 | pub mod discovery; |
| 36 | pub mod live_sync; | 37 | pub mod live_sync; |
| 38 | pub mod metrics; | ||
| 37 | pub mod tag_variations; | 39 | pub mod tag_variations; |
| 38 | } \ No newline at end of file | 40 | } \ No newline at end of file |
diff --git a/tests/proactive_sync_metrics.rs b/tests/sync/metrics.rs index 32abe74..dbb9dc0 100644 --- a/tests/proactive_sync_metrics.rs +++ b/tests/sync/metrics.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | //! GRASP-02 Phase 6: Proactive Sync Metrics Integration Tests | 1 | //! Proactive Sync Metrics Tests |
| 2 | //! | 2 | //! |
| 3 | //! Tests the Prometheus metrics integration for proactive sync: | 3 | //! Tests for Prometheus metrics integration with proactive sync: |
| 4 | //! - All sync metrics exposed at `/metrics` endpoint | 4 | //! - All sync metrics exposed at `/metrics` endpoint |
| 5 | //! - Connection metrics update correctly | 5 | //! - Connection metrics update correctly |
| 6 | //! - Health state metrics reflect actual state | 6 | //! - Health state metrics reflect actual state |
| @@ -10,45 +10,15 @@ | |||
| 10 | //! # Running Tests | 10 | //! # Running Tests |
| 11 | //! | 11 | //! |
| 12 | //! ```bash | 12 | //! ```bash |
| 13 | //! cargo test --test proactive_sync_metrics | 13 | //! cargo test --test sync metrics |
| 14 | //! cargo test --test proactive_sync_metrics -- --nocapture | 14 | //! cargo test --test sync metrics -- --nocapture |
| 15 | //! ``` | 15 | //! ``` |
| 16 | 16 | ||
| 17 | mod common; | ||
| 18 | |||
| 19 | use std::time::Duration; | 17 | use std::time::Duration; |
| 20 | 18 | ||
| 21 | use common::TestRelay; | ||
| 22 | use nostr_sdk::prelude::*; | 19 | use nostr_sdk::prelude::*; |
| 23 | 20 | ||
| 24 | /// Kind 30617 - Repository State (NIP-34) | 21 | use crate::common::{sync_helpers::*, TestRelay}; |
| 25 | const KIND_REPOSITORY_STATE: u16 = 30617; | ||
| 26 | |||
| 27 | /// Create a valid repository announcement event for testing | ||
| 28 | fn create_valid_repo_announcement(keys: &Keys, domain: &str, identifier: &str) -> Event { | ||
| 29 | let tags = vec![ | ||
| 30 | Tag::identifier(identifier), | ||
| 31 | Tag::custom( | ||
| 32 | TagKind::custom("clone"), | ||
| 33 | vec![format!("http://{}/{}", domain, identifier)], | ||
| 34 | ), | ||
| 35 | Tag::custom(TagKind::custom("relays"), vec![format!("ws://{}", domain)]), | ||
| 36 | ]; | ||
| 37 | |||
| 38 | EventBuilder::new(Kind::Custom(KIND_REPOSITORY_STATE), "Repository state") | ||
| 39 | .tags(tags) | ||
| 40 | .sign_with_keys(keys) | ||
| 41 | .expect("Failed to sign event") | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Helper to fetch metrics from a relay's HTTP endpoint | ||
| 45 | async fn fetch_metrics(relay: &TestRelay) -> Result<String, reqwest::Error> { | ||
| 46 | // Extract host:port from ws:// URL | ||
| 47 | let ws_url = relay.url(); | ||
| 48 | let http_url = ws_url.replace("ws://", "http://").replace("/", "") + "/metrics"; | ||
| 49 | |||
| 50 | reqwest::get(&http_url).await?.text().await | ||
| 51 | } | ||
| 52 | 22 | ||
| 53 | /// Test that sync metrics are exposed at /metrics endpoint | 23 | /// Test that sync metrics are exposed at /metrics endpoint |
| 54 | #[tokio::test] | 24 | #[tokio::test] |
| @@ -58,8 +28,8 @@ async fn test_sync_metrics_exposed() { | |||
| 58 | // Give time for relay to start | 28 | // Give time for relay to start |
| 59 | tokio::time::sleep(Duration::from_millis(500)).await; | 29 | tokio::time::sleep(Duration::from_millis(500)).await; |
| 60 | 30 | ||
| 61 | // Fetch metrics | 31 | // Fetch metrics using the shared helper |
| 62 | let metrics_result = fetch_metrics(&relay).await; | 32 | let metrics_result = fetch_metrics(&relay.url()).await; |
| 63 | 33 | ||
| 64 | relay.stop().await; | 34 | relay.stop().await; |
| 65 | 35 | ||
| @@ -84,7 +54,7 @@ async fn test_sync_metric_names_present() { | |||
| 84 | tokio::time::sleep(Duration::from_secs(2)).await; | 54 | tokio::time::sleep(Duration::from_secs(2)).await; |
| 85 | 55 | ||
| 86 | // Fetch metrics from the syncing relay | 56 | // Fetch metrics from the syncing relay |
| 87 | let metrics = fetch_metrics(&sync_relay) | 57 | let metrics = fetch_metrics(&sync_relay.url()) |
| 88 | .await | 58 | .await |
| 89 | .expect("Failed to fetch metrics"); | 59 | .expect("Failed to fetch metrics"); |
| 90 | 60 | ||
| @@ -113,7 +83,7 @@ async fn test_connection_metrics_on_success() { | |||
| 113 | tokio::time::sleep(Duration::from_secs(2)).await; | 83 | tokio::time::sleep(Duration::from_secs(2)).await; |
| 114 | 84 | ||
| 115 | // Fetch metrics - we can verify the relay started and metrics endpoint works | 85 | // Fetch metrics - we can verify the relay started and metrics endpoint works |
| 116 | let metrics = fetch_metrics(&sync_relay) | 86 | let metrics = fetch_metrics(&sync_relay.url()) |
| 117 | .await | 87 | .await |
| 118 | .expect("Failed to fetch metrics"); | 88 | .expect("Failed to fetch metrics"); |
| 119 | 89 | ||
| @@ -139,7 +109,7 @@ async fn test_event_sync_metrics() { | |||
| 139 | 109 | ||
| 140 | // Create and submit an event to source relay | 110 | // Create and submit an event to source relay |
| 141 | let keys = Keys::generate(); | 111 | let keys = Keys::generate(); |
| 142 | let event = create_valid_repo_announcement(&keys, &source_relay.domain(), "metrics-test-repo"); | 112 | let event = create_repo_announcement(&keys, &[&source_relay.domain()], "metrics-test-repo"); |
| 143 | 113 | ||
| 144 | let client = Client::default(); | 114 | let client = Client::default(); |
| 145 | client | 115 | client |
| @@ -154,7 +124,7 @@ async fn test_event_sync_metrics() { | |||
| 154 | tokio::time::sleep(Duration::from_secs(2)).await; | 124 | tokio::time::sleep(Duration::from_secs(2)).await; |
| 155 | 125 | ||
| 156 | // Fetch metrics from sync relay | 126 | // Fetch metrics from sync relay |
| 157 | let metrics = fetch_metrics(&sync_relay) | 127 | let metrics = fetch_metrics(&sync_relay.url()) |
| 158 | .await | 128 | .await |
| 159 | .expect("Failed to fetch metrics"); | 129 | .expect("Failed to fetch metrics"); |
| 160 | 130 | ||
| @@ -180,7 +150,7 @@ async fn test_health_state_metrics() { | |||
| 180 | tokio::time::sleep(Duration::from_secs(3)).await; | 150 | tokio::time::sleep(Duration::from_secs(3)).await; |
| 181 | 151 | ||
| 182 | // Fetch metrics | 152 | // Fetch metrics |
| 183 | let metrics = fetch_metrics(&sync_relay) | 153 | let metrics = fetch_metrics(&sync_relay.url()) |
| 184 | .await | 154 | .await |
| 185 | .expect("Failed to fetch metrics"); | 155 | .expect("Failed to fetch metrics"); |
| 186 | 156 | ||
| @@ -203,7 +173,7 @@ async fn test_gap_event_tracking() { | |||
| 203 | let keys = Keys::generate(); | 173 | let keys = Keys::generate(); |
| 204 | 174 | ||
| 205 | // Submit event before sync relay starts | 175 | // Submit event before sync relay starts |
| 206 | let event = create_valid_repo_announcement(&keys, &source_relay.domain(), "pre-existing-repo"); | 176 | let event = create_repo_announcement(&keys, &[&source_relay.domain()], "pre-existing-repo"); |
| 207 | 177 | ||
| 208 | let client = Client::default(); | 178 | let client = Client::default(); |
| 209 | client | 179 | client |
| @@ -220,7 +190,7 @@ async fn test_gap_event_tracking() { | |||
| 220 | tokio::time::sleep(Duration::from_secs(3)).await; | 190 | tokio::time::sleep(Duration::from_secs(3)).await; |
| 221 | 191 | ||
| 222 | // Fetch metrics | 192 | // Fetch metrics |
| 223 | let metrics = fetch_metrics(&sync_relay) | 193 | let metrics = fetch_metrics(&sync_relay.url()) |
| 224 | .await | 194 | .await |
| 225 | .expect("Failed to fetch metrics"); | 195 | .expect("Failed to fetch metrics"); |
| 226 | 196 | ||
| @@ -256,9 +226,9 @@ async fn test_multi_relay_load() { | |||
| 256 | // Submit events to all source relays | 226 | // Submit events to all source relays |
| 257 | let keys = Keys::generate(); | 227 | let keys = Keys::generate(); |
| 258 | 228 | ||
| 259 | let event1 = create_valid_repo_announcement(&keys, &source_relay_1.domain(), "repo-1"); | 229 | let event1 = create_repo_announcement(&keys, &[&source_relay_1.domain()], "repo-1"); |
| 260 | let event2 = create_valid_repo_announcement(&keys, &source_relay_2.domain(), "repo-2"); | 230 | let event2 = create_repo_announcement(&keys, &[&source_relay_2.domain()], "repo-2"); |
| 261 | let event3 = create_valid_repo_announcement(&keys, &source_relay_3.domain(), "repo-3"); | 231 | let event3 = create_repo_announcement(&keys, &[&source_relay_3.domain()], "repo-3"); |
| 262 | 232 | ||
| 263 | // Submit events | 233 | // Submit events |
| 264 | let client1 = Client::default(); | 234 | let client1 = Client::default(); |
| @@ -289,7 +259,7 @@ async fn test_multi_relay_load() { | |||
| 289 | tokio::time::sleep(Duration::from_secs(3)).await; | 259 | tokio::time::sleep(Duration::from_secs(3)).await; |
| 290 | 260 | ||
| 291 | // Fetch metrics from sync relay | 261 | // Fetch metrics from sync relay |
| 292 | let metrics = fetch_metrics(&sync_relay) | 262 | let metrics = fetch_metrics(&sync_relay.url()) |
| 293 | .await | 263 | .await |
| 294 | .expect("Failed to fetch metrics"); | 264 | .expect("Failed to fetch metrics"); |
| 295 | 265 | ||
| @@ -315,7 +285,7 @@ async fn test_prometheus_format_valid() { | |||
| 315 | let relay = TestRelay::start().await; | 285 | let relay = TestRelay::start().await; |
| 316 | tokio::time::sleep(Duration::from_millis(500)).await; | 286 | tokio::time::sleep(Duration::from_millis(500)).await; |
| 317 | 287 | ||
| 318 | let metrics = fetch_metrics(&relay) | 288 | let metrics = fetch_metrics(&relay.url()) |
| 319 | .await | 289 | .await |
| 320 | .expect("Failed to fetch metrics"); | 290 | .expect("Failed to fetch metrics"); |
| 321 | 291 | ||
| @@ -350,7 +320,7 @@ async fn test_metrics_availability_during_sync() { | |||
| 350 | 320 | ||
| 351 | // Make multiple metrics requests while sync is active | 321 | // Make multiple metrics requests while sync is active |
| 352 | for i in 0..3 { | 322 | for i in 0..3 { |
| 353 | let metrics = fetch_metrics(&sync_relay).await; | 323 | let metrics = fetch_metrics(&sync_relay.url()).await; |
| 354 | assert!( | 324 | assert!( |
| 355 | metrics.is_ok(), | 325 | metrics.is_ok(), |
| 356 | "Metrics request {} should succeed during sync", | 326 | "Metrics request {} should succeed during sync", |
| @@ -361,4 +331,4 @@ async fn test_metrics_availability_during_sync() { | |||
| 361 | 331 | ||
| 362 | sync_relay.stop().await; | 332 | sync_relay.stop().await; |
| 363 | source_relay.stop().await; | 333 | source_relay.stop().await; |
| 364 | } | 334 | } \ No newline at end of file |
diff --git a/tests/sync/mod.rs b/tests/sync/mod.rs index b0da8b8..cf8f599 100644 --- a/tests/sync/mod.rs +++ b/tests/sync/mod.rs | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | //! - Live sync (events sync in real-time after connection established) | 8 | //! - Live sync (events sync in real-time after connection established) |
| 9 | //! - Tag variations (testing different Layer 2/3 tag types: a/A/q, e/E/q) | 9 | //! - Tag variations (testing different Layer 2/3 tag types: a/A/q, e/E/q) |
| 10 | //! - Catchup sync (events from disconnected period sync on reconnect) | 10 | //! - Catchup sync (events from disconnected period sync on reconnect) |
| 11 | //! - Metrics (Prometheus metrics for sync operations) | ||
| 11 | //! | 12 | //! |
| 12 | //! # Test Files | 13 | //! # Test Files |
| 13 | //! | 14 | //! |
| @@ -16,6 +17,7 @@ | |||
| 16 | //! - `live_sync.rs` - Tests 5, 6, 7: real-time sync after connection | 17 | //! - `live_sync.rs` - Tests 5, 6, 7: real-time sync after connection |
| 17 | //! - `tag_variations.rs` - Tests 8, 9: Layer 2/3 tag type coverage | 18 | //! - `tag_variations.rs` - Tests 8, 9: Layer 2/3 tag type coverage |
| 18 | //! - `catchup.rs` - Test 0: catchup after disconnect (stub, `#[ignore]`) | 19 | //! - `catchup.rs` - Test 0: catchup after disconnect (stub, `#[ignore]`) |
| 20 | //! - `metrics.rs` - Prometheus metrics integration tests | ||
| 19 | //! | 21 | //! |
| 20 | //! # Shared Imports | 22 | //! # Shared Imports |
| 21 | //! | 23 | //! |
| @@ -23,10 +25,12 @@ | |||
| 23 | //! - `TestClient` - Client with retry logic | 25 | //! - `TestClient` - Client with retry logic |
| 24 | //! - Event builders for Layer 2/3 events | 26 | //! - Event builders for Layer 2/3 events |
| 25 | //! - `wait_for_event_on_relay()` - Non-panicking assertion helper | 27 | //! - `wait_for_event_on_relay()` - Non-panicking assertion helper |
| 28 | //! - `fetch_metrics()` - Prometheus metrics fetching | ||
| 26 | 29 | ||
| 27 | // Test modules | 30 | // Test modules |
| 28 | pub mod bootstrap; | 31 | pub mod bootstrap; |
| 29 | pub mod catchup; | 32 | pub mod catchup; |
| 30 | pub mod discovery; | 33 | pub mod discovery; |
| 31 | pub mod live_sync; | 34 | pub mod live_sync; |
| 35 | pub mod metrics; | ||
| 32 | pub mod tag_variations; \ No newline at end of file | 36 | pub mod tag_variations; \ No newline at end of file |