diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-12-23 15:20:00 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-12-23 15:25:57 +0000 |
| commit | ac3b71ba65bb52b2d85f1ab9f6c00316a1a885ab (patch) | |
| tree | 4127a1265dfac6136a75ea241af5579f023c5b0e /grasp-audit | |
| parent | ea3f90d3952f0a1760dbb03ff4a706731534514c (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')
| -rw-r--r-- | grasp-audit/src/client.rs | 45 | ||||
| -rw-r--r-- | grasp-audit/src/fixtures.rs | 51 | ||||
| -rw-r--r-- | grasp-audit/src/specs/grasp01/push_authorization.rs | 7 |
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 | ||
| 3 | use crate::audit::{AuditConfig, AuditEventBuilder, AuditMode}; | 3 | use crate::audit::{AuditConfig, AuditEventBuilder, AuditMode}; |
| 4 | use crate::fixtures::FixtureKind; | 4 | use crate::fixtures::FixtureKind; |
| 5 | use anyhow::{anyhow, Result}; | 5 | use anyhow::{anyhow, Context, Result}; |
| 6 | use nostr_sdk::prelude::*; | 6 | use nostr_sdk::prelude::*; |
| 7 | use std::collections::HashMap; | 7 | use std::collections::HashMap; |
| 8 | use std::sync::{Arc, Mutex}; | 8 | use 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( | |||
| 1667 | use std::fs; | 1683 | use std::fs; |
| 1668 | use std::path::{Path, PathBuf}; | 1684 | use std::path::{Path, PathBuf}; |
| 1669 | use std::process::Command; | 1685 | use std::process::Command; |
| 1686 | use 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(), |