diff options
Diffstat (limited to 'grasp-audit/src/fixtures.rs')
| -rw-r--r-- | grasp-audit/src/fixtures.rs | 110 |
1 files changed, 67 insertions, 43 deletions
diff --git a/grasp-audit/src/fixtures.rs b/grasp-audit/src/fixtures.rs index 9a00aef..fc6e8cb 100644 --- a/grasp-audit/src/fixtures.rs +++ b/grasp-audit/src/fixtures.rs | |||
| @@ -154,6 +154,22 @@ pub enum FixtureKind { | |||
| 154 | /// - Timestamp: 10 seconds in the past | 154 | /// - Timestamp: 10 seconds in the past |
| 155 | RepoState, | 155 | RepoState, |
| 156 | 156 | ||
| 157 | /// Owner's repository state announcement (kind 30618) sent to relay and accepted into purgatory | ||
| 158 | /// | ||
| 159 | /// This is the "sent" stage: the state event has been published to the relay and | ||
| 160 | /// accepted (OK response), but no git data has been pushed yet so it remains in | ||
| 161 | /// purgatory and is not served to clients. | ||
| 162 | /// | ||
| 163 | /// Use this when you need the state event to exist on the relay but do not need | ||
| 164 | /// the full push/serve cycle. For the complete cycle (git pushed + verified served), | ||
| 165 | /// use `OwnerStateDataPushed`. | ||
| 166 | /// | ||
| 167 | /// - Requires ValidRepoSent (uses same repo_id) | ||
| 168 | /// - Signed by owner keys (`client.keys()`) | ||
| 169 | /// - Points to DETERMINISTIC_COMMIT_HASH | ||
| 170 | /// - Timestamp: 10 seconds in the past | ||
| 171 | OwnerRepoStateSent, | ||
| 172 | |||
| 157 | /// PR (Pull Request) event for the SAME repo_id as ValidRepoServed | 173 | /// PR (Pull Request) event for the SAME repo_id as ValidRepoServed |
| 158 | /// - Requires ValidRepoServed (uses same repo_id, needs queryable repo) | 174 | /// - Requires ValidRepoServed (uses same repo_id, needs queryable repo) |
| 159 | /// - Signed by `client.pr_author_keys()` | 175 | /// - Signed by `client.pr_author_keys()` |
| @@ -343,6 +359,8 @@ impl FixtureKind { | |||
| 343 | // Fixtures that depend on ValidRepoServed (need queryable announcement) | 359 | // Fixtures that depend on ValidRepoServed (need queryable announcement) |
| 344 | Self::RepoWithIssue => vec![Self::ValidRepoServed], | 360 | Self::RepoWithIssue => vec![Self::ValidRepoServed], |
| 345 | Self::RepoState => vec![Self::ValidRepoSent], | 361 | Self::RepoState => vec![Self::ValidRepoSent], |
| 362 | // OwnerRepoStateSent depends on ValidRepoSent: state event sent, sitting in purgatory | ||
| 363 | Self::OwnerRepoStateSent => vec![Self::ValidRepoSent], | ||
| 346 | Self::PREvent => vec![Self::ValidRepoServed], | 364 | Self::PREvent => vec![Self::ValidRepoServed], |
| 347 | Self::PREventGenerated => vec![Self::ValidRepoServed], | 365 | Self::PREventGenerated => vec![Self::ValidRepoServed], |
| 348 | Self::PRWrongCommitPushedBeforeEvent => vec![Self::PREventGenerated], | 366 | Self::PRWrongCommitPushedBeforeEvent => vec![Self::PREventGenerated], |
| @@ -354,7 +372,8 @@ impl FixtureKind { | |||
| 354 | Self::PREvent2GitDataPushed => vec![Self::PREvent2Sent], | 372 | Self::PREvent2GitDataPushed => vec![Self::PREvent2Sent], |
| 355 | Self::PREvent2Served => vec![Self::PREvent2GitDataPushed], | 373 | Self::PREvent2Served => vec![Self::PREvent2GitDataPushed], |
| 356 | 374 | ||
| 357 | Self::OwnerStateDataPushed => vec![Self::ValidRepoSent], | 375 | // OwnerStateDataPushed depends on OwnerRepoStateSent (git push + purgatory release) |
| 376 | Self::OwnerStateDataPushed => vec![Self::OwnerRepoStateSent], | ||
| 358 | 377 | ||
| 359 | // Fixtures that depend on RepoWithIssue | 378 | // Fixtures that depend on RepoWithIssue |
| 360 | Self::RepoWithComment => vec![Self::RepoWithIssue], | 379 | Self::RepoWithComment => vec![Self::RepoWithIssue], |
| @@ -399,6 +418,8 @@ impl FixtureKind { | |||
| 399 | Self::HeadSetToDevelopBranch => true, | 418 | Self::HeadSetToDevelopBranch => true, |
| 400 | // ValidRepoServed doesn't send anything itself, just returns cached event | 419 | // ValidRepoServed doesn't send anything itself, just returns cached event |
| 401 | Self::ValidRepoServed => true, | 420 | Self::ValidRepoServed => true, |
| 421 | // OwnerRepoStateSent sends its state event and notes purgatory internally | ||
| 422 | Self::OwnerRepoStateSent => true, | ||
| 402 | // All other fixtures return a single event for the caller to send | 423 | // All other fixtures return a single event for the caller to send |
| 403 | _ => false, | 424 | _ => false, |
| 404 | } | 425 | } |
| @@ -774,6 +795,40 @@ impl<'a> TestContext<'a> { | |||
| 774 | .map_err(|e| anyhow::anyhow!("Failed to build state announcement: {}", e)) | 795 | .map_err(|e| anyhow::anyhow!("Failed to build state announcement: {}", e)) |
| 775 | } | 796 | } |
| 776 | 797 | ||
| 798 | FixtureKind::OwnerRepoStateSent => { | ||
| 799 | use nostr_sdk::prelude::*; | ||
| 800 | |||
| 801 | // ValidRepoSent is ensured by ensure_fixture before this is called | ||
| 802 | let repo = self.get_cached_dependency(FixtureKind::ValidRepoSent)?; | ||
| 803 | let repo_id = self.extract_repo_id(&repo)?; | ||
| 804 | |||
| 805 | let base_time = Timestamp::now().as_secs(); | ||
| 806 | let older_timestamp = Timestamp::from(base_time - 10); | ||
| 807 | |||
| 808 | let state_event = self | ||
| 809 | .client | ||
| 810 | .event_builder(Kind::RepoState, "") | ||
| 811 | .tag(Tag::identifier(&repo_id)) | ||
| 812 | .tag(Tag::custom( | ||
| 813 | TagKind::custom("refs/heads/main"), | ||
| 814 | vec![DETERMINISTIC_COMMIT_HASH.to_string()], | ||
| 815 | )) | ||
| 816 | .tag(Tag::custom( | ||
| 817 | TagKind::custom("HEAD"), | ||
| 818 | vec!["ref: refs/heads/main".to_string()], | ||
| 819 | )) | ||
| 820 | .custom_time(older_timestamp) | ||
| 821 | .build(self.client.keys()) | ||
| 822 | .map_err(|e| anyhow::anyhow!("Failed to build state announcement: {}", e))?; | ||
| 823 | |||
| 824 | // Send to relay - event will be accepted but held in purgatory (no git data yet) | ||
| 825 | self.client | ||
| 826 | .send_event_and_note_purgatory(state_event.clone()) | ||
| 827 | .await?; | ||
| 828 | |||
| 829 | Ok(state_event) | ||
| 830 | } | ||
| 831 | |||
| 777 | FixtureKind::PREvent => { | 832 | FixtureKind::PREvent => { |
| 778 | use nostr_sdk::prelude::*; | 833 | use nostr_sdk::prelude::*; |
| 779 | 834 | ||
| @@ -945,57 +1000,26 @@ impl<'a> TestContext<'a> { | |||
| 945 | .ok_or_else(|| anyhow::anyhow!("Missing d tag in repo announcement")) | 1000 | .ok_or_else(|| anyhow::anyhow!("Missing d tag in repo announcement")) |
| 946 | } | 1001 | } |
| 947 | 1002 | ||
| 948 | /// Build OwnerStateDataPushed fixture: full 4-stage fixture for push authorization | 1003 | /// Build OwnerStateDataPushed fixture: git push + purgatory release for owner's state event |
| 949 | /// | 1004 | /// |
| 950 | /// This handles all stages of the fixture: | 1005 | /// `OwnerRepoStateSent` is ensured as a dependency before this is called — the state event |
| 951 | /// 1. **Generated**: Creates RepoState (repo announcement + state event) | 1006 | /// is already on the relay in purgatory. This fixture completes the cycle: |
| 952 | /// 2. **Sent**: Sends events to relay (returns OK, accepted but 'purgatory:...' message) | 1007 | /// 1. **DataPushed**: Clones repo, creates deterministic commit, pushes to relay |
| 953 | /// 3. **Verify Not Served**: Confirms event is not served by relays | 1008 | /// 2. **Verified**: Confirms state event is released from purgatory and served |
| 954 | /// 4. **DataPushed**: Clones repo, creates deterministic commit, pushes to relay | ||
| 955 | /// 5. **Verified**: Confirms event is served by relay | ||
| 956 | /// | 1009 | /// |
| 957 | /// # Returns | 1010 | /// # Returns |
| 958 | /// The state event (kind 30618) after all stages complete successfully | 1011 | /// The state event (kind 30618) after git data is pushed and purgatory is released |
| 959 | async fn build_owner_state_data_pushed(&self) -> Result<Event> { | 1012 | async fn build_owner_state_data_pushed(&self) -> Result<Event> { |
| 960 | use nostr_sdk::prelude::*; | 1013 | use nostr_sdk::prelude::*; |
| 961 | 1014 | ||
| 962 | // ============================================================ | 1015 | // OwnerRepoStateSent is ensured by ensure_fixture before this is called. |
| 963 | // Stage 1: ValidRepoSent is ensured by ensure_fixture before this is called | 1016 | // The state event is already on the relay in purgatory - retrieve it from cache. |
| 964 | // ============================================================ | 1017 | let state_event = self.get_cached_dependency(FixtureKind::OwnerRepoStateSent)?; |
| 965 | let repo = self.get_cached_dependency(FixtureKind::ValidRepoSent)?; | 1018 | let repo = self.get_cached_dependency(FixtureKind::ValidRepoSent)?; |
| 966 | let repo_id = self.extract_repo_id(&repo)?; | 1019 | let repo_id = self.extract_repo_id(&repo)?; |
| 967 | 1020 | ||
| 968 | // Build state event | ||
| 969 | let base_time = Timestamp::now().as_secs(); | ||
| 970 | let older_timestamp = Timestamp::from(base_time - 10); // 10 seconds ago | ||
| 971 | |||
| 972 | let state_event = self | ||
| 973 | .client | ||
| 974 | .event_builder(Kind::RepoState, "") | ||
| 975 | .tag(Tag::identifier(&repo_id)) | ||
| 976 | .tag(Tag::custom( | ||
| 977 | TagKind::custom("refs/heads/main"), | ||
| 978 | vec![DETERMINISTIC_COMMIT_HASH.to_string()], | ||
| 979 | )) | ||
| 980 | .tag(Tag::custom( | ||
| 981 | TagKind::custom("HEAD"), | ||
| 982 | vec!["ref: refs/heads/main".to_string()], | ||
| 983 | )) | ||
| 984 | .custom_time(older_timestamp) | ||
| 985 | .build(self.client.keys()) | ||
| 986 | .map_err(|e| anyhow::anyhow!("Failed to build state announcement: {}", e))?; | ||
| 987 | |||
| 988 | // ============================================================ | 1021 | // ============================================================ |
| 989 | // Stage 2 & 3: Send to Relay, get Accepted response and Verify its Not Served | 1022 | // Stage 1: DataPushed - Clone repo, create commit, push |
| 990 | // ============================================================ | ||
| 991 | let (_, _in_purgatory) = self | ||
| 992 | .client | ||
| 993 | .send_event_and_note_purgatory(state_event.clone()) | ||
| 994 | .await?; | ||
| 995 | // Note: We don't fail if purgatory wasn't observed - the fixture proceeds regardless | ||
| 996 | |||
| 997 | // ============================================================ | ||
| 998 | // Stage 4: DataPushed - Clone repo, create commit, push | ||
| 999 | // ============================================================ | 1023 | // ============================================================ |
| 1000 | 1024 | ||
| 1001 | // Get relay domain from connected relay | 1025 | // Get relay domain from connected relay |
| @@ -1097,7 +1121,7 @@ impl<'a> TestContext<'a> { | |||
| 1097 | } | 1121 | } |
| 1098 | 1122 | ||
| 1099 | // ============================================================ | 1123 | // ============================================================ |
| 1100 | // Stage 5: Verify state event is on relay | 1124 | // Stage 2: Verify state event is released from purgatory |
| 1101 | // ============================================================ | 1125 | // ============================================================ |
| 1102 | 1126 | ||
| 1103 | tokio::time::sleep(Duration::from_millis(200)).await; | 1127 | tokio::time::sleep(Duration::from_millis(200)).await; |