diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2026-02-13 13:24:46 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2026-02-13 17:29:23 +0000 |
| commit | 1d09e4bdea7e328cf2740818df9df660c5532a99 (patch) | |
| tree | dcb758a70a2e9b84709df247cc685a2f6423094e /tests/purgatory_persistence.rs | |
| parent | a2a99d5a4137b57e4141cf2840f2f51b38035cfa (diff) | |
feat: implement announcement purgatory core (breaks archive sync test)
Route new announcements to purgatory instead of accepting immediately.
Announcements are promoted to the database when git data arrives,
ensuring we only serve announcements for repos with actual content.
Implemented:
- AnnouncementPurgatoryEntry type and DashMap store
- Route new announcements to purgatory (replacement announcements skip)
- Promote announcements on git data arrival (process_purgatory_announcements)
- Authorization checks purgatory announcements (fetch_repository_data_with_purgatory)
- State policy uses purgatory announcements for maintainer validation
- Cleanup task handles announcement expiry
- Updated count()/cleanup() to 3-tuples
Known broken:
- test_archive_read_only_creates_bare_repo fails: sync module does not
treat purgatory announcements as confirmed repos, so per-repo sync
(state events, PRs) is never triggered for purgatory announcements
- Announcement persistence (save/restore) not implemented
- SyncLevel (StateOnly vs Full) not implemented
- Soft expiry two-phase not implemented
- Expiry extension on state event / git auth not wired up
Diffstat (limited to 'tests/purgatory_persistence.rs')
| -rw-r--r-- | tests/purgatory_persistence.rs | 26 |
1 files changed, 14 insertions, 12 deletions
diff --git a/tests/purgatory_persistence.rs b/tests/purgatory_persistence.rs index fe37c33..5abbf15 100644 --- a/tests/purgatory_persistence.rs +++ b/tests/purgatory_persistence.rs | |||
| @@ -120,7 +120,8 @@ async fn test_full_purgatory_save_restore_cycle() { | |||
| 120 | // so we'll focus on testing state and PR events persistence | 120 | // so we'll focus on testing state and PR events persistence |
| 121 | 121 | ||
| 122 | // Verify initial counts | 122 | // Verify initial counts |
| 123 | let (state_count, pr_count) = purgatory.count(); | 123 | let (announcement_count, state_count, pr_count) = purgatory.count(); |
| 124 | assert_eq!(announcement_count, 0, "Should have 0 announcements"); | ||
| 124 | assert_eq!(state_count, 2, "Should have 2 state events"); | 125 | assert_eq!(state_count, 2, "Should have 2 state events"); |
| 125 | assert_eq!( | 126 | assert_eq!( |
| 126 | pr_count, 3, | 127 | pr_count, 3, |
| @@ -142,7 +143,8 @@ async fn test_full_purgatory_save_restore_cycle() { | |||
| 142 | ); | 143 | ); |
| 143 | 144 | ||
| 144 | // Verify all data was restored | 145 | // Verify all data was restored |
| 145 | let (state_count2, pr_count2) = purgatory2.count(); | 146 | let (announcement_count2, state_count2, pr_count2) = purgatory2.count(); |
| 147 | assert_eq!(announcement_count2, 0, "Should have 0 announcements after restore"); | ||
| 146 | assert_eq!(state_count2, 2, "Should have 2 state events after restore"); | 148 | assert_eq!(state_count2, 2, "Should have 2 state events after restore"); |
| 147 | assert_eq!( | 149 | assert_eq!( |
| 148 | pr_count2, 3, | 150 | pr_count2, 3, |
| @@ -275,7 +277,7 @@ async fn test_purgatory_downtime_adjustment() { | |||
| 275 | purgatory2.restore_from_disk(&state_path).unwrap(); | 277 | purgatory2.restore_from_disk(&state_path).unwrap(); |
| 276 | 278 | ||
| 277 | // Verify event is still there (downtime was accounted for) | 279 | // Verify event is still there (downtime was accounted for) |
| 278 | let (state_count, _) = purgatory2.count(); | 280 | let (_, state_count, _) = purgatory2.count(); |
| 279 | assert_eq!(state_count, 1); | 281 | assert_eq!(state_count, 1); |
| 280 | 282 | ||
| 281 | let repo1_states = purgatory2.find_state("repo1"); | 283 | let repo1_states = purgatory2.find_state("repo1"); |
| @@ -401,7 +403,7 @@ async fn test_purgatory_restore_missing_file() { | |||
| 401 | assert!(result.is_err(), "Should error on missing file"); | 403 | assert!(result.is_err(), "Should error on missing file"); |
| 402 | 404 | ||
| 403 | // Purgatory should still be usable (empty state) | 405 | // Purgatory should still be usable (empty state) |
| 404 | let (state_count, pr_count) = purgatory.count(); | 406 | let (_, state_count, pr_count) = purgatory.count(); |
| 405 | assert_eq!(state_count, 0); | 407 | assert_eq!(state_count, 0); |
| 406 | assert_eq!(pr_count, 0); | 408 | assert_eq!(pr_count, 0); |
| 407 | 409 | ||
| @@ -410,7 +412,7 @@ async fn test_purgatory_restore_missing_file() { | |||
| 410 | let event = create_test_event(&keys, "test").await; | 412 | let event = create_test_event(&keys, "test").await; |
| 411 | purgatory.add_state(event, "repo1".to_string(), keys.public_key()); | 413 | purgatory.add_state(event, "repo1".to_string(), keys.public_key()); |
| 412 | 414 | ||
| 413 | let (state_count, _) = purgatory.count(); | 415 | let (_, state_count, _) = purgatory.count(); |
| 414 | assert_eq!(state_count, 1); | 416 | assert_eq!(state_count, 1); |
| 415 | } | 417 | } |
| 416 | 418 | ||
| @@ -461,7 +463,7 @@ async fn test_purgatory_restore_corrupted_file() { | |||
| 461 | assert!(result.is_err(), "Should error on corrupted file"); | 463 | assert!(result.is_err(), "Should error on corrupted file"); |
| 462 | 464 | ||
| 463 | // Purgatory should still be usable | 465 | // Purgatory should still be usable |
| 464 | let (state_count, pr_count) = purgatory.count(); | 466 | let (_, state_count, pr_count) = purgatory.count(); |
| 465 | assert_eq!(state_count, 0); | 467 | assert_eq!(state_count, 0); |
| 466 | assert_eq!(pr_count, 0); | 468 | assert_eq!(pr_count, 0); |
| 467 | } | 469 | } |
| @@ -504,7 +506,7 @@ async fn test_empty_purgatory_save_restore() { | |||
| 504 | purgatory2.restore_from_disk(&state_path).unwrap(); | 506 | purgatory2.restore_from_disk(&state_path).unwrap(); |
| 505 | 507 | ||
| 506 | // Verify empty state | 508 | // Verify empty state |
| 507 | let (state_count, pr_count) = purgatory2.count(); | 509 | let (_, state_count, pr_count) = purgatory2.count(); |
| 508 | assert_eq!(state_count, 0); | 510 | assert_eq!(state_count, 0); |
| 509 | assert_eq!(pr_count, 0); | 511 | assert_eq!(pr_count, 0); |
| 510 | assert_eq!(purgatory2.expired_count(), 0); | 512 | assert_eq!(purgatory2.expired_count(), 0); |
| @@ -591,7 +593,7 @@ async fn test_purgatory_continues_working_after_restore() { | |||
| 591 | purgatory2.add_state(event2.clone(), "repo2".to_string(), keys.public_key()); | 593 | purgatory2.add_state(event2.clone(), "repo2".to_string(), keys.public_key()); |
| 592 | 594 | ||
| 593 | // Verify both old and new events work | 595 | // Verify both old and new events work |
| 594 | let (state_count, _) = purgatory2.count(); | 596 | let (_, state_count, _) = purgatory2.count(); |
| 595 | assert_eq!(state_count, 2); | 597 | assert_eq!(state_count, 2); |
| 596 | 598 | ||
| 597 | let repo1_states = purgatory2.find_state("repo1"); | 599 | let repo1_states = purgatory2.find_state("repo1"); |
| @@ -603,7 +605,7 @@ async fn test_purgatory_continues_working_after_restore() { | |||
| 603 | assert_eq!(repo2_states[0].event.id, event2.id); | 605 | assert_eq!(repo2_states[0].event.id, event2.id); |
| 604 | 606 | ||
| 605 | // Verify cleanup still works | 607 | // Verify cleanup still works |
| 606 | let (state_removed, pr_removed) = purgatory2.cleanup(); | 608 | let (_, state_removed, pr_removed) = purgatory2.cleanup(); |
| 607 | // Nothing should be expired yet | 609 | // Nothing should be expired yet |
| 608 | assert_eq!(state_removed, 0); | 610 | assert_eq!(state_removed, 0); |
| 609 | assert_eq!(pr_removed, 0); | 611 | assert_eq!(pr_removed, 0); |
| @@ -684,15 +686,15 @@ async fn test_purgatory_entries_expired_during_downtime() { | |||
| 684 | purgatory2.restore_from_disk(&state_path).unwrap(); | 686 | purgatory2.restore_from_disk(&state_path).unwrap(); |
| 685 | 687 | ||
| 686 | // Event should be restored | 688 | // Event should be restored |
| 687 | let (state_count, _) = purgatory2.count(); | 689 | let (_, state_count, _) = purgatory2.count(); |
| 688 | assert_eq!(state_count, 1); | 690 | assert_eq!(state_count, 1); |
| 689 | 691 | ||
| 690 | // Cleanup should work (even if nothing is expired yet) | 692 | // Cleanup should work (even if nothing is expired yet) |
| 691 | let (state_removed, _) = purgatory2.cleanup(); | 693 | let (_, state_removed, _) = purgatory2.cleanup(); |
| 692 | // Nothing expired yet since we didn't wait 30 minutes | 694 | // Nothing expired yet since we didn't wait 30 minutes |
| 693 | assert_eq!(state_removed, 0); | 695 | assert_eq!(state_removed, 0); |
| 694 | 696 | ||
| 695 | let (state_count, _) = purgatory2.count(); | 697 | let (_, state_count, _) = purgatory2.count(); |
| 696 | assert_eq!(state_count, 1); | 698 | assert_eq!(state_count, 1); |
| 697 | } | 699 | } |
| 698 | 700 | ||