upleb.uk

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

summaryrefslogtreecommitdiff
path: root/tests/purgatory_sync.rs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2026-01-07 15:50:50 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2026-01-07 15:50:50 +0000
commit049cff14fa731c95b9b0074f67469df3af19870b (patch)
tree78f87b52385eaaaa1d221745cdf367ee81b598ba /tests/purgatory_sync.rs
parent1db877d53c4ff45971c69fecc5165c352ec316c9 (diff)
test: add test_pr_event_syncs_from_remote
Diffstat (limited to 'tests/purgatory_sync.rs')
-rw-r--r--tests/purgatory_sync.rs168
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 @@
28mod common; 28mod common;
29 29
30use common::{ 30use 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};
35use nostr_sdk::prelude::*; 36use nostr_sdk::prelude::*;
36use std::time::Duration; 37use 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]
295async 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}