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:50:49 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-12-11 08:50:49 +0000
commit1bb259202fc67b4af96f470fe3769895356725c2 (patch)
tree0aba78c33026c9bcb020ee53eaf587a82a78bf09 /tests/sync
parent61d4796d84960ec9f25392635afceea3a3bd0916 (diff)
test: add additional sync metrics tests for better coverage (Phase 8)
Diffstat (limited to 'tests/sync')
-rw-r--r--tests/sync/metrics.rs331
1 files changed, 331 insertions, 0 deletions
diff --git a/tests/sync/metrics.rs b/tests/sync/metrics.rs
index dbb9dc0..8c801ba 100644
--- a/tests/sync/metrics.rs
+++ b/tests/sync/metrics.rs
@@ -331,4 +331,335 @@ async fn test_metrics_availability_during_sync() {
331 331
332 sync_relay.stop().await; 332 sync_relay.stop().await;
333 source_relay.stop().await; 333 source_relay.stop().await;
334}
335
336// ============================================================================
337// Additional Coverage Tests (Phase 8)
338// ============================================================================
339
340/// Test metrics when connection to sync source fails
341///
342/// Verifies that:
343/// - Metrics endpoint remains functional when sync connection fails
344/// - Connection attempt metrics are recorded even for failures
345/// - The relay continues to operate despite sync failures
346#[tokio::test]
347async fn test_connection_failure_metrics() {
348 // Start a syncing relay pointing to a non-existent relay
349 // Port 19998 should not have anything running
350 let sync_relay = TestRelay::start_with_sync(Some("ws://127.0.0.1:19998".into())).await;
351
352 // Wait for connection attempts to fail
353 tokio::time::sleep(Duration::from_secs(3)).await;
354
355 // Fetch metrics - should still work despite sync failures
356 let metrics = fetch_metrics(&sync_relay.url())
357 .await
358 .expect("Metrics endpoint should remain functional");
359
360 sync_relay.stop().await;
361
362 // Verify connection attempt metrics are present (even with zeroes)
363 // The metrics endpoint should contain ngit_sync prefixed metrics
364 assert!(
365 metrics.contains("ngit_sync"),
366 "Sync metrics should be exposed even during connection failures"
367 );
368
369 // Check for connection-related metric patterns
370 let has_connection_metrics = metrics.contains("connection") || metrics.contains("relay");
371 assert!(
372 has_connection_metrics || metrics.contains("ngit_"),
373 "Should have some form of connection/relay metrics"
374 );
375}
376
377/// Test that failure counters increment on repeated connection failures
378///
379/// Verifies that the relay tracks consecutive failures and exposes
380/// them via metrics (ngit_sync_relay_failures metric).
381#[tokio::test]
382async fn test_failure_counter_increments() {
383 // Use a very high port that definitely won't be listening
384 let sync_relay = TestRelay::start_with_sync(Some("ws://127.0.0.1:59999".into())).await;
385
386 // First check - initial state
387 tokio::time::sleep(Duration::from_secs(1)).await;
388 let metrics_initial = fetch_metrics(&sync_relay.url())
389 .await
390 .expect("Should fetch initial metrics");
391
392 // Wait for more connection attempts
393 tokio::time::sleep(Duration::from_secs(3)).await;
394
395 // Second check - after more failures
396 let metrics_after = fetch_metrics(&sync_relay.url())
397 .await
398 .expect("Should fetch metrics after failures");
399
400 sync_relay.stop().await;
401
402 // Metrics should be present at both times
403 assert!(!metrics_initial.is_empty(), "Initial metrics should exist");
404 assert!(!metrics_after.is_empty(), "Later metrics should exist");
405
406 // Both should contain sync-related metrics
407 assert!(
408 metrics_after.contains("ngit_"),
409 "Should contain ngit_ prefixed metrics after failures"
410 );
411}
412
413/// Test that relay counts are properly tracked in metrics
414///
415/// Verifies:
416/// - ngit_sync_relays_tracked_total reflects discovered relays
417/// - ngit_sync_relays_connected_total updates with connection state
418/// - Count metrics use proper gauges (can go up and down)
419#[tokio::test]
420async fn test_relay_count_metrics() {
421 // Start source relay first
422 let source_relay = TestRelay::start().await;
423 tokio::time::sleep(Duration::from_millis(200)).await;
424
425 // Start syncing relay pointing to actual source
426 let sync_relay = TestRelay::start_with_sync(Some(source_relay.url().into())).await;
427
428 // Wait for connection to establish
429 tokio::time::sleep(Duration::from_secs(2)).await;
430
431 let metrics_connected = fetch_metrics(&sync_relay.url())
432 .await
433 .expect("Should fetch metrics when connected");
434
435 // Stop the source relay to trigger disconnection
436 source_relay.stop().await;
437
438 // Wait for disconnect detection
439 tokio::time::sleep(Duration::from_secs(2)).await;
440
441 let metrics_disconnected = fetch_metrics(&sync_relay.url())
442 .await
443 .expect("Should fetch metrics after source disconnection");
444
445 sync_relay.stop().await;
446
447 // Metrics should exist in both states
448 assert!(
449 !metrics_connected.is_empty(),
450 "Connected state metrics should exist"
451 );
452 assert!(
453 !metrics_disconnected.is_empty(),
454 "Disconnected state metrics should exist"
455 );
456}
457
458/// Test event source label differentiation in metrics
459///
460/// Verifies that the ngit_sync_events_total metric properly
461/// distinguishes between event sources via labels:
462/// - source="live" for real-time subscription events
463/// - source="startup" for initial catchup events
464/// - source="reconnect" for reconnection catchup events
465/// - source="daily" for daily drift detection events
466#[tokio::test]
467async fn test_event_source_labels_in_metrics() {
468 // Set up source with pre-existing events (will trigger startup catchup)
469 let source_relay = TestRelay::start().await;
470 tokio::time::sleep(Duration::from_millis(200)).await;
471
472 // Create and submit an event before sync relay starts
473 let keys = Keys::generate();
474 let pre_event = create_repo_announcement(&keys, &[&source_relay.domain()], "pre-startup-repo");
475
476 let client = Client::default();
477 client
478 .add_relay(source_relay.url())
479 .await
480 .expect("Failed to add relay");
481 client.connect().await;
482 let _ = client.send_event(&pre_event).await;
483
484 // Now start syncing relay - this triggers startup catchup
485 let sync_relay = TestRelay::start_with_sync(Some(source_relay.url().into())).await;
486
487 // Wait for startup sync
488 tokio::time::sleep(Duration::from_secs(2)).await;
489
490 // Submit another event - this will be received via live sync
491 let live_event = create_repo_announcement(&keys, &[&source_relay.domain()], "live-sync-repo");
492 let _ = client.send_event(&live_event).await;
493
494 // Wait for live sync
495 tokio::time::sleep(Duration::from_secs(2)).await;
496
497 let metrics = fetch_metrics(&sync_relay.url())
498 .await
499 .expect("Should fetch metrics");
500
501 client.disconnect().await;
502 sync_relay.stop().await;
503 source_relay.stop().await;
504
505 // Verify metric line exists for events_total
506 // It should have labels distinguishing sources
507 let has_events_metric = metrics.contains("ngit_sync_events_total")
508 || metrics.contains("events")
509 || metrics.contains("ngit_sync");
510
511 assert!(
512 has_events_metric,
513 "Should have event-related sync metrics"
514 );
515}
516
517/// Test concurrent metrics requests don't cause issues
518///
519/// Verifies that the metrics endpoint is thread-safe and can
520/// handle multiple simultaneous requests during active sync.
521#[tokio::test]
522async fn test_concurrent_metrics_requests() {
523 let source_relay = TestRelay::start().await;
524 let sync_relay = TestRelay::start_with_sync(Some(source_relay.url().into())).await;
525
526 tokio::time::sleep(Duration::from_secs(1)).await;
527
528 // Clone the URL string so we have an owned value for spawned tasks
529 let sync_url: String = sync_relay.url().to_string();
530
531 // Spawn multiple concurrent metrics requests
532 let handles: Vec<_> = (0..5)
533 .map(|i| {
534 let url = sync_url.clone();
535 tokio::spawn(async move {
536 let result = fetch_metrics(&url).await;
537 (i, result.is_ok())
538 })
539 })
540 .collect();
541
542 // Wait for all requests and collect results
543 let mut successes = 0;
544 for handle in handles {
545 let (idx, success) = handle.await.expect("Task should not panic");
546 if success {
547 successes += 1;
548 } else {
549 eprintln!("Concurrent request {} failed", idx);
550 }
551 }
552
553 sync_relay.stop().await;
554 source_relay.stop().await;
555
556 // All concurrent requests should succeed
557 assert_eq!(
558 successes, 5,
559 "All 5 concurrent metrics requests should succeed"
560 );
561}
562
563/// Test that metric values are properly formatted numbers
564///
565/// Verifies that Prometheus metric values are valid numeric formats,
566/// which is essential for proper scraping and alerting.
567#[tokio::test]
568async fn test_metric_values_are_numeric() {
569 let relay = TestRelay::start().await;
570 tokio::time::sleep(Duration::from_millis(500)).await;
571
572 let metrics = fetch_metrics(&relay.url())
573 .await
574 .expect("Should fetch metrics");
575
576 relay.stop().await;
577
578 // Parse each line and verify metric values are numeric
579 let mut metric_count = 0;
580 let mut all_valid = true;
581
582 for line in metrics.lines() {
583 // Skip comments and empty lines
584 if line.starts_with('#') || line.is_empty() {
585 continue;
586 }
587
588 // Metric lines have format: metric_name{labels} value
589 // or: metric_name value
590 if let Some(value_str) = line.split_whitespace().last() {
591 // Try to parse as f64 (Prometheus uses float format)
592 if value_str.parse::<f64>().is_err() {
593 eprintln!("Invalid metric value in line: {}", line);
594 all_valid = false;
595 } else {
596 metric_count += 1;
597 }
598 }
599 }
600
601 assert!(all_valid, "All metric values should be valid numbers");
602 assert!(
603 metric_count > 0,
604 "Should have at least one metric with a value"
605 );
606}
607
608/// Test gap events are tracked distinctly from other sync events
609///
610/// Gap events are historical events discovered during catchup that weren't
611/// received during live sync. This test verifies they are tracked separately
612/// in the ngit_sync_gap_events_total metric.
613#[tokio::test]
614async fn test_gap_events_tracked_separately() {
615 // Create source relay with initial content
616 let source_relay = TestRelay::start().await;
617 tokio::time::sleep(Duration::from_millis(200)).await;
618
619 let keys = Keys::generate();
620
621 // Create multiple events on source before sync relay starts
622 let client = Client::default();
623 client
624 .add_relay(source_relay.url())
625 .await
626 .expect("Failed to add relay");
627 client.connect().await;
628
629 // Submit several events to create a "gap"
630 for i in 0..3 {
631 let event = create_repo_announcement(
632 &keys,
633 &[&source_relay.domain()],
634 &format!("gap-repo-{}", i),
635 );
636 let _ = client.send_event(&event).await;
637 }
638
639 // Wait for events to be stored
640 tokio::time::sleep(Duration::from_millis(500)).await;
641
642 // Now start sync relay - it will catchup on the gap events
643 let sync_relay = TestRelay::start_with_sync(Some(source_relay.url().into())).await;
644
645 // Wait for catchup to complete
646 tokio::time::sleep(Duration::from_secs(3)).await;
647
648 let metrics = fetch_metrics(&sync_relay.url())
649 .await
650 .expect("Should fetch metrics");
651
652 client.disconnect().await;
653 sync_relay.stop().await;
654 source_relay.stop().await;
655
656 // Check for gap-related metrics or general sync metrics
657 let has_sync_metrics = metrics.contains("ngit_sync")
658 || metrics.contains("gap")
659 || metrics.contains("events");
660
661 assert!(
662 has_sync_metrics,
663 "Metrics should track sync activity including gap events"
664 );
334} \ No newline at end of file 665} \ No newline at end of file