diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-12-11 16:45:34 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-12-11 16:46:01 +0000 |
| commit | 52489d3b1a7d79e164b4cc901b53fd06c05ce1b1 (patch) | |
| tree | 9be147a22a95b7634a8120a60f2cd8899805088a /tests | |
| parent | 6d0447f31eb9f9282e60ac3c90c665a8b3781331 (diff) | |
sync: test sync works without negentropy and add disable option in sync
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/common/relay.rs | 55 | ||||
| -rw-r--r-- | tests/sync/bootstrap.rs | 108 |
2 files changed, 158 insertions, 5 deletions
diff --git a/tests/common/relay.rs b/tests/common/relay.rs index 0b3926e..2dd526b 100644 --- a/tests/common/relay.rs +++ b/tests/common/relay.rs | |||
| @@ -38,7 +38,21 @@ impl TestRelay { | |||
| 38 | 38 | ||
| 39 | /// Start relay on a specific port | 39 | /// Start relay on a specific port |
| 40 | pub async fn start_with_port(port: u16) -> Self { | 40 | pub async fn start_with_port(port: u16) -> Self { |
| 41 | Self::start_with_options(port, None).await | 41 | Self::start_with_full_options(port, None, false).await |
| 42 | } | ||
| 43 | |||
| 44 | /// Start relay on a specific port with full options | ||
| 45 | /// | ||
| 46 | /// This is useful for testing history sync where we need to: | ||
| 47 | /// 1. Start relay_b (first instance) to get its domain | ||
| 48 | /// 2. Stop relay_b | ||
| 49 | /// 3. Start relay_b (second instance) on SAME port with different options | ||
| 50 | pub async fn start_on_port_with_options( | ||
| 51 | port: u16, | ||
| 52 | bootstrap_relay_url: Option<String>, | ||
| 53 | disable_negentropy: bool, | ||
| 54 | ) -> Self { | ||
| 55 | Self::start_with_full_options(port, bootstrap_relay_url, disable_negentropy).await | ||
| 42 | } | 56 | } |
| 43 | 57 | ||
| 44 | /// Start relay with sync from another relay (bootstrap relay) | 58 | /// Start relay with sync from another relay (bootstrap relay) |
| @@ -58,11 +72,39 @@ impl TestRelay { | |||
| 58 | /// } | 72 | /// } |
| 59 | /// ``` | 73 | /// ``` |
| 60 | pub async fn start_with_sync(bootstrap_relay_url: Option<String>) -> Self { | 74 | pub async fn start_with_sync(bootstrap_relay_url: Option<String>) -> Self { |
| 61 | Self::start_with_options(Self::find_free_port(), bootstrap_relay_url).await | 75 | Self::start_with_full_options(Self::find_free_port(), bootstrap_relay_url, false).await |
| 76 | } | ||
| 77 | |||
| 78 | /// Start relay with sync and negentropy disabled | ||
| 79 | /// | ||
| 80 | /// This is useful for testing that sync works without NIP-77 negentropy. | ||
| 81 | /// History sync will use REQ+EOSE instead of the more efficient negentropy protocol. | ||
| 82 | /// | ||
| 83 | /// # Example | ||
| 84 | /// | ||
| 85 | /// ```no_run | ||
| 86 | /// use common::TestRelay; | ||
| 87 | /// | ||
| 88 | /// #[tokio::test] | ||
| 89 | /// async fn test_sync_without_negentropy() { | ||
| 90 | /// let source = TestRelay::start().await; | ||
| 91 | /// let syncing = TestRelay::start_with_sync_no_negentropy(Some(source.url().into())).await; | ||
| 92 | /// // ... test sync behavior without negentropy ... | ||
| 93 | /// syncing.stop().await; | ||
| 94 | /// source.stop().await; | ||
| 95 | /// } | ||
| 96 | /// ``` | ||
| 97 | pub async fn start_with_sync_no_negentropy(bootstrap_relay_url: Option<String>) -> Self { | ||
| 98 | Self::start_with_full_options(Self::find_free_port(), bootstrap_relay_url, true).await | ||
| 62 | } | 99 | } |
| 63 | 100 | ||
| 64 | /// Start relay with options | 101 | /// Start relay with options (internal, maintains backward compatibility) |
| 65 | async fn start_with_options(port: u16, bootstrap_relay_url: Option<String>) -> Self { | 102 | async fn start_with_options(port: u16, bootstrap_relay_url: Option<String>) -> Self { |
| 103 | Self::start_with_full_options(port, bootstrap_relay_url, false).await | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Start relay with full options | ||
| 107 | async fn start_with_full_options(port: u16, bootstrap_relay_url: Option<String>, disable_negentropy: bool) -> Self { | ||
| 66 | let bind_address = format!("127.0.0.1:{}", port); | 108 | let bind_address = format!("127.0.0.1:{}", port); |
| 67 | let url = format!("ws://127.0.0.1:{}", port); | 109 | let url = format!("ws://127.0.0.1:{}", port); |
| 68 | 110 | ||
| @@ -108,6 +150,11 @@ impl TestRelay { | |||
| 108 | cmd.env("NGIT_SYNC_BOOTSTRAP_RELAY_URL", bootstrap_url); | 150 | cmd.env("NGIT_SYNC_BOOTSTRAP_RELAY_URL", bootstrap_url); |
| 109 | } | 151 | } |
| 110 | 152 | ||
| 153 | // Add negentropy disable flag if requested | ||
| 154 | if disable_negentropy { | ||
| 155 | cmd.env("NGIT_SYNC_DISABLE_NEGENTROPY", "true"); | ||
| 156 | } | ||
| 157 | |||
| 111 | let process = cmd.spawn().expect("Failed to start relay process"); | 158 | let process = cmd.spawn().expect("Failed to start relay process"); |
| 112 | 159 | ||
| 113 | let relay = Self { process, url, port }; | 160 | let relay = Self { process, url, port }; |
| @@ -166,7 +213,7 @@ impl TestRelay { | |||
| 166 | } | 213 | } |
| 167 | 214 | ||
| 168 | /// Find a free port to use for testing | 215 | /// Find a free port to use for testing |
| 169 | fn find_free_port() -> u16 { | 216 | pub fn find_free_port() -> u16 { |
| 170 | use std::net::TcpListener; | 217 | use std::net::TcpListener; |
| 171 | 218 | ||
| 172 | // Bind to port 0 to get a random free port | 219 | // Bind to port 0 to get a random free port |
diff --git a/tests/sync/bootstrap.rs b/tests/sync/bootstrap.rs index 0d68609..8a181c9 100644 --- a/tests/sync/bootstrap.rs +++ b/tests/sync/bootstrap.rs | |||
| @@ -305,4 +305,110 @@ async fn test_announcement_not_listing_relay_is_not_synced() { | |||
| 305 | announcement_id | 305 | announcement_id |
| 306 | ); | 306 | ); |
| 307 | println!("SUCCESS: Announcement was correctly rejected by relay_b (not synced)"); | 307 | println!("SUCCESS: Announcement was correctly rejected by relay_b (not synced)"); |
| 308 | } \ No newline at end of file | 308 | } |
| 309 | |||
| 310 | /// Test: History sync (bootstrap) works without NIP-77 negentropy | ||
| 311 | /// | ||
| 312 | /// This tests that HISTORY sync works when negentropy is disabled. | ||
| 313 | /// History sync means: events that existed on the source relay BEFORE | ||
| 314 | /// the syncing relay connected. | ||
| 315 | /// | ||
| 316 | /// Scenario: | ||
| 317 | /// 1. Start relay_b temporarily to get its domain (then stop it) | ||
| 318 | /// 2. Start relay_a (source) | ||
| 319 | /// 3. Create announcement listing both relay domains | ||
| 320 | /// 4. Send announcement to relay_a (event exists BEFORE relay_b connects) | ||
| 321 | /// 5. Start relay_b AGAIN on same port, with negentropy DISABLED | ||
| 322 | /// 6. relay_b should sync the pre-existing event via REQ+EOSE (history sync) | ||
| 323 | /// 7. Verify relay_b has the event | ||
| 324 | /// | ||
| 325 | /// This is different from "live sync" where events arrive after connection. | ||
| 326 | #[tokio::test] | ||
| 327 | async fn test_history_sync_without_negentropy() { | ||
| 328 | // 1. First, start relay_b temporarily just to reserve a port and get its domain | ||
| 329 | let relay_b_port = TestRelay::find_free_port(); | ||
| 330 | let relay_b_domain = format!("127.0.0.1:{}", relay_b_port); | ||
| 331 | println!( | ||
| 332 | "Reserved port {} for relay_b (domain: {})", | ||
| 333 | relay_b_port, relay_b_domain | ||
| 334 | ); | ||
| 335 | |||
| 336 | // 2. Start relay_a (source relay) | ||
| 337 | let relay_a = TestRelay::start().await; | ||
| 338 | println!( | ||
| 339 | "relay_a started at {} (domain: {})", | ||
| 340 | relay_a.url(), | ||
| 341 | relay_a.domain() | ||
| 342 | ); | ||
| 343 | |||
| 344 | // 3. Create test keys | ||
| 345 | let keys = Keys::generate(); | ||
| 346 | |||
| 347 | // 4. Create announcement listing BOTH relay domains | ||
| 348 | // This event will exist on relay_a BEFORE relay_b ever connects | ||
| 349 | let announcement = create_repo_announcement( | ||
| 350 | &keys, | ||
| 351 | &[&relay_a.domain(), &relay_b_domain], | ||
| 352 | "test-repo-history-no-negentropy", | ||
| 353 | ); | ||
| 354 | let announcement_id = announcement.id; | ||
| 355 | |||
| 356 | println!( | ||
| 357 | "Created announcement {} (kind {})", | ||
| 358 | announcement_id, | ||
| 359 | announcement.kind.as_u16() | ||
| 360 | ); | ||
| 361 | for tag in announcement.tags.iter() { | ||
| 362 | println!(" Tag: {:?}", tag.as_slice()); | ||
| 363 | } | ||
| 364 | |||
| 365 | // 5. Send announcement to relay_a (event now exists BEFORE relay_b connects) | ||
| 366 | let client_a = TestClient::new(relay_a.url(), keys.clone()) | ||
| 367 | .await | ||
| 368 | .expect("Failed to connect to relay_a"); | ||
| 369 | |||
| 370 | client_a | ||
| 371 | .send_event(&announcement) | ||
| 372 | .await | ||
| 373 | .expect("Failed to send announcement to relay_a"); | ||
| 374 | println!("Announcement sent to relay_a (event exists BEFORE relay_b connects)"); | ||
| 375 | |||
| 376 | client_a.disconnect().await; | ||
| 377 | |||
| 378 | // 6. Wait a moment to ensure the event is stored | ||
| 379 | tokio::time::sleep(Duration::from_millis(500)).await; | ||
| 380 | |||
| 381 | // 7. NOW start relay_b on the reserved port, with negentropy DISABLED | ||
| 382 | // This relay_b has never connected before - it needs to do HISTORY sync | ||
| 383 | let relay_b = TestRelay::start_on_port_with_options( | ||
| 384 | relay_b_port, | ||
| 385 | Some(relay_a.url().into()), | ||
| 386 | true, // disable_negentropy = true | ||
| 387 | ).await; | ||
| 388 | println!( | ||
| 389 | "relay_b started at {} (domain: {}) - negentropy DISABLED, will do HISTORY sync", | ||
| 390 | relay_b.url(), | ||
| 391 | relay_b.domain() | ||
| 392 | ); | ||
| 393 | |||
| 394 | // 8. Wait for history sync to complete (using REQ+EOSE, not negentropy) | ||
| 395 | tokio::time::sleep(Duration::from_secs(3)).await; | ||
| 396 | |||
| 397 | // 9. Verify announcement synced to relay_b via HISTORY sync | ||
| 398 | let filter = Filter::new() | ||
| 399 | .kind(Kind::Custom(KIND_REPOSITORY_STATE)) | ||
| 400 | .author(keys.public_key()); | ||
| 401 | |||
| 402 | let synced = wait_for_event_on_relay(relay_b.url(), filter, Duration::from_secs(5)).await; | ||
| 403 | |||
| 404 | // 10. Cleanup | ||
| 405 | relay_b.stop().await; | ||
| 406 | relay_a.stop().await; | ||
| 407 | |||
| 408 | assert!( | ||
| 409 | synced, | ||
| 410 | "Announcement {} should have synced from relay_a to relay_b via HISTORY sync (REQ+EOSE, negentropy disabled)", | ||
| 411 | announcement_id | ||
| 412 | ); | ||
| 413 | println!("SUCCESS: History sync works without negentropy (using REQ+EOSE fallback)"); | ||
| 414 | } | ||