diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2026-01-07 15:50:50 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2026-01-07 15:50:50 +0000 |
| commit | 049cff14fa731c95b9b0074f67469df3af19870b (patch) | |
| tree | 78f87b52385eaaaa1d221745cdf367ee81b598ba /tests/purgatory_sync.rs | |
| parent | 1db877d53c4ff45971c69fecc5165c352ec316c9 (diff) | |
test: add test_pr_event_syncs_from_remote
Diffstat (limited to 'tests/purgatory_sync.rs')
| -rw-r--r-- | tests/purgatory_sync.rs | 168 |
1 files changed, 165 insertions, 3 deletions
diff --git a/tests/purgatory_sync.rs b/tests/purgatory_sync.rs index 3da086c..bb99f46 100644 --- a/tests/purgatory_sync.rs +++ b/tests/purgatory_sync.rs | |||
| @@ -28,9 +28,10 @@ | |||
| 28 | mod common; | 28 | mod common; |
| 29 | 29 | ||
| 30 | use common::{ | 30 | use common::{ |
| 31 | check_ref_at_commit, create_repo_announcement, create_state_event, | 31 | build_repo_coord, check_ref_at_commit, create_pr_event, create_repo_announcement, |
| 32 | create_test_repo_with_commit, push_to_relay, verify_event_not_served, wait_for_event_served, | 32 | create_state_event, create_test_repo_with_commit, push_ref_to_relay, push_to_relay, |
| 33 | wait_for_sync_connection, CommitVariant, TestRelay, | 33 | verify_event_not_served, wait_for_event_served, wait_for_sync_connection, CommitVariant, |
| 34 | TestRelay, | ||
| 34 | }; | 35 | }; |
| 35 | use nostr_sdk::prelude::*; | 36 | use nostr_sdk::prelude::*; |
| 36 | use std::time::Duration; | 37 | use std::time::Duration; |
| @@ -277,3 +278,164 @@ async fn test_state_event_syncs_from_remote() { | |||
| 277 | syncing_relay.stop().await; | 278 | syncing_relay.stop().await; |
| 278 | source_relay.stop().await; | 279 | source_relay.stop().await; |
| 279 | } | 280 | } |
| 281 | |||
| 282 | /// Test that a PR event entering purgatory triggers remote commit fetch | ||
| 283 | /// and is released once the commit is available. | ||
| 284 | /// | ||
| 285 | /// Scenario: | ||
| 286 | /// 1. Start source relay with repository announcement | ||
| 287 | /// 2. Create PR event (goes to purgatory - no git data yet) | ||
| 288 | /// 3. Push commit to refs/nostr/<event-id> (authorized by PR event in purgatory) | ||
| 289 | /// 4. PR event gets released from purgatory on source relay | ||
| 290 | /// 5. Start syncing relay | ||
| 291 | /// 6. Syncing relay syncs PR event (goes to purgatory - no local git data) | ||
| 292 | /// 7. Syncing relay fetches commit from source's clone URL | ||
| 293 | /// 8. Verify PR event is released and refs/nostr/<event-id> created on syncing relay | ||
| 294 | #[tokio::test] | ||
| 295 | async fn test_pr_event_syncs_from_remote() { | ||
| 296 | // 1. Start source relay | ||
| 297 | let source_relay = TestRelay::start().await; | ||
| 298 | let owner_keys = Keys::generate(); | ||
| 299 | let pr_author_keys = Keys::generate(); | ||
| 300 | let identifier = "pr-sync-test-repo"; | ||
| 301 | |||
| 302 | // Pre-allocate syncing relay port so we can include it in announcement | ||
| 303 | let syncing_port = TestRelay::find_free_port(); | ||
| 304 | let syncing_domain = format!("127.0.0.1:{}", syncing_port); | ||
| 305 | |||
| 306 | // 2. Create test repository locally with PR commit | ||
| 307 | let temp_dir = tempfile::tempdir().expect("Failed to create temp dir"); | ||
| 308 | let commit_hash = create_test_repo_with_commit(temp_dir.path(), CommitVariant::PrTest) | ||
| 309 | .expect("Failed to create test repo"); | ||
| 310 | |||
| 311 | let npub = owner_keys | ||
| 312 | .public_key() | ||
| 313 | .to_bech32() | ||
| 314 | .expect("Failed to get npub"); | ||
| 315 | |||
| 316 | // 3. Create and send announcement listing BOTH relays | ||
| 317 | // This ensures the syncing relay will accept the PR event when it syncs | ||
| 318 | let announcement = create_repo_announcement( | ||
| 319 | &owner_keys, | ||
| 320 | &[&source_relay.domain(), &syncing_domain], | ||
| 321 | identifier, | ||
| 322 | ); | ||
| 323 | |||
| 324 | let source_client = Client::new(owner_keys.clone()); | ||
| 325 | source_client | ||
| 326 | .add_relay(source_relay.url()) | ||
| 327 | .await | ||
| 328 | .expect("Failed to add source relay"); | ||
| 329 | source_client.connect().await; | ||
| 330 | |||
| 331 | // Wait for connection | ||
| 332 | tokio::time::sleep(Duration::from_millis(500)).await; | ||
| 333 | |||
| 334 | // Send announcement to source relay (creates bare repo) | ||
| 335 | source_client | ||
| 336 | .send_event(&announcement) | ||
| 337 | .await | ||
| 338 | .expect("Failed to send announcement to source"); | ||
| 339 | |||
| 340 | tokio::time::sleep(Duration::from_millis(200)).await; | ||
| 341 | |||
| 342 | // 4. Create and send PR event BEFORE pushing | ||
| 343 | // The PR event goes to purgatory on source relay, which authorizes the push | ||
| 344 | let repo_coord = build_repo_coord(&owner_keys, identifier); | ||
| 345 | |||
| 346 | let pr_event = create_pr_event(&pr_author_keys, &repo_coord, &commit_hash, "Test PR for sync") | ||
| 347 | .expect("Failed to create PR event"); | ||
| 348 | |||
| 349 | let pr_event_id = pr_event.id; | ||
| 350 | |||
| 351 | // Send PR event to source relay using PR author's client | ||
| 352 | let pr_client = Client::new(pr_author_keys.clone()); | ||
| 353 | pr_client | ||
| 354 | .add_relay(source_relay.url()) | ||
| 355 | .await | ||
| 356 | .expect("Failed to add source relay for PR"); | ||
| 357 | pr_client.connect().await; | ||
| 358 | tokio::time::sleep(Duration::from_millis(500)).await; | ||
| 359 | |||
| 360 | pr_client | ||
| 361 | .send_event(&pr_event) | ||
| 362 | .await | ||
| 363 | .expect("Failed to send PR event to source"); | ||
| 364 | |||
| 365 | // Small delay to ensure PR event is processed into purgatory | ||
| 366 | tokio::time::sleep(Duration::from_millis(200)).await; | ||
| 367 | |||
| 368 | // 5. Push commit to refs/nostr/<event-id> on source relay | ||
| 369 | // The PR event in purgatory authorizes this push | ||
| 370 | let ref_name = format!("refs/nostr/{}", pr_event_id.to_hex()); | ||
| 371 | push_ref_to_relay( | ||
| 372 | temp_dir.path(), | ||
| 373 | &source_relay.domain(), | ||
| 374 | &npub, | ||
| 375 | identifier, | ||
| 376 | &commit_hash, | ||
| 377 | &ref_name, | ||
| 378 | ) | ||
| 379 | .expect("Push to refs/nostr/<event-id> should succeed"); | ||
| 380 | |||
| 381 | // After push, PR event should be released from purgatory on source relay | ||
| 382 | wait_for_event_served(source_relay.url(), &pr_event_id, Duration::from_secs(5)) | ||
| 383 | .await | ||
| 384 | .expect("PR event should be served on source relay after push"); | ||
| 385 | |||
| 386 | // 6. Start syncing relay (syncs from source) | ||
| 387 | let syncing_relay = TestRelay::start_on_port_with_options( | ||
| 388 | syncing_port, | ||
| 389 | Some(source_relay.url().to_string()), | ||
| 390 | false, | ||
| 391 | ) | ||
| 392 | .await; | ||
| 393 | |||
| 394 | // Wait for sync connection to establish | ||
| 395 | wait_for_sync_connection(syncing_relay.url(), 1, Duration::from_secs(5)) | ||
| 396 | .await | ||
| 397 | .expect("Sync connection should establish"); | ||
| 398 | |||
| 399 | // 7. Wait for PR event to be released on syncing relay | ||
| 400 | // The sync should: | ||
| 401 | // a) Fetch the announcement and PR event from source relay | ||
| 402 | // b) Accept announcement (creates bare repo structure) | ||
| 403 | // c) Put PR event in purgatory (commit missing on syncing relay) | ||
| 404 | // d) Fetch commit from source relay's clone URL | ||
| 405 | // e) Release the PR event from purgatory | ||
| 406 | // f) Create refs/nostr/<event-id> pointing to the commit | ||
| 407 | let found = wait_for_event_served( | ||
| 408 | syncing_relay.url(), | ||
| 409 | &pr_event_id, | ||
| 410 | Duration::from_secs(30), // Allow time for sync + git fetch | ||
| 411 | ) | ||
| 412 | .await; | ||
| 413 | |||
| 414 | assert!( | ||
| 415 | found.is_ok(), | ||
| 416 | "PR event should be served after sync fetches commit: {:?}", | ||
| 417 | found.err() | ||
| 418 | ); | ||
| 419 | |||
| 420 | // 8. Verify refs/nostr/<event-id> was created on syncing relay | ||
| 421 | let ref_correct = check_ref_at_commit( | ||
| 422 | &syncing_domain, | ||
| 423 | &npub, | ||
| 424 | identifier, | ||
| 425 | &ref_name, | ||
| 426 | &commit_hash, | ||
| 427 | ) | ||
| 428 | .await | ||
| 429 | .expect("Failed to check PR ref"); | ||
| 430 | |||
| 431 | assert!( | ||
| 432 | ref_correct, | ||
| 433 | "refs/nostr/<event-id> should point to PR commit" | ||
| 434 | ); | ||
| 435 | |||
| 436 | // Cleanup | ||
| 437 | source_client.disconnect().await; | ||
| 438 | pr_client.disconnect().await; | ||
| 439 | syncing_relay.stop().await; | ||
| 440 | source_relay.stop().await; | ||
| 441 | } | ||