upleb.uk

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

summaryrefslogtreecommitdiff
path: root/tests/sync
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-12-11 08:47:08 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-12-11 08:47:08 +0000
commit61d4796d84960ec9f25392635afceea3a3bd0916 (patch)
tree98bd7c10d34decc2f7c96f50ddead8e8dc63f473 /tests/sync
parentffcf8a7bc679f0aff9135063d343be3161b3b439 (diff)
refactor: move metrics tests to tests/sync/ structure (Phase 7)
Diffstat (limited to 'tests/sync')
-rw-r--r--tests/sync/metrics.rs334
-rw-r--r--tests/sync/mod.rs4
2 files changed, 338 insertions, 0 deletions
diff --git a/tests/sync/metrics.rs b/tests/sync/metrics.rs
new file mode 100644
index 0000000..dbb9dc0
--- /dev/null
+++ b/tests/sync/metrics.rs
@@ -0,0 +1,334 @@
1//! Proactive Sync Metrics Tests
2//!
3//! Tests for Prometheus metrics integration with proactive sync:
4//! - All sync metrics exposed at `/metrics` endpoint
5//! - Connection metrics update correctly
6//! - Health state metrics reflect actual state
7//! - Gap events tracked correctly
8//! - Load test with 3+ relays
9//!
10//! # Running Tests
11//!
12//! ```bash
13//! cargo test --test sync metrics
14//! cargo test --test sync metrics -- --nocapture
15//! ```
16
17use std::time::Duration;
18
19use nostr_sdk::prelude::*;
20
21use crate::common::{sync_helpers::*, TestRelay};
22
23/// Test that sync metrics are exposed at /metrics endpoint
24#[tokio::test]
25async fn test_sync_metrics_exposed() {
26 let relay = TestRelay::start().await;
27
28 // Give time for relay to start
29 tokio::time::sleep(Duration::from_millis(500)).await;
30
31 // Fetch metrics using the shared helper
32 let metrics_result = fetch_metrics(&relay.url()).await;
33
34 relay.stop().await;
35
36 // Check that we got metrics (even if sync isn't configured)
37 let metrics = metrics_result.expect("Failed to fetch metrics");
38
39 // Verify basic metrics structure exists
40 assert!(
41 metrics.contains("ngit_") || metrics.contains("# HELP"),
42 "Metrics endpoint should return Prometheus metrics"
43 );
44}
45
46/// Test that sync metrics include expected metric names
47#[tokio::test]
48async fn test_sync_metric_names_present() {
49 // Start a relay with sync configured
50 let source_relay = TestRelay::start().await;
51 let sync_relay = TestRelay::start_with_sync(Some(source_relay.url().into())).await;
52
53 // Give time for sync connection to attempt
54 tokio::time::sleep(Duration::from_secs(2)).await;
55
56 // Fetch metrics from the syncing relay
57 let metrics = fetch_metrics(&sync_relay.url())
58 .await
59 .expect("Failed to fetch metrics");
60
61 sync_relay.stop().await;
62 source_relay.stop().await;
63
64 // Check for expected sync metric names (they may have zero values)
65 // At minimum, the ngit_ prefix metrics should be present
66 assert!(
67 metrics.contains("ngit_"),
68 "Metrics should include ngit_ prefixed metrics"
69 );
70}
71
72/// Test connection metrics update correctly on successful connection
73#[tokio::test]
74async fn test_connection_metrics_on_success() {
75 // Start source relay
76 let source_relay = TestRelay::start().await;
77 tokio::time::sleep(Duration::from_millis(200)).await;
78
79 // Start syncing relay
80 let sync_relay = TestRelay::start_with_sync(Some(source_relay.url().into())).await;
81
82 // Wait for connection to establish
83 tokio::time::sleep(Duration::from_secs(2)).await;
84
85 // Fetch metrics - we can verify the relay started and metrics endpoint works
86 let metrics = fetch_metrics(&sync_relay.url())
87 .await
88 .expect("Failed to fetch metrics");
89
90 sync_relay.stop().await;
91 source_relay.stop().await;
92
93 // Verify metrics endpoint returned data
94 assert!(!metrics.is_empty(), "Metrics endpoint should return data");
95}
96
97/// Test that events syncing updates metrics
98#[tokio::test]
99async fn test_event_sync_metrics() {
100 // Start source relay
101 let source_relay = TestRelay::start().await;
102 tokio::time::sleep(Duration::from_millis(200)).await;
103
104 // Start syncing relay
105 let sync_relay = TestRelay::start_with_sync(Some(source_relay.url().into())).await;
106
107 // Wait for connection
108 tokio::time::sleep(Duration::from_secs(1)).await;
109
110 // Create and submit an event to source relay
111 let keys = Keys::generate();
112 let event = create_repo_announcement(&keys, &[&source_relay.domain()], "metrics-test-repo");
113
114 let client = Client::default();
115 client
116 .add_relay(source_relay.url())
117 .await
118 .expect("Failed to add relay");
119 client.connect().await;
120
121 let _ = client.send_event(&event).await;
122
123 // Wait for sync to occur
124 tokio::time::sleep(Duration::from_secs(2)).await;
125
126 // Fetch metrics from sync relay
127 let metrics = fetch_metrics(&sync_relay.url())
128 .await
129 .expect("Failed to fetch metrics");
130
131 client.disconnect().await;
132 sync_relay.stop().await;
133 source_relay.stop().await;
134
135 // Verify metrics endpoint returned data after sync activity
136 assert!(
137 !metrics.is_empty(),
138 "Metrics should be present after sync activity"
139 );
140}
141
142/// Test health state tracking in metrics
143#[tokio::test]
144async fn test_health_state_metrics() {
145 // Start a syncing relay pointing to a non-existent source
146 // This will result in connection failures and health state changes
147 let sync_relay = TestRelay::start_with_sync(Some("ws://127.0.0.1:19999".into())).await;
148
149 // Wait for some connection attempts
150 tokio::time::sleep(Duration::from_secs(3)).await;
151
152 // Fetch metrics
153 let metrics = fetch_metrics(&sync_relay.url())
154 .await
155 .expect("Failed to fetch metrics");
156
157 sync_relay.stop().await;
158
159 // The relay should still be operational even with failed sync
160 assert!(
161 !metrics.is_empty(),
162 "Metrics should be present even with sync failures"
163 );
164}
165
166/// Test gap event tracking (events received during catchup)
167#[tokio::test]
168async fn test_gap_event_tracking() {
169 // Start source relay and add some events first
170 let source_relay = TestRelay::start().await;
171 tokio::time::sleep(Duration::from_millis(200)).await;
172
173 let keys = Keys::generate();
174
175 // Submit event before sync relay starts
176 let event = create_repo_announcement(&keys, &[&source_relay.domain()], "pre-existing-repo");
177
178 let client = Client::default();
179 client
180 .add_relay(source_relay.url())
181 .await
182 .expect("Failed to add relay");
183 client.connect().await;
184 let _ = client.send_event(&event).await;
185
186 // Now start syncing relay - it should catch up on existing events
187 let sync_relay = TestRelay::start_with_sync(Some(source_relay.url().into())).await;
188
189 // Wait for catchup
190 tokio::time::sleep(Duration::from_secs(3)).await;
191
192 // Fetch metrics
193 let metrics = fetch_metrics(&sync_relay.url())
194 .await
195 .expect("Failed to fetch metrics");
196
197 client.disconnect().await;
198 sync_relay.stop().await;
199 source_relay.stop().await;
200
201 // Verify metrics exist after gap sync scenario
202 assert!(
203 !metrics.is_empty(),
204 "Metrics should track gap sync activity"
205 );
206}
207
208/// Load test with 3+ relays configured for sync
209#[tokio::test]
210async fn test_multi_relay_load() {
211 // Start 3 source relays
212 let source_relay_1 = TestRelay::start().await;
213 let source_relay_2 = TestRelay::start().await;
214 let source_relay_3 = TestRelay::start().await;
215
216 tokio::time::sleep(Duration::from_millis(500)).await;
217
218 // Start a syncing relay pointing to first source
219 // Note: The current implementation only supports single sync relay URL
220 // but the test demonstrates the system handles multiple relay scenarios
221 let sync_relay = TestRelay::start_with_sync(Some(source_relay_1.url().into())).await;
222
223 // Wait for connections
224 tokio::time::sleep(Duration::from_secs(2)).await;
225
226 // Submit events to all source relays
227 let keys = Keys::generate();
228
229 let event1 = create_repo_announcement(&keys, &[&source_relay_1.domain()], "repo-1");
230 let event2 = create_repo_announcement(&keys, &[&source_relay_2.domain()], "repo-2");
231 let event3 = create_repo_announcement(&keys, &[&source_relay_3.domain()], "repo-3");
232
233 // Submit events
234 let client1 = Client::default();
235 client1
236 .add_relay(source_relay_1.url())
237 .await
238 .expect("Failed to add relay");
239 client1.connect().await;
240 let _ = client1.send_event(&event1).await;
241
242 let client2 = Client::default();
243 client2
244 .add_relay(source_relay_2.url())
245 .await
246 .expect("Failed to add relay");
247 client2.connect().await;
248 let _ = client2.send_event(&event2).await;
249
250 let client3 = Client::default();
251 client3
252 .add_relay(source_relay_3.url())
253 .await
254 .expect("Failed to add relay");
255 client3.connect().await;
256 let _ = client3.send_event(&event3).await;
257
258 // Wait for sync
259 tokio::time::sleep(Duration::from_secs(3)).await;
260
261 // Fetch metrics from sync relay
262 let metrics = fetch_metrics(&sync_relay.url())
263 .await
264 .expect("Failed to fetch metrics");
265
266 // Cleanup
267 client1.disconnect().await;
268 client2.disconnect().await;
269 client3.disconnect().await;
270 sync_relay.stop().await;
271 source_relay_1.stop().await;
272 source_relay_2.stop().await;
273 source_relay_3.stop().await;
274
275 // Verify metrics system handled load
276 assert!(
277 !metrics.is_empty(),
278 "Metrics should be available under multi-relay load"
279 );
280}
281
282/// Test that Prometheus text format is valid
283#[tokio::test]
284async fn test_prometheus_format_valid() {
285 let relay = TestRelay::start().await;
286 tokio::time::sleep(Duration::from_millis(500)).await;
287
288 let metrics = fetch_metrics(&relay.url())
289 .await
290 .expect("Failed to fetch metrics");
291
292 relay.stop().await;
293
294 // Check for valid Prometheus format markers
295 // - Lines starting with # are comments (HELP, TYPE)
296 // - Metric lines have format: metric_name{labels} value
297 let lines: Vec<&str> = metrics.lines().collect();
298
299 // Should have some content
300 assert!(!lines.is_empty(), "Metrics should have content");
301
302 // Check for at least some standard Prometheus patterns
303 let has_help = lines.iter().any(|l| l.starts_with("# HELP"));
304 let has_type = lines.iter().any(|l| l.starts_with("# TYPE"));
305
306 // At minimum we expect help/type comments for any registered metrics
307 assert!(
308 has_help || has_type || lines.iter().any(|l| l.contains("ngit_")),
309 "Metrics should contain Prometheus format elements"
310 );
311}
312
313/// Test metrics endpoint availability during sync operations
314#[tokio::test]
315async fn test_metrics_availability_during_sync() {
316 let source_relay = TestRelay::start().await;
317 let sync_relay = TestRelay::start_with_sync(Some(source_relay.url().into())).await;
318
319 tokio::time::sleep(Duration::from_millis(500)).await;
320
321 // Make multiple metrics requests while sync is active
322 for i in 0..3 {
323 let metrics = fetch_metrics(&sync_relay.url()).await;
324 assert!(
325 metrics.is_ok(),
326 "Metrics request {} should succeed during sync",
327 i + 1
328 );
329 tokio::time::sleep(Duration::from_millis(200)).await;
330 }
331
332 sync_relay.stop().await;
333 source_relay.stop().await;
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
28pub mod bootstrap; 31pub mod bootstrap;
29pub mod catchup; 32pub mod catchup;
30pub mod discovery; 33pub mod discovery;
31pub mod live_sync; 34pub mod live_sync;
35pub mod metrics;
32pub mod tag_variations; \ No newline at end of file 36pub mod tag_variations; \ No newline at end of file