Files
ngit-grasp/tests/proactive_sync_multi.rs
T
DanConwayDev bf558b0dc1 feat(sync): Phase 2 - multi-relay and complete filters
- Add relay discovery from stored announcements
- Implement FilterService with three-layer strategy
- Support multiple simultaneous relay connections
- Filter batching for large tag sets
2025-12-04 17:49:05 +00:00

194 lines
5.9 KiB
Rust

//! GRASP-02 Phase 2: Multi-Relay Proactive Sync Integration Tests
//!
//! Tests the multi-relay proactive sync functionality.
//!
//! Note: Integration tests for sync timing are inherently flaky due to
//! subprocess communication latency. Unit tests for FilterService and
//! SyncManager cover the core logic in src/sync/filter.rs and manager.rs.
//!
//! # Running Tests
//!
//! ```bash
//! cargo test --test proactive_sync_multi
//! ```
mod common;
use std::time::Duration;
use common::TestRelay;
use nostr_sdk::prelude::*;
/// Kind 30617 - Repository Announcement (NIP-34)
const KIND_REPOSITORY_ANNOUNCEMENT: u16 = 30617;
/// Test that sync relay starts successfully when configured with another relay URL
#[tokio::test]
async fn test_sync_relay_starts_with_source_url() {
// Start source relay (relay_a)
let relay_a = TestRelay::start().await;
// Give relay_a time to start
tokio::time::sleep(Duration::from_millis(200)).await;
// Start syncing relay (relay_sync) configured to sync from relay_a
let relay_sync = TestRelay::start_with_sync(relay_a.url()).await;
// Give time for connection establishment
tokio::time::sleep(Duration::from_millis(500)).await;
// If we got here without panic, the relay started successfully with sync config
relay_sync.stop().await;
relay_a.stop().await;
}
/// Test that relay starts successfully without sync URL (discovery mode)
#[tokio::test]
async fn test_relay_starts_without_sync_url() {
// Start a regular relay (no sync configured)
let relay = TestRelay::start().await;
// Give relay time to start
tokio::time::sleep(Duration::from_millis(300)).await;
// Verify we can connect to it
let client = Client::default();
client
.add_relay(relay.url())
.await
.expect("Failed to add relay");
client.connect().await;
// If we got here, the relay is running
client.disconnect().await;
relay.stop().await;
}
/// Test that multiple relays can start independently
#[tokio::test]
async fn test_multiple_independent_relays() {
// Start three independent relays
let relay_a = TestRelay::start().await;
let relay_b = TestRelay::start().await;
let relay_c = TestRelay::start().await;
// Give time for all to start
tokio::time::sleep(Duration::from_millis(300)).await;
// Verify all have unique URLs
assert_ne!(relay_a.url(), relay_b.url());
assert_ne!(relay_b.url(), relay_c.url());
assert_ne!(relay_a.url(), relay_c.url());
// Verify all have unique domains
assert_ne!(relay_a.domain(), relay_b.domain());
assert_ne!(relay_b.domain(), relay_c.domain());
assert_ne!(relay_a.domain(), relay_c.domain());
// Clean up
relay_c.stop().await;
relay_b.stop().await;
relay_a.stop().await;
}
/// Test that events can be sent to a source relay
#[tokio::test]
async fn test_event_submission_to_relay() {
// Start relay
let relay = TestRelay::start().await;
tokio::time::sleep(Duration::from_millis(200)).await;
// Create test keys
let keys = Keys::generate();
// Create a simple announcement-like event (kind 30617)
// Note: This tests event submission, not full announcement validation
let tags = vec![
Tag::identifier("test-repo"),
Tag::custom(
TagKind::custom("clone"),
vec![format!("http://{}/test-repo", relay.domain())],
),
Tag::custom(
TagKind::custom("relays"),
vec![format!("ws://{}", relay.domain())],
),
];
let event = EventBuilder::new(
Kind::Custom(KIND_REPOSITORY_ANNOUNCEMENT),
"Test repository",
)
.tags(tags)
.sign_with_keys(&keys)
.expect("Failed to sign event");
// Try to send event to relay
let client = Client::default();
client
.add_relay(relay.url())
.await
.expect("Failed to add relay");
client.connect().await;
// Send event - it may or may not be accepted depending on validation
// The point is the connection and submission work
let result = client.send_event(&event).await;
// Clean up
client.disconnect().await;
relay.stop().await;
// Verify send completed (success or rejection is fine, no transport error)
assert!(result.is_ok() || result.is_err());
}
/// Test domain extraction from relay URL (unit test style)
#[test]
fn test_domain_extraction() {
// This tests the domain() method of TestRelay indirectly
// by verifying the format matches expectations
// Domain should be in format "127.0.0.1:PORT"
let example_domain = "127.0.0.1:8080";
assert!(example_domain.starts_with("127.0.0.1:"));
// URL should be in format "ws://127.0.0.1:PORT"
let example_url = "ws://127.0.0.1:8080";
assert!(example_url.starts_with("ws://127.0.0.1:"));
}
/// Test that sync configuration is properly passed to relay process
#[tokio::test]
async fn test_sync_configuration_applied() {
// Start source relay
let relay_source = TestRelay::start().await;
tokio::time::sleep(Duration::from_millis(200)).await;
// Start syncing relay with explicit sync URL
let relay_sync = TestRelay::start_with_sync(relay_source.url()).await;
tokio::time::sleep(Duration::from_millis(300)).await;
// Both relays should be running
// The sync relay has NGIT_SYNC_RELAY_URL set (verified by relay starting)
let client_source = Client::default();
client_source
.add_relay(relay_source.url())
.await
.expect("Failed to add source relay");
client_source.connect().await;
let client_sync = Client::default();
client_sync
.add_relay(relay_sync.url())
.await
.expect("Failed to add sync relay");
client_sync.connect().await;
// Both should be accessible
client_sync.disconnect().await;
client_source.disconnect().await;
relay_sync.stop().await;
relay_source.stop().await;
}