upleb.uk

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

summaryrefslogtreecommitdiff
path: root/tests/purgatory_persistence.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/purgatory_persistence.rs')
-rw-r--r--tests/purgatory_persistence.rs157
1 files changed, 143 insertions, 14 deletions
diff --git a/tests/purgatory_persistence.rs b/tests/purgatory_persistence.rs
index 4dc5e94..655b0d9 100644
--- a/tests/purgatory_persistence.rs
+++ b/tests/purgatory_persistence.rs
@@ -31,9 +31,11 @@
31 31
32mod common; 32mod common;
33 33
34use common::purgatory_helpers::create_announcement_event;
34use ngit_grasp::purgatory::Purgatory; 35use ngit_grasp::purgatory::Purgatory;
35use ngit_grasp::sync::rejected_index::{EventType, RejectedEventsIndex, RejectionReason}; 36use ngit_grasp::sync::rejected_index::{EventType, RejectedEventsIndex, RejectionReason};
36use nostr_sdk::prelude::*; 37use nostr_sdk::prelude::*;
38use std::collections::HashSet;
37use std::time::Duration; 39use std::time::Duration;
38 40
39/// Helper to create a test event 41/// Helper to create a test event
@@ -120,11 +122,31 @@ async fn test_full_purgatory_save_restore_cycle() {
120 // Add a PR placeholder (git-data-first scenario) 122 // Add a PR placeholder (git-data-first scenario)
121 purgatory.add_pr_placeholder("placeholder-id".to_string(), "commit-xyz".to_string()); 123 purgatory.add_pr_placeholder("placeholder-id".to_string(), "commit-xyz".to_string());
122 124
123 // Note: We can't directly test expired events without accessing private fields, 125 // Add an announcement to purgatory (requires a real directory for the repo path)
124 // so we'll focus on testing state and PR events persistence 126 let repo_dir = temp_dir.path().join("repo.git");
127 std::fs::create_dir_all(&repo_dir).unwrap();
128 let ann_keys = Keys::generate();
129 let ann_event = create_announcement_event(
130 &ann_keys,
131 "my-repo",
132 &["http://example.com/my-repo.git"],
133 &["wss://relay.example.com"],
134 )
135 .unwrap();
136 let ann_event_id = ann_event.id;
137 let mut ann_relays = HashSet::new();
138 ann_relays.insert("wss://relay.example.com".to_string());
139 purgatory.add_announcement(
140 ann_event,
141 "my-repo".to_string(),
142 ann_keys.public_key(),
143 repo_dir.clone(),
144 ann_relays,
145 );
125 146
126 // Verify initial counts 147 // Verify initial counts
127 let (state_count, pr_count) = purgatory.count(); 148 let (announcement_count, state_count, pr_count) = purgatory.count();
149 assert_eq!(announcement_count, 1, "Should have 1 announcement");
128 assert_eq!(state_count, 2, "Should have 2 state events"); 150 assert_eq!(state_count, 2, "Should have 2 state events");
129 assert_eq!( 151 assert_eq!(
130 pr_count, 3, 152 pr_count, 3,
@@ -146,13 +168,23 @@ async fn test_full_purgatory_save_restore_cycle() {
146 ); 168 );
147 169
148 // Verify all data was restored 170 // Verify all data was restored
149 let (state_count2, pr_count2) = purgatory2.count(); 171 let (announcement_count2, state_count2, pr_count2) = purgatory2.count();
172 assert_eq!(announcement_count2, 1, "Should have 1 announcement after restore");
150 assert_eq!(state_count2, 2, "Should have 2 state events after restore"); 173 assert_eq!(state_count2, 2, "Should have 2 state events after restore");
151 assert_eq!( 174 assert_eq!(
152 pr_count2, 3, 175 pr_count2, 3,
153 "Should have 3 PR events after restore (2 events + 1 placeholder)" 176 "Should have 3 PR events after restore (2 events + 1 placeholder)"
154 ); 177 );
155 178
179 // Verify announcement was restored correctly
180 let restored_ann = purgatory2
181 .find_announcement(&ann_keys.public_key(), "my-repo")
182 .expect("Announcement should be restored");
183 assert_eq!(restored_ann.event.id, ann_event_id);
184 assert_eq!(restored_ann.identifier, "my-repo");
185 assert_eq!(restored_ann.repo_path, repo_dir);
186 assert!(!restored_ann.soft_expired);
187
156 // Verify specific state events 188 // Verify specific state events
157 let repo1_states = purgatory2.find_state("repo1"); 189 let repo1_states = purgatory2.find_state("repo1");
158 assert_eq!(repo1_states.len(), 1); 190 assert_eq!(repo1_states.len(), 1);
@@ -284,7 +316,7 @@ async fn test_purgatory_downtime_adjustment() {
284 purgatory2.restore_from_disk(&state_path).unwrap(); 316 purgatory2.restore_from_disk(&state_path).unwrap();
285 317
286 // Verify event is still there (downtime was accounted for) 318 // Verify event is still there (downtime was accounted for)
287 let (state_count, _) = purgatory2.count(); 319 let (_, state_count, _) = purgatory2.count();
288 assert_eq!(state_count, 1); 320 assert_eq!(state_count, 1);
289 321
290 let repo1_states = purgatory2.find_state("repo1"); 322 let repo1_states = purgatory2.find_state("repo1");
@@ -410,7 +442,7 @@ async fn test_purgatory_restore_missing_file() {
410 assert!(result.is_err(), "Should error on missing file"); 442 assert!(result.is_err(), "Should error on missing file");
411 443
412 // Purgatory should still be usable (empty state) 444 // Purgatory should still be usable (empty state)
413 let (state_count, pr_count) = purgatory.count(); 445 let (_, state_count, pr_count) = purgatory.count();
414 assert_eq!(state_count, 0); 446 assert_eq!(state_count, 0);
415 assert_eq!(pr_count, 0); 447 assert_eq!(pr_count, 0);
416 448
@@ -419,7 +451,7 @@ async fn test_purgatory_restore_missing_file() {
419 let event = create_test_event(&keys, "test").await; 451 let event = create_test_event(&keys, "test").await;
420 purgatory.add_state(event, "repo1".to_string(), keys.public_key(), false); 452 purgatory.add_state(event, "repo1".to_string(), keys.public_key(), false);
421 453
422 let (state_count, _) = purgatory.count(); 454 let (_, state_count, _) = purgatory.count();
423 assert_eq!(state_count, 1); 455 assert_eq!(state_count, 1);
424} 456}
425 457
@@ -470,7 +502,7 @@ async fn test_purgatory_restore_corrupted_file() {
470 assert!(result.is_err(), "Should error on corrupted file"); 502 assert!(result.is_err(), "Should error on corrupted file");
471 503
472 // Purgatory should still be usable 504 // Purgatory should still be usable
473 let (state_count, pr_count) = purgatory.count(); 505 let (_, state_count, pr_count) = purgatory.count();
474 assert_eq!(state_count, 0); 506 assert_eq!(state_count, 0);
475 assert_eq!(pr_count, 0); 507 assert_eq!(pr_count, 0);
476} 508}
@@ -513,7 +545,7 @@ async fn test_empty_purgatory_save_restore() {
513 purgatory2.restore_from_disk(&state_path).unwrap(); 545 purgatory2.restore_from_disk(&state_path).unwrap();
514 546
515 // Verify empty state 547 // Verify empty state
516 let (state_count, pr_count) = purgatory2.count(); 548 let (_, state_count, pr_count) = purgatory2.count();
517 assert_eq!(state_count, 0); 549 assert_eq!(state_count, 0);
518 assert_eq!(pr_count, 0); 550 assert_eq!(pr_count, 0);
519 assert_eq!(purgatory2.expired_count(), 0); 551 assert_eq!(purgatory2.expired_count(), 0);
@@ -620,7 +652,7 @@ async fn test_purgatory_continues_working_after_restore() {
620 ); 652 );
621 653
622 // Verify both old and new events work 654 // Verify both old and new events work
623 let (state_count, _) = purgatory2.count(); 655 let (_, state_count, _) = purgatory2.count();
624 assert_eq!(state_count, 2); 656 assert_eq!(state_count, 2);
625 657
626 let repo1_states = purgatory2.find_state("repo1"); 658 let repo1_states = purgatory2.find_state("repo1");
@@ -632,7 +664,7 @@ async fn test_purgatory_continues_working_after_restore() {
632 assert_eq!(repo2_states[0].event.id, event2.id); 664 assert_eq!(repo2_states[0].event.id, event2.id);
633 665
634 // Verify cleanup still works 666 // Verify cleanup still works
635 let (state_removed, pr_removed) = purgatory2.cleanup(); 667 let (_, state_removed, pr_removed) = purgatory2.cleanup();
636 // Nothing should be expired yet 668 // Nothing should be expired yet
637 assert_eq!(state_removed, 0); 669 assert_eq!(state_removed, 0);
638 assert_eq!(pr_removed, 0); 670 assert_eq!(pr_removed, 0);
@@ -713,15 +745,15 @@ async fn test_purgatory_entries_expired_during_downtime() {
713 purgatory2.restore_from_disk(&state_path).unwrap(); 745 purgatory2.restore_from_disk(&state_path).unwrap();
714 746
715 // Event should be restored 747 // Event should be restored
716 let (state_count, _) = purgatory2.count(); 748 let (_, state_count, _) = purgatory2.count();
717 assert_eq!(state_count, 1); 749 assert_eq!(state_count, 1);
718 750
719 // Cleanup should work (even if nothing is expired yet) 751 // Cleanup should work (even if nothing is expired yet)
720 let (state_removed, _) = purgatory2.cleanup(); 752 let (_, state_removed, _) = purgatory2.cleanup();
721 // Nothing expired yet since we didn't wait 30 minutes 753 // Nothing expired yet since we didn't wait 30 minutes
722 assert_eq!(state_removed, 0); 754 assert_eq!(state_removed, 0);
723 755
724 let (state_count, _) = purgatory2.count(); 756 let (_, state_count, _) = purgatory2.count();
725 assert_eq!(state_count, 1); 757 assert_eq!(state_count, 1);
726} 758}
727 759
@@ -775,3 +807,100 @@ async fn test_rejected_cache_entries_expired_during_downtime() {
775 assert_eq!(index2.hot_cache_len(), 0); 807 assert_eq!(index2.hot_cache_len(), 0);
776 assert_eq!(index2.cold_index_len(), 1); 808 assert_eq!(index2.cold_index_len(), 1);
777} 809}
810
811/// Test 18: Announcement events are saved and restored across restarts
812#[tokio::test]
813async fn test_announcement_save_restore_cycle() {
814 let temp_dir = tempfile::tempdir().unwrap();
815 let git_data_path = temp_dir.path().join("git");
816 let state_path = temp_dir.path().join("purgatory.json");
817
818 // Create a real bare repo directory (restore skips entries whose path is missing)
819 let repo_dir = temp_dir.path().join("owner.git");
820 std::fs::create_dir_all(&repo_dir).unwrap();
821
822 let purgatory = Purgatory::new(&git_data_path);
823 let keys = Keys::generate();
824
825 let ann_event = create_announcement_event(
826 &keys,
827 "my-repo",
828 &["http://example.com/my-repo.git"],
829 &["wss://relay.example.com"],
830 )
831 .unwrap();
832 let ann_event_id = ann_event.id;
833
834 let mut relays = HashSet::new();
835 relays.insert("wss://relay.example.com".to_string());
836
837 purgatory.add_announcement(
838 ann_event,
839 "my-repo".to_string(),
840 keys.public_key(),
841 repo_dir.clone(),
842 relays.clone(),
843 );
844
845 let (ann_count, _, _) = purgatory.count();
846 assert_eq!(ann_count, 1);
847
848 // Save to disk
849 purgatory.save_to_disk(&state_path).unwrap();
850 assert!(state_path.exists());
851
852 // Restore into a fresh purgatory
853 let purgatory2 = Purgatory::new(&git_data_path);
854 purgatory2.restore_from_disk(&state_path).unwrap();
855
856 assert!(!state_path.exists(), "State file should be deleted after restore");
857
858 let (ann_count2, _, _) = purgatory2.count();
859 assert_eq!(ann_count2, 1, "Announcement should be restored");
860
861 let restored = purgatory2
862 .find_announcement(&keys.public_key(), "my-repo")
863 .expect("Announcement should be findable after restore");
864
865 assert_eq!(restored.event.id, ann_event_id);
866 assert_eq!(restored.identifier, "my-repo");
867 assert_eq!(restored.owner, keys.public_key());
868 assert_eq!(restored.repo_path, repo_dir);
869 assert_eq!(restored.relays, relays);
870 assert!(!restored.soft_expired);
871}
872
873/// Test 19: Announcement with missing repo path is skipped on restore
874#[tokio::test]
875async fn test_announcement_missing_repo_skipped_on_restore() {
876 let temp_dir = tempfile::tempdir().unwrap();
877 let git_data_path = temp_dir.path().join("git");
878 let state_path = temp_dir.path().join("purgatory.json");
879
880 // Point to a path that does NOT exist on disk
881 let missing_repo = temp_dir.path().join("nonexistent.git");
882
883 let purgatory = Purgatory::new(&git_data_path);
884 let keys = Keys::generate();
885
886 let ann_event = create_announcement_event(&keys, "my-repo", &[], &[]).unwrap();
887
888 purgatory.add_announcement(
889 ann_event,
890 "my-repo".to_string(),
891 keys.public_key(),
892 missing_repo,
893 HashSet::new(),
894 );
895
896 purgatory.save_to_disk(&state_path).unwrap();
897
898 let purgatory2 = Purgatory::new(&git_data_path);
899 purgatory2.restore_from_disk(&state_path).unwrap();
900
901 let (ann_count, _, _) = purgatory2.count();
902 assert_eq!(
903 ann_count, 0,
904 "Announcement with missing repo path must be skipped"
905 );
906}