upleb.uk

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

summaryrefslogtreecommitdiff
path: root/grasp-audit/src
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-12-23 15:20:00 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-12-23 15:25:57 +0000
commitac3b71ba65bb52b2d85f1ab9f6c00316a1a885ab (patch)
tree4127a1265dfac6136a75ea241af5579f023c5b0e /grasp-audit/src
parentea3f90d3952f0a1760dbb03ff4a706731534514c (diff)
test: prepare OwnerStateDataPushed fixture for purgatory
This is the model for how to prepare all push tests for purgatory
Diffstat (limited to 'grasp-audit/src')
-rw-r--r--grasp-audit/src/client.rs45
-rw-r--r--grasp-audit/src/fixtures.rs51
-rw-r--r--grasp-audit/src/specs/grasp01/push_authorization.rs7
3 files changed, 82 insertions, 21 deletions
diff --git a/grasp-audit/src/client.rs b/grasp-audit/src/client.rs
index 259a317..5995483 100644
--- a/grasp-audit/src/client.rs
+++ b/grasp-audit/src/client.rs
@@ -2,7 +2,7 @@
2 2
3use crate::audit::{AuditConfig, AuditEventBuilder, AuditMode}; 3use crate::audit::{AuditConfig, AuditEventBuilder, AuditMode};
4use crate::fixtures::FixtureKind; 4use crate::fixtures::FixtureKind;
5use anyhow::{anyhow, Result}; 5use anyhow::{anyhow, Context, Result};
6use nostr_sdk::prelude::*; 6use nostr_sdk::prelude::*;
7use std::collections::HashMap; 7use std::collections::HashMap;
8use std::sync::{Arc, Mutex}; 8use std::sync::{Arc, Mutex};
@@ -181,6 +181,49 @@ impl AuditClient {
181 Ok(event_id) 181 Ok(event_id)
182 } 182 }
183 183
184 /// Send an event (with audit tags automatically added) and expect OK, success, 'purgatory:', verify not served - TODO the msg verificaiton is not implemented
185 pub async fn send_event_expect_purgatory_not_served(&self, event: Event) -> Result<EventId> {
186 if self.config.read_only {
187 return Err(anyhow!("Client is in read-only mode"));
188 }
189
190 let output = self.client.send_event(&event).await?;
191 let event_id = *output.id();
192
193 // Check if any relay rejected the event and return the error message
194 if !output.failed.is_empty() {
195 // Get the first failed relay error message
196 let (relay_url, error) = output.failed.iter().next().unwrap();
197 return Err(anyhow!("Relay {} rejected event: {}", relay_url, error));
198 }
199
200 // Wait a bit for event to propagate
201 tokio::time::sleep(Duration::from_millis(300)).await;
202
203 // ------------------------------------------------------
204 // TODO Magically enable purgatory by uncommenting this:
205 // ------------------------------------------------------
206 // ------------------------------------------------------
207 // if !self.is_event_on_relay(event.id).await? {
208 // return Err(anyhow!(
209 // "event sent to relay was served instead of being put in purgatory"
210 // ));
211 // }
212 // ------------------------------------------------------
213
214 Ok(event_id)
215 }
216
217 /// check if an event is on the relay
218 pub async fn is_event_on_relay(&self, id: EventId) -> Result<bool> {
219 Ok(!self
220 .client
221 .fetch_events(vec![Filter::new().id(id)], Duration::from_secs(1))
222 .await
223 .context("error trying to query relay for event")?
224 .is_empty())
225 }
226
184 /// Create an event builder that automatically includes audit tags 227 /// Create an event builder that automatically includes audit tags
185 /// 228 ///
186 /// All events built through this method will automatically have audit tags appended 229 /// All events built through this method will automatically have audit tags appended
diff --git a/grasp-audit/src/fixtures.rs b/grasp-audit/src/fixtures.rs
index 62f93e8..c5eb5f8 100644
--- a/grasp-audit/src/fixtures.rs
+++ b/grasp-audit/src/fixtures.rs
@@ -214,11 +214,12 @@ pub enum FixtureKind {
214 214
215 /// Owner's state event with git data successfully pushed (full 4-stage fixture) 215 /// Owner's state event with git data successfully pushed (full 4-stage fixture)
216 /// 216 ///
217 /// This fixture represents the complete flow for testing push authorization: 217 /// This fixture represents the complete flow for testing state push authorization:
218 /// 1. **Generated**: Creates RepoState (repo announcement + state event) 218 /// 1. **Generated**: Creates RepoState (repo announcement + state event)
219 /// 2. **Sent**: Sends events to relay 219 /// 2. **Sent**: Sends events to relay (returns OK, accepted but 'purgatory:...' message)
220 /// 3. **Verified**: Confirms events accepted by relay 220 /// 3. **Verify Not Served**: Confirms event is not served by relays
221 /// 4. **DataPushed**: Clones repo, creates deterministic commit, pushes to relay 221 /// 4. **DataPushed**: Clones repo, creates deterministic commit, pushes to relay
222 /// 5. **Verified**: Confirms event is served by relay
222 /// 223 ///
223 /// - Requires ValidRepo (uses same repo_id) 224 /// - Requires ValidRepo (uses same repo_id)
224 /// - State event signed by owner keys (`client.keys()`) 225 /// - State event signed by owner keys (`client.keys()`)
@@ -867,9 +868,10 @@ impl<'a> TestContext<'a> {
867 /// 868 ///
868 /// This handles all stages of the fixture: 869 /// This handles all stages of the fixture:
869 /// 1. **Generated**: Creates RepoState (repo announcement + state event) 870 /// 1. **Generated**: Creates RepoState (repo announcement + state event)
870 /// 2. **Sent**: Sends events to relay 871 /// 2. **Sent**: Sends events to relay (returns OK, accepted but 'purgatory:...' message)
871 /// 3. **Verified**: Confirms events accepted by relay 872 /// 3. **Verify Not Served**: Confirms event is not served by relays
872 /// 4. **DataPushed**: Clones repo, creates deterministic commit, pushes to relay 873 /// 4. **DataPushed**: Clones repo, creates deterministic commit, pushes to relay
874 /// 5. **Verified**: Confirms event is served by relay
873 /// 875 ///
874 /// # Returns 876 /// # Returns
875 /// The state event (kind 30618) after all stages complete successfully 877 /// The state event (kind 30618) after all stages complete successfully
@@ -877,7 +879,7 @@ impl<'a> TestContext<'a> {
877 use nostr_sdk::prelude::*; 879 use nostr_sdk::prelude::*;
878 880
879 // ============================================================ 881 // ============================================================
880 // Stage 1 & 2: ValidRepo is ensured by ensure_fixture before this is called 882 // Stage 1: ValidRepo is ensured by ensure_fixture before this is called
881 // ============================================================ 883 // ============================================================
882 let repo = self.get_cached_dependency(FixtureKind::ValidRepo)?; 884 let repo = self.get_cached_dependency(FixtureKind::ValidRepo)?;
883 let repo_id = self.extract_repo_id(&repo)?; 885 let repo_id = self.extract_repo_id(&repo)?;
@@ -902,13 +904,12 @@ impl<'a> TestContext<'a> {
902 .build(self.client.keys()) 904 .build(self.client.keys())
903 .map_err(|e| anyhow::anyhow!("Failed to build state announcement: {}", e))?; 905 .map_err(|e| anyhow::anyhow!("Failed to build state announcement: {}", e))?;
904 906
905 // Send state event to relay
906 self.client.send_event(state_event.clone()).await?;
907
908 // ============================================================ 907 // ============================================================
909 // Stage 3: Verify state event was accepted 908 // Stage 2 & 3: Send to Relay, get Accepted response and Verify its Not Served
910 // ============================================================ 909 // ============================================================
911 tokio::time::sleep(std::time::Duration::from_millis(200)).await; 910 self.client
911 .send_event_expect_purgatory_not_served(state_event.clone())
912 .await?;
912 913
913 // ============================================================ 914 // ============================================================
914 // Stage 4: DataPushed - Clone repo, create commit, push 915 // Stage 4: DataPushed - Clone repo, create commit, push
@@ -1000,14 +1001,29 @@ impl<'a> TestContext<'a> {
1000 cleanup(&clone_path); 1001 cleanup(&clone_path);
1001 1002
1002 match push_result { 1003 match push_result {
1003 Ok(true) => Ok(state_event), 1004 Ok(res) => {
1004 Ok(false) => Err(anyhow::anyhow!( 1005 if !res {
1005 "Push was rejected but should have been accepted. \ 1006 return Err(anyhow::anyhow!(
1007 "Push was rejected but should have been accepted. \
1006 The state event points to commit {} which matches the pushed commit.", 1008 The state event points to commit {} which matches the pushed commit.",
1007 DETERMINISTIC_COMMIT_HASH 1009 DETERMINISTIC_COMMIT_HASH
1008 )), 1010 ));
1009 Err(e) => Err(anyhow::anyhow!("Push error: {}", e)), 1011 }
1012 }
1013 Err(e) => return Err(anyhow::anyhow!("Push error: {}", e)),
1014 }
1015
1016 // ============================================================
1017 // Stage 5: Verify state event is on relay
1018 // ============================================================
1019
1020 tokio::time::sleep(Duration::from_millis(200)).await;
1021
1022 if !self.client.is_event_on_relay(state_event.id).await? {
1023 return Err(anyhow::anyhow!("state event not released from purgatory"));
1010 } 1024 }
1025
1026 Ok(state_event)
1011 } 1027 }
1012 1028
1013 /// Build MaintainerStateDataPushed fixture: full 4-stage fixture for maintainer push authorization 1029 /// Build MaintainerStateDataPushed fixture: full 4-stage fixture for maintainer push authorization
@@ -1667,6 +1683,7 @@ pub async fn send_and_verify_rejected(
1667use std::fs; 1683use std::fs;
1668use std::path::{Path, PathBuf}; 1684use std::path::{Path, PathBuf};
1669use std::process::Command; 1685use std::process::Command;
1686use std::time::Duration;
1670 1687
1671/// Clone a repository from the relay and return the path 1688/// Clone a repository from the relay and return the path
1672/// 1689///
diff --git a/grasp-audit/src/specs/grasp01/push_authorization.rs b/grasp-audit/src/specs/grasp01/push_authorization.rs
index 4f21e70..6726fe5 100644
--- a/grasp-audit/src/specs/grasp01/push_authorization.rs
+++ b/grasp-audit/src/specs/grasp01/push_authorization.rs
@@ -486,9 +486,10 @@ impl PushAuthorizationTests {
486 /// 486 ///
487 /// This test uses the OwnerStateDataPushed fixture which handles all 4 stages: 487 /// This test uses the OwnerStateDataPushed fixture which handles all 4 stages:
488 /// 1. **Generated**: Creates RepoState (repo announcement + state event) 488 /// 1. **Generated**: Creates RepoState (repo announcement + state event)
489 /// 2. **Sent**: Sends events to relay 489 /// 2. **Sent**: Sends events to relay (returns OK, accepted but 'purgatory:...' message)
490 /// 3. **Verified**: Confirms events accepted by relay 490 /// 3. **Verify Not Served**: Confirms event is not served by relays
491 /// 4. **DataPushed**: Clones repo, creates deterministic commit, pushes to relay 491 /// 4. **DataPushed**: Clones repo, creates deterministic commit, pushes to relay
492 /// 5. **Verified**: Confirms event is served by relay
492 /// 493 ///
493 /// The test wraps the fixture result in pass/fail using the error message. 494 /// The test wraps the fixture result in pass/fail using the error message.
494 #[allow(unused_variables)] // relay_domain is now handled by fixture 495 #[allow(unused_variables)] // relay_domain is now handled by fixture
@@ -504,7 +505,7 @@ impl PushAuthorizationTests {
504 match ctx.get_fixture(FixtureKind::OwnerStateDataPushed).await { 505 match ctx.get_fixture(FixtureKind::OwnerStateDataPushed).await {
505 Ok(_state_event) => TestResult::new( 506 Ok(_state_event) => TestResult::new(
506 test_name, 507 test_name,
507 "GRASP-01:git-http:36", 508 "GRASP-01:git-http:36", // TODO do we add purgatory line here?
508 "Push authorized with matching state", 509 "Push authorized with matching state",
509 ) 510 )
510 .pass(), 511 .pass(),