upleb.uk

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

summaryrefslogtreecommitdiff
path: root/grasp-audit/src/specs
diff options
context:
space:
mode:
Diffstat (limited to 'grasp-audit/src/specs')
-rw-r--r--grasp-audit/src/specs/grasp01/purgatory.rs107
1 files changed, 52 insertions, 55 deletions
diff --git a/grasp-audit/src/specs/grasp01/purgatory.rs b/grasp-audit/src/specs/grasp01/purgatory.rs
index 29eabad..0686da8 100644
--- a/grasp-audit/src/specs/grasp01/purgatory.rs
+++ b/grasp-audit/src/specs/grasp01/purgatory.rs
@@ -46,7 +46,11 @@ impl PurgatoryTests {
46 results.add(Self::test_announcement_not_served_before_git_data(client).await); 46 results.add(Self::test_announcement_not_served_before_git_data(client).await);
47 results.add(Self::test_announcement_served_after_git_push(client).await); 47 results.add(Self::test_announcement_served_after_git_push(client).await);
48 results.add(Self::test_bare_repo_exists_for_purgatory_announcement(client).await); 48 results.add(Self::test_bare_repo_exists_for_purgatory_announcement(client).await);
49
50 // State event purgatory tests
49 results.add(Self::test_state_event_accepted_for_purgatory_announcement(client).await); 51 results.add(Self::test_state_event_accepted_for_purgatory_announcement(client).await);
52 results.add(Self::test_state_event_not_served_before_git_data(client).await);
53 results.add(Self::test_state_event_served_after_git_push(client).await);
50 54
51 // Deletion event tests (NIP-09) 55 // Deletion event tests (NIP-09)
52 results.add(Self::test_deletion_by_event_id_removes_purgatory_state_event(client).await); 56 results.add(Self::test_deletion_by_event_id_removes_purgatory_state_event(client).await);
@@ -54,10 +58,6 @@ impl PurgatoryTests {
54 Self::test_deletion_by_coordinate_removes_purgatory_state_event(client).await, 58 Self::test_deletion_by_coordinate_removes_purgatory_state_event(client).await,
55 ); 59 );
56 60
57 // State event purgatory tests (already implemented)
58 results.add(Self::test_state_event_not_served_before_git_data(client).await);
59 results.add(Self::test_state_event_served_after_git_push(client).await);
60
61 // PR purgatory tests 61 // PR purgatory tests
62 results.add(Self::test_pr_event_accepted_into_purgatory_and_isnt_served(client).await); 62 results.add(Self::test_pr_event_accepted_into_purgatory_and_isnt_served(client).await);
63 results.add(Self::test_pr_event_in_purgatory_git_push_accepted(client).await); 63 results.add(Self::test_pr_event_in_purgatory_git_push_accepted(client).await);
@@ -92,9 +92,12 @@ impl PurgatoryTests {
92 .run(|| async { 92 .run(|| async {
93 let ctx = TestContext::new(client); 93 let ctx = TestContext::new(client);
94 94
95 // Create a fresh repo announcement (not the served variant) 95 // Use the purgatory-specific fixture which creates its own independent repo.
96 // The shared ValidRepoSent may already be promoted (served) by the time this
97 // test runs if earlier specs triggered OwnerStateDataPushed. PurgatoryValidRepoSent
98 // is never promoted by any other test so the announcement stays in purgatory.
96 let repo = ctx 99 let repo = ctx
97 .get_fixture(FixtureKind::ValidRepoSent) 100 .get_fixture(FixtureKind::PurgatoryValidRepoSent)
98 .await 101 .await
99 .map_err(|e| format!("Failed to create repo announcement: {}", e))?; 102 .map_err(|e| format!("Failed to create repo announcement: {}", e))?;
100 103
@@ -106,7 +109,7 @@ impl PurgatoryTests {
106 .ok_or("Missing d tag in repo announcement")? 109 .ok_or("Missing d tag in repo announcement")?
107 .to_string(); 110 .to_string();
108 111
109 // Query for the announcement - should NOT be served 112 // Query for the announcement - should NOT be served (purgatory)
110 let filter = Filter::new() 113 let filter = Filter::new()
111 .kind(Kind::GitRepoAnnouncement) 114 .kind(Kind::GitRepoAnnouncement)
112 .author(client.public_key()) 115 .author(client.public_key())
@@ -153,13 +156,13 @@ impl PurgatoryTests {
153 .run(|| async { 156 .run(|| async {
154 let ctx = TestContext::new(client); 157 let ctx = TestContext::new(client);
155 158
156 // OwnerStateDataPushed fixture handles the full lifecycle: 159 // PurgatoryOwnerStateDataPushed fixture handles the full lifecycle:
157 // 1. Creates repo announcement (purgatory) 160 // 1. Creates repo announcement (purgatory)
158 // 2. Creates state event (purgatory) 161 // 2. Creates state event (purgatory)
159 // 3. Pushes git data 162 // 3. Pushes git data
160 // 4. Verifies events are served 163 // 4. Verifies events are served
161 let state_event = ctx 164 let state_event = ctx
162 .get_fixture(FixtureKind::OwnerStateDataPushed) 165 .get_fixture(FixtureKind::PurgatoryOwnerStateDataPushed)
163 .await 166 .await
164 .map_err(|e| format!("Failed to complete full lifecycle: {}", e))?; 167 .map_err(|e| format!("Failed to complete full lifecycle: {}", e))?;
165 168
@@ -190,18 +193,16 @@ impl PurgatoryTests {
190 )); 193 ));
191 } 194 }
192 195
193 // Verify state event is served 196 // Verify state event is served by querying its specific event ID.
194 let state_filter = Filter::new() 197 // We intentionally query by ID rather than kind+author+identifier because
195 .kind(Kind::RepoState) 198 // other tests (e.g. push-auth) may have sent a newer replaceable state event
196 .author(client.public_key()) 199 // for the same repo_id, which would displace this one in an identifier query.
197 .identifier(&repo_id); 200 let served = client
198 201 .is_event_on_relay(state_event.id)
199 let state_events = client
200 .query(state_filter)
201 .await 202 .await
202 .map_err(|e| format!("Failed to query state events: {}", e))?; 203 .map_err(|e| format!("Failed to query state event: {}", e))?;
203 204
204 if !state_events.iter().any(|e| e.id == state_event.id) { 205 if !served {
205 return Err(format!( 206 return Err(format!(
206 "State event not served after git push. Event ID: {}", 207 "State event not served after git push. Event ID: {}",
207 state_event.id 208 state_event.id
@@ -234,9 +235,9 @@ impl PurgatoryTests {
234 .run(|| async { 235 .run(|| async {
235 let ctx = TestContext::new(client); 236 let ctx = TestContext::new(client);
236 237
237 // Get a repo announcement (in purgatory, no git data yet) 238 // Get the purgatory-specific repo announcement (never promoted by other tests)
238 let repo = ctx 239 let repo = ctx
239 .get_fixture(FixtureKind::ValidRepoSent) 240 .get_fixture(FixtureKind::PurgatoryValidRepoSent)
240 .await 241 .await
241 .map_err(|e| format!("Failed to create repo announcement: {}", e))?; 242 .map_err(|e| format!("Failed to create repo announcement: {}", e))?;
242 243
@@ -314,9 +315,9 @@ impl PurgatoryTests {
314 .run(|| async { 315 .run(|| async {
315 let ctx = TestContext::new(client); 316 let ctx = TestContext::new(client);
316 317
317 // Get a repo announcement (in purgatory) 318 // Get the purgatory-specific repo announcement (never promoted by other tests)
318 let repo = ctx 319 let repo = ctx
319 .get_fixture(FixtureKind::ValidRepoSent) 320 .get_fixture(FixtureKind::PurgatoryValidRepoSent)
320 .await 321 .await
321 .map_err(|e| format!("Failed to create repo announcement: {}", e))?; 322 .map_err(|e| format!("Failed to create repo announcement: {}", e))?;
322 323
@@ -407,11 +408,13 @@ impl PurgatoryTests {
407 .run(|| async { 408 .run(|| async {
408 let ctx = TestContext::new(client); 409 let ctx = TestContext::new(client);
409 410
410 // Get a repo with git data already pushed 411 // Use the isolated purgatory repo so this test's new state event
412 // does not displace the shared OwnerStateDataPushed state event
413 // that push-authorization tests depend on.
411 let existing_state = ctx 414 let existing_state = ctx
412 .get_fixture(FixtureKind::OwnerStateDataPushed) 415 .get_fixture(FixtureKind::PurgatoryOwnerStateDataPushed)
413 .await 416 .await
414 .map_err(|e| format!("Failed to get existing repo: {}", e))?; 417 .map_err(|e| format!("Failed to get purgatory test repo: {}", e))?;
415 418
416 let repo_id = existing_state 419 let repo_id = existing_state
417 .tags 420 .tags
@@ -461,7 +464,7 @@ impl PurgatoryTests {
461 /// Spec: GRASP-01 Line 22 464 /// Spec: GRASP-01 Line 22
462 /// "...kept in purgatory (not served) until the related git data arrives" 465 /// "...kept in purgatory (not served) until the related git data arrives"
463 /// 466 ///
464 /// This test verifies the full lifecycle using OwnerStateDataPushed fixture: 467 /// This test verifies the full lifecycle using PurgatoryOwnerStateDataPushed fixture:
465 /// 1. State event is sent (enters purgatory) 468 /// 1. State event is sent (enters purgatory)
466 /// 2. Git data is pushed matching the state event 469 /// 2. Git data is pushed matching the state event
467 /// 3. State event is now served 470 /// 3. State event is now served
@@ -474,32 +477,22 @@ impl PurgatoryTests {
474 .run(|| async { 477 .run(|| async {
475 let ctx = TestContext::new(client); 478 let ctx = TestContext::new(client);
476 479
477 // OwnerStateDataPushed handles the full lifecycle 480 // PurgatoryOwnerStateDataPushed handles the full lifecycle
478 let state_event = ctx 481 let state_event = ctx
479 .get_fixture(FixtureKind::OwnerStateDataPushed) 482 .get_fixture(FixtureKind::PurgatoryOwnerStateDataPushed)
480 .await 483 .await
481 .map_err(|e| format!("Failed to complete full lifecycle: {}", e))?; 484 .map_err(|e| format!("Failed to complete full lifecycle: {}", e))?;
482 485
483 // Verify state event is now served 486 // Verify state event is served by querying its specific event ID.
484 let repo_id = state_event 487 // We intentionally query by ID rather than kind+author+identifier because
485 .tags 488 // other tests (e.g. push-auth) may have sent a newer replaceable state event
486 .iter() 489 // for the same repo_id, which would displace this one in an identifier query.
487 .find(|t| t.kind() == TagKind::d()) 490 let served = client
488 .and_then(|t| t.content()) 491 .is_event_on_relay(state_event.id)
489 .ok_or("Missing d tag in state event")?
490 .to_string();
491
492 let filter = Filter::new()
493 .kind(Kind::RepoState)
494 .author(client.public_key())
495 .identifier(&repo_id);
496
497 let events = client
498 .query(filter)
499 .await 492 .await
500 .map_err(|e| format!("Failed to query state events: {}", e))?; 493 .map_err(|e| format!("Failed to query state event: {}", e))?;
501 494
502 if !events.iter().any(|e| e.id == state_event.id) { 495 if !served {
503 return Err(format!( 496 return Err(format!(
504 "State event not served after git push. Event ID: {}", 497 "State event not served after git push. Event ID: {}",
505 state_event.id 498 state_event.id
@@ -665,7 +658,7 @@ impl PurgatoryTests {
665 /// each referencing an event the author is requesting to be deleted." 658 /// each referencing an event the author is requesting to be deleted."
666 /// 659 ///
667 /// This test verifies: 660 /// This test verifies:
668 /// 1. Get a promoted repo (OwnerStateDataPushed) so git pushes are possible 661 /// 1. Get a promoted repo (PurgatoryOwnerStateDataPushed) so git pushes are possible
669 /// 2. Clone the repo and create a unique commit (not yet pushed) 662 /// 2. Clone the repo and create a unique commit (not yet pushed)
670 /// 3. Submit a state event pointing to that unique commit (enters purgatory) 663 /// 3. Submit a state event pointing to that unique commit (enters purgatory)
671 /// 4. Send a kind 5 deletion event referencing the state event by event ID 664 /// 4. Send a kind 5 deletion event referencing the state event by event ID
@@ -681,11 +674,12 @@ impl PurgatoryTests {
681 .run(|| async { 674 .run(|| async {
682 let ctx = TestContext::new(client); 675 let ctx = TestContext::new(client);
683 676
684 // Stage 1: get a promoted repo with git data already on the relay 677 // Stage 1: get the isolated purgatory repo (independent from the shared
678 // OwnerStateDataPushed chain that push-authorization tests depend on)
685 let existing_state = ctx 679 let existing_state = ctx
686 .get_fixture(FixtureKind::OwnerStateDataPushed) 680 .get_fixture(FixtureKind::PurgatoryOwnerStateDataPushed)
687 .await 681 .await
688 .map_err(|e| format!("Failed to get promoted repo: {}", e))?; 682 .map_err(|e| format!("Failed to get purgatory test repo: {}", e))?;
689 683
690 let repo_id = existing_state 684 let repo_id = existing_state
691 .tags 685 .tags
@@ -788,7 +782,7 @@ impl PurgatoryTests {
788 /// event up to the `created_at` timestamp of the deletion request event." 782 /// event up to the `created_at` timestamp of the deletion request event."
789 /// 783 ///
790 /// This test verifies: 784 /// This test verifies:
791 /// 1. Get a promoted repo (OwnerStateDataPushed) so git pushes are possible 785 /// 1. Get a promoted repo (PurgatoryOwnerStateDataPushed) so git pushes are possible
792 /// 2. Generate a fresh keypair for a new maintainer 786 /// 2. Generate a fresh keypair for a new maintainer
793 /// 3. Send a replacement owner announcement adding the new maintainer (goes to DB) 787 /// 3. Send a replacement owner announcement adding the new maintainer (goes to DB)
794 /// 4. Send a state event signed by the new maintainer pointing to a unique commit 788 /// 4. Send a state event signed by the new maintainer pointing to a unique commit
@@ -807,11 +801,14 @@ impl PurgatoryTests {
807 .run(|| async { 801 .run(|| async {
808 let ctx = TestContext::new(client); 802 let ctx = TestContext::new(client);
809 803
810 // Stage 1: get a promoted repo with git data already on the relay 804 // Stage 1: get the isolated purgatory repo (independent from the shared
805 // OwnerStateDataPushed chain that push-authorization tests depend on).
806 // This test sends a replacement announcement (kind 30617) for the repo which
807 // would corrupt the shared repo's maintainer set if we used OwnerStateDataPushed.
811 let existing_state = ctx 808 let existing_state = ctx
812 .get_fixture(FixtureKind::OwnerStateDataPushed) 809 .get_fixture(FixtureKind::PurgatoryOwnerStateDataPushed)
813 .await 810 .await
814 .map_err(|e| format!("Failed to get promoted repo: {}", e))?; 811 .map_err(|e| format!("Failed to get purgatory test repo: {}", e))?;
815 812
816 let repo_id = existing_state 813 let repo_id = existing_state
817 .tags 814 .tags