diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-12-01 23:16:34 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-12-01 23:18:12 +0000 |
| commit | 9a2e127663d8b3d776241472f4d769fb5d0ee27b (patch) | |
| tree | ca7be08cb852264dc10e6ef298da807e28f51caf /grasp-audit | |
| parent | 29ac0975a6a2f9e1cc585bd56a28b93205d0a2ac (diff) | |
better fixtures: clean up
Diffstat (limited to 'grasp-audit')
| -rw-r--r-- | grasp-audit/README.md | 16 | ||||
| -rw-r--r-- | grasp-audit/src/fixtures.rs | 332 |
2 files changed, 68 insertions, 280 deletions
diff --git a/grasp-audit/README.md b/grasp-audit/README.md index ab3bddc..2f4387c 100644 --- a/grasp-audit/README.md +++ b/grasp-audit/README.md | |||
| @@ -252,14 +252,14 @@ pub async fn test_something(client: &AuditClient) -> TestResult { | |||
| 252 | 252 | ||
| 253 | ### Available Fixtures | 253 | ### Available Fixtures |
| 254 | 254 | ||
| 255 | | FixtureKind | Provides | Use When | | 255 | | FixtureKind | Provides | Use When | |
| 256 | | ------------------------ | --------------------------------------- | ------------------------------------- | | 256 | | ------------------------------------ | --------------------------------------- | ------------------------------------- | |
| 257 | | `ValidRepo` | Accepted repo announcement (kind 30617) | Need a repo as prerequisite | | 257 | | `ValidRepo` | Accepted repo announcement (kind 30617) | Need a repo as prerequisite | |
| 258 | | `RepoState` | Repo + state event (kind 30618) | Testing owner push authorization | | 258 | | `RepoState` | Repo + state event (kind 30618) | Testing owner push authorization | |
| 259 | | `MaintainerAnnouncement` | Maintainer's repo announcement | Testing maintainer chain setup | | 259 | | `RepoWithIssue` | Repo + accepted issue (kind 1621) | Testing issue-dependent events | |
| 260 | | `MaintainerState` | Maintainer's state event | Testing maintainer push authorization | | 260 | | `RepoWithComment` | Repo + issue + comment | Testing comment-dependent events | |
| 261 | | `RepoWithIssue` | Repo + accepted issue (kind 1621) | Testing issue-dependent events | | 261 | | `MaintainerStateDataPushed` | Full maintainer push with git data | Testing maintainer push authorization | |
| 262 | | `RepoWithComment` | Repo + issue + comment | Testing comment-dependent events | | 262 | | `RecursiveMaintainerStateDataPushed` | Full recursive maintainer push | Testing recursive maintainer chain | |
| 263 | 263 | ||
| 264 | ### Fixture Lifecycle: Generate → Send → Verify | 264 | ### Fixture Lifecycle: Generate → Send → Verify |
| 265 | 265 | ||
diff --git a/grasp-audit/src/fixtures.rs b/grasp-audit/src/fixtures.rs index bda7a78..e48f6b9 100644 --- a/grasp-audit/src/fixtures.rs +++ b/grasp-audit/src/fixtures.rs | |||
| @@ -18,16 +18,8 @@ | |||
| 18 | //! when appropriate. | 18 | //! when appropriate. |
| 19 | //! | 19 | //! |
| 20 | //! # What is a Fixture? | 20 | //! # What is a Fixture? |
| 21 | //! A fixture represents the state of a repository on a grasp server and/or nostr events to be | 21 | //! A fixture represents the state of a repository on a grasp server (events and git refs) |
| 22 | //! sent to the server to change this state. | 22 | //! and/or nostr events to be sent to the server to change this state. |
| 23 | //! | ||
| 24 | //! 1. <event-name>Generated - Nostr Event created (not yet sent) | ||
| 25 | //! 2. <event-name>Sent - Sent To Grasp Server | ||
| 26 | //! 3. <event-name> - Verfied and Confirmed as accepted via client query | ||
| 27 | //! 4. <event-or-data-pushed-name>DataPushed - what refs were pushed | ||
| 28 | //! | ||
| 29 | //! Some Nostr Events need each of these stages as seperate fixtures whereas 1-3 or event 1-4 are often | ||
| 30 | //! bundled and 4 is only sometimes needed. | ||
| 31 | //! | 23 | //! |
| 32 | //! Nearly all fixures include dependant fixtures so tests dont need to call every parent fixture. | 24 | //! Nearly all fixures include dependant fixtures so tests dont need to call every parent fixture. |
| 33 | //! | 25 | //! |
| @@ -141,44 +133,6 @@ pub enum FixtureKind { | |||
| 141 | /// - Timestamp: 10 seconds in the past | 133 | /// - Timestamp: 10 seconds in the past |
| 142 | RepoState, | 134 | RepoState, |
| 143 | 135 | ||
| 144 | /// Maintainer's repo announcement only for the SAME repo_id as ValidRepo | ||
| 145 | /// - Requires ValidRepo (uses same repo_id for maintainer chain) | ||
| 146 | /// - Announcement signed by `client.maintainer_keys()` | ||
| 147 | /// - Lists `client.recursive_maintainer_pubkey_hex()` in maintainers tag | ||
| 148 | /// - Does NOT include state event (use MaintainerState for that) | ||
| 149 | MaintainerAnnouncement, | ||
| 150 | |||
| 151 | /// Maintainer's state event only for the SAME repo_id as ValidRepo | ||
| 152 | /// - Requires ValidRepo (uses same repo_id for maintainer chain) | ||
| 153 | /// - State event signed by `client.maintainer_keys()` | ||
| 154 | /// - Points to MAINTAINER_DETERMINISTIC_COMMIT_HASH | ||
| 155 | /// - Timestamp: 5 seconds in the past (more recent than owner's state) | ||
| 156 | /// - Does NOT include announcement (use MaintainerAnnouncement for that) | ||
| 157 | MaintainerState, | ||
| 158 | |||
| 159 | /// Recursive maintainer's announcement only for the SAME repo_id as ValidRepo | ||
| 160 | /// - Requires ValidRepo (uses same repo_id for recursive chain) | ||
| 161 | /// - Announcement signed by `client.recursive_maintainer_keys()` | ||
| 162 | /// - Lists owner and maintainer in maintainers tag | ||
| 163 | /// - Does NOT include state event (use RecursiveMaintainerState for that) | ||
| 164 | RecursiveMaintainerAnnouncement, | ||
| 165 | |||
| 166 | /// Recursive maintainer's state event only for the SAME repo_id as ValidRepo | ||
| 167 | /// - Requires ValidRepo (uses same repo_id for recursive chain) | ||
| 168 | /// - State event signed by `client.recursive_maintainer_keys()` | ||
| 169 | /// - Points to RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH | ||
| 170 | /// - Timestamp: 2 seconds in the past (most recent) | ||
| 171 | /// - Does NOT include announcement (use RecursiveMaintainerAnnouncement for that) | ||
| 172 | RecursiveMaintainerState, | ||
| 173 | |||
| 174 | /// Recursive maintainer's announcement + state for the SAME repo_id as ValidRepo | ||
| 175 | /// - Requires ValidRepo (uses same repo_id for recursive chain) | ||
| 176 | /// - Announcement signed by `client.recursive_maintainer_keys()` | ||
| 177 | /// - Lists owner and maintainer in maintainers tag | ||
| 178 | /// - State event points to RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH | ||
| 179 | /// - Timestamp: 2 seconds in the past (most recent) | ||
| 180 | RecursiveMaintainerRepoAndState, | ||
| 181 | |||
| 182 | /// PR (Pull Request) event for the SAME repo_id as ValidRepo | 136 | /// PR (Pull Request) event for the SAME repo_id as ValidRepo |
| 183 | /// - Requires ValidRepo (uses same repo_id) | 137 | /// - Requires ValidRepo (uses same repo_id) |
| 184 | /// - Signed by `client.pr_author_keys()` | 138 | /// - Signed by `client.pr_author_keys()` |
| @@ -322,32 +276,27 @@ impl FixtureKind { | |||
| 322 | match self { | 276 | match self { |
| 323 | // Base fixtures - no dependencies | 277 | // Base fixtures - no dependencies |
| 324 | Self::ValidRepo => vec![], | 278 | Self::ValidRepo => vec![], |
| 325 | 279 | ||
| 326 | // Fixtures that depend on ValidRepo | 280 | // Fixtures that depend on ValidRepo |
| 327 | Self::RepoWithIssue => vec![Self::ValidRepo], | 281 | Self::RepoWithIssue => vec![Self::ValidRepo], |
| 328 | Self::RepoState => vec![Self::ValidRepo], | 282 | Self::RepoState => vec![Self::ValidRepo], |
| 329 | Self::MaintainerAnnouncement => vec![Self::ValidRepo], | ||
| 330 | Self::MaintainerState => vec![Self::ValidRepo], | ||
| 331 | Self::RecursiveMaintainerAnnouncement => vec![Self::ValidRepo], | ||
| 332 | Self::RecursiveMaintainerState => vec![Self::ValidRepo], | ||
| 333 | Self::RecursiveMaintainerRepoAndState => vec![Self::ValidRepo], | ||
| 334 | Self::PREvent => vec![Self::ValidRepo], | 283 | Self::PREvent => vec![Self::ValidRepo], |
| 335 | Self::PREventGenerated => vec![Self::ValidRepo], | 284 | Self::PREventGenerated => vec![Self::ValidRepo], |
| 336 | Self::PRWrongCommitPushedBeforeEvent => vec![Self::PREventGenerated], | 285 | Self::PRWrongCommitPushedBeforeEvent => vec![Self::PREventGenerated], |
| 337 | Self::PREventSentAfterWrongPush => vec![Self::PRWrongCommitPushedBeforeEvent], | 286 | Self::PREventSentAfterWrongPush => vec![Self::PRWrongCommitPushedBeforeEvent], |
| 338 | Self::OwnerStateDataPushed => vec![Self::ValidRepo], | 287 | Self::OwnerStateDataPushed => vec![Self::ValidRepo], |
| 339 | 288 | ||
| 340 | // Fixtures that depend on RepoWithIssue | 289 | // Fixtures that depend on RepoWithIssue |
| 341 | Self::RepoWithComment => vec![Self::RepoWithIssue], | 290 | Self::RepoWithComment => vec![Self::RepoWithIssue], |
| 342 | 291 | ||
| 343 | // MaintainerStateDataPushed depends on OwnerStateDataPushed | 292 | // MaintainerStateDataPushed depends on OwnerStateDataPushed |
| 344 | // (maintainer force-pushes over owner's data) | 293 | // (maintainer force-pushes over owner's data) |
| 345 | Self::MaintainerStateDataPushed => vec![Self::OwnerStateDataPushed], | 294 | Self::MaintainerStateDataPushed => vec![Self::OwnerStateDataPushed], |
| 346 | 295 | ||
| 347 | // RecursiveMaintainerStateDataPushed depends on MaintainerStateDataPushed | 296 | // RecursiveMaintainerStateDataPushed depends on MaintainerStateDataPushed |
| 348 | // (recursive maintainer force-pushes over maintainer's data) | 297 | // (recursive maintainer force-pushes over maintainer's data) |
| 349 | Self::RecursiveMaintainerStateDataPushed => vec![Self::MaintainerStateDataPushed], | 298 | Self::RecursiveMaintainerStateDataPushed => vec![Self::MaintainerStateDataPushed], |
| 350 | 299 | ||
| 351 | // HeadSetToDevelopBranch depends on RecursiveMaintainerStateDataPushed | 300 | // HeadSetToDevelopBranch depends on RecursiveMaintainerStateDataPushed |
| 352 | // (all git data already exists, we just publish a new state event) | 301 | // (all git data already exists, we just publish a new state event) |
| 353 | Self::HeadSetToDevelopBranch => vec![Self::RecursiveMaintainerStateDataPushed], | 302 | Self::HeadSetToDevelopBranch => vec![Self::RecursiveMaintainerStateDataPushed], |
| @@ -365,8 +314,6 @@ impl FixtureKind { | |||
| 365 | Self::OwnerStateDataPushed => true, | 314 | Self::OwnerStateDataPushed => true, |
| 366 | Self::MaintainerStateDataPushed => true, | 315 | Self::MaintainerStateDataPushed => true, |
| 367 | Self::RecursiveMaintainerStateDataPushed => true, | 316 | Self::RecursiveMaintainerStateDataPushed => true, |
| 368 | // RecursiveMaintainerRepoAndState sends multiple events internally | ||
| 369 | Self::RecursiveMaintainerRepoAndState => true, | ||
| 370 | // PREventGenerated builds but does NOT send the PR event (that's the point) | 317 | // PREventGenerated builds but does NOT send the PR event (that's the point) |
| 371 | Self::PREventGenerated => true, | 318 | Self::PREventGenerated => true, |
| 372 | // PRWrongCommitPushedBeforeEvent pushes git data but doesn't send event | 319 | // PRWrongCommitPushedBeforeEvent pushes git data but doesn't send event |
| @@ -567,7 +514,10 @@ impl<'a> TestContext<'a> { | |||
| 567 | /// # Ok(()) | 514 | /// # Ok(()) |
| 568 | /// # } | 515 | /// # } |
| 569 | /// ``` | 516 | /// ``` |
| 570 | pub fn ensure_fixture(&self, kind: FixtureKind) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Event>> + Send + '_>> { | 517 | pub fn ensure_fixture( |
| 518 | &self, | ||
| 519 | kind: FixtureKind, | ||
| 520 | ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Event>> + Send + '_>> { | ||
| 571 | Box::pin(async move { | 521 | Box::pin(async move { |
| 572 | // Check cache first | 522 | // Check cache first |
| 573 | if let Some(cached) = self.get_cached(kind) { | 523 | if let Some(cached) = self.get_cached(kind) { |
| @@ -592,15 +542,17 @@ impl<'a> TestContext<'a> { | |||
| 592 | } | 542 | } |
| 593 | 543 | ||
| 594 | // Build the fixture | 544 | // Build the fixture |
| 595 | let event = self.build_fixture_inner(kind).await.with_context(|| { | 545 | let event = self |
| 596 | format!("Failed to build {:?} fixture", kind) | 546 | .build_fixture_inner(kind) |
| 597 | })?; | 547 | .await |
| 548 | .with_context(|| format!("Failed to build {:?} fixture", kind))?; | ||
| 598 | 549 | ||
| 599 | // Send to relay if this fixture doesn't handle it internally | 550 | // Send to relay if this fixture doesn't handle it internally |
| 600 | if !kind.sends_own_events() { | 551 | if !kind.sends_own_events() { |
| 601 | self.client.send_event(event.clone()).await.with_context(|| { | 552 | self.client |
| 602 | format!("Failed to send {:?} fixture event to relay", kind) | 553 | .send_event(event.clone()) |
| 603 | })?; | 554 | .await |
| 555 | .with_context(|| format!("Failed to send {:?} fixture event to relay", kind))?; | ||
| 604 | } | 556 | } |
| 605 | 557 | ||
| 606 | // Cache and return | 558 | // Cache and return |
| @@ -683,12 +635,8 @@ impl<'a> TestContext<'a> { | |||
| 683 | let repo = self.get_cached_dependency(FixtureKind::ValidRepo)?; | 635 | let repo = self.get_cached_dependency(FixtureKind::ValidRepo)?; |
| 684 | 636 | ||
| 685 | // Build issue referencing it - caller will send it | 637 | // Build issue referencing it - caller will send it |
| 686 | self.client.create_issue( | 638 | self.client |
| 687 | &repo, | 639 | .create_issue(&repo, "Test Issue", "Issue content for testing", vec![]) |
| 688 | "Test Issue", | ||
| 689 | "Issue content for testing", | ||
| 690 | vec![], | ||
| 691 | ) | ||
| 692 | } | 640 | } |
| 693 | 641 | ||
| 694 | FixtureKind::RepoWithComment => { | 642 | FixtureKind::RepoWithComment => { |
| @@ -736,61 +684,6 @@ impl<'a> TestContext<'a> { | |||
| 736 | .map_err(|e| anyhow::anyhow!("Failed to build state announcement: {}", e)) | 684 | .map_err(|e| anyhow::anyhow!("Failed to build state announcement: {}", e)) |
| 737 | } | 685 | } |
| 738 | 686 | ||
| 739 | FixtureKind::MaintainerAnnouncement => { | ||
| 740 | // ValidRepo is ensured by ensure_fixture before this is called | ||
| 741 | let owner_repo = self.get_cached_dependency(FixtureKind::ValidRepo)?; | ||
| 742 | |||
| 743 | let repo_id = self.extract_repo_id(&owner_repo)?; | ||
| 744 | self.build_maintainer_announcement(&repo_id).await | ||
| 745 | } | ||
| 746 | |||
| 747 | FixtureKind::MaintainerState => { | ||
| 748 | // ValidRepo is ensured by ensure_fixture before this is called | ||
| 749 | let owner_repo = self.get_cached_dependency(FixtureKind::ValidRepo)?; | ||
| 750 | |||
| 751 | let repo_id = self.extract_repo_id(&owner_repo)?; | ||
| 752 | self.build_maintainer_state(&repo_id) | ||
| 753 | } | ||
| 754 | |||
| 755 | FixtureKind::RecursiveMaintainerAnnouncement => { | ||
| 756 | // ValidRepo is ensured by ensure_fixture before this is called | ||
| 757 | let owner_repo = self.get_cached_dependency(FixtureKind::ValidRepo)?; | ||
| 758 | |||
| 759 | let repo_id = self.extract_repo_id(&owner_repo)?; | ||
| 760 | self.build_recursive_maintainer_announcement(&repo_id).await | ||
| 761 | } | ||
| 762 | |||
| 763 | FixtureKind::RecursiveMaintainerState => { | ||
| 764 | // ValidRepo is ensured by ensure_fixture before this is called | ||
| 765 | let owner_repo = self.get_cached_dependency(FixtureKind::ValidRepo)?; | ||
| 766 | |||
| 767 | let repo_id = self.extract_repo_id(&owner_repo)?; | ||
| 768 | self.build_recursive_maintainer_state(&repo_id) | ||
| 769 | } | ||
| 770 | |||
| 771 | FixtureKind::RecursiveMaintainerRepoAndState => { | ||
| 772 | // ValidRepo is ensured by ensure_fixture before this is called | ||
| 773 | let owner_repo = self.get_cached_dependency(FixtureKind::ValidRepo)?; | ||
| 774 | |||
| 775 | let repo_id = self.extract_repo_id(&owner_repo)?; | ||
| 776 | |||
| 777 | // Build and send the maintainer's repo announcement first | ||
| 778 | // This establishes the chain: Owner -> Maintainer -> RecursiveMaintainer | ||
| 779 | let maintainer_announcement = self.build_maintainer_announcement(&repo_id).await?; | ||
| 780 | self.client.send_event(maintainer_announcement).await?; | ||
| 781 | |||
| 782 | // Build and send the recursive maintainer's repo announcement | ||
| 783 | let recursive_maintainer_announcement = self | ||
| 784 | .build_recursive_maintainer_announcement(&repo_id) | ||
| 785 | .await?; | ||
| 786 | self.client | ||
| 787 | .send_event(recursive_maintainer_announcement) | ||
| 788 | .await?; | ||
| 789 | |||
| 790 | // Return the state event (caller will send it) | ||
| 791 | self.build_recursive_maintainer_state(&repo_id) | ||
| 792 | } | ||
| 793 | |||
| 794 | FixtureKind::PREvent => { | 687 | FixtureKind::PREvent => { |
| 795 | use nostr_sdk::prelude::*; | 688 | use nostr_sdk::prelude::*; |
| 796 | 689 | ||
| @@ -883,9 +776,7 @@ impl<'a> TestContext<'a> { | |||
| 883 | self.build_pr_event_sent_after_wrong_push().await | 776 | self.build_pr_event_sent_after_wrong_push().await |
| 884 | } | 777 | } |
| 885 | 778 | ||
| 886 | FixtureKind::OwnerStateDataPushed => { | 779 | FixtureKind::OwnerStateDataPushed => self.build_owner_state_data_pushed().await, |
| 887 | self.build_owner_state_data_pushed().await | ||
| 888 | } | ||
| 889 | 780 | ||
| 890 | FixtureKind::MaintainerStateDataPushed => { | 781 | FixtureKind::MaintainerStateDataPushed => { |
| 891 | self.build_maintainer_state_data_pushed().await | 782 | self.build_maintainer_state_data_pushed().await |
| @@ -895,9 +786,7 @@ impl<'a> TestContext<'a> { | |||
| 895 | self.build_recursive_maintainer_state_data_pushed().await | 786 | self.build_recursive_maintainer_state_data_pushed().await |
| 896 | } | 787 | } |
| 897 | 788 | ||
| 898 | FixtureKind::HeadSetToDevelopBranch => { | 789 | FixtureKind::HeadSetToDevelopBranch => self.build_head_set_to_develop_branch().await, |
| 899 | self.build_head_set_to_develop_branch().await | ||
| 900 | } | ||
| 901 | } | 790 | } |
| 902 | } | 791 | } |
| 903 | 792 | ||
| @@ -950,119 +839,6 @@ impl<'a> TestContext<'a> { | |||
| 950 | .map_err(|e| anyhow::anyhow!("Failed to build maintainer repo announcement: {}", e)) | 839 | .map_err(|e| anyhow::anyhow!("Failed to build maintainer repo announcement: {}", e)) |
| 951 | } | 840 | } |
| 952 | 841 | ||
| 953 | /// Build maintainer state event for the given repo_id | ||
| 954 | fn build_maintainer_state(&self, repo_id: &str) -> Result<Event> { | ||
| 955 | use nostr_sdk::prelude::*; | ||
| 956 | |||
| 957 | // Create state announcement 5 seconds in the past, signed by maintainer | ||
| 958 | let base_time = Timestamp::now().as_u64(); | ||
| 959 | let older_timestamp = Timestamp::from(base_time - 5); // 5 seconds ago | ||
| 960 | |||
| 961 | self.client | ||
| 962 | .event_builder(Kind::Custom(30618), "") | ||
| 963 | .tag(Tag::identifier(repo_id)) | ||
| 964 | .tag(Tag::custom( | ||
| 965 | TagKind::custom("refs/heads/main"), | ||
| 966 | vec![MAINTAINER_DETERMINISTIC_COMMIT_HASH.to_string()], | ||
| 967 | )) | ||
| 968 | .tag(Tag::custom( | ||
| 969 | TagKind::custom("HEAD"), | ||
| 970 | vec!["ref: refs/heads/main".to_string()], | ||
| 971 | )) | ||
| 972 | .custom_time(older_timestamp) | ||
| 973 | .build(self.client.maintainer_keys()) | ||
| 974 | .map_err(|e| anyhow::anyhow!("Failed to build maintainer state announcement: {}", e)) | ||
| 975 | } | ||
| 976 | |||
| 977 | /// Build recursive maintainer announcement event for the given repo_id | ||
| 978 | async fn build_recursive_maintainer_announcement(&self, repo_id: &str) -> Result<Event> { | ||
| 979 | use nostr_sdk::prelude::*; | ||
| 980 | |||
| 981 | // Get relay URL for clone tag | ||
| 982 | let relay_url = self | ||
| 983 | .client | ||
| 984 | .client() | ||
| 985 | .relays() | ||
| 986 | .await | ||
| 987 | .keys() | ||
| 988 | .next() | ||
| 989 | .ok_or_else(|| anyhow::anyhow!("No relay connected"))? | ||
| 990 | .to_string(); | ||
| 991 | let http_url = relay_url | ||
| 992 | .replace("ws://", "http://") | ||
| 993 | .replace("wss://", "https://"); | ||
| 994 | |||
| 995 | // Create recursive maintainer's repo announcement for the SAME repo_id | ||
| 996 | let recursive_maintainer_npub = self | ||
| 997 | .client | ||
| 998 | .recursive_maintainer_keys() | ||
| 999 | .public_key() | ||
| 1000 | .to_bech32() | ||
| 1001 | .map_err(|e| anyhow::anyhow!("Failed to convert recursive maintainer pubkey: {}", e))?; | ||
| 1002 | |||
| 1003 | self.client | ||
| 1004 | .event_builder( | ||
| 1005 | Kind::GitRepoAnnouncement, | ||
| 1006 | format!("Recursive maintainer announcement for {}", repo_id), | ||
| 1007 | ) | ||
| 1008 | .tag(Tag::identifier(repo_id)) | ||
| 1009 | .tag(Tag::custom( | ||
| 1010 | TagKind::custom("name"), | ||
| 1011 | vec![format!("{} (recursive maintainer)", repo_id)], | ||
| 1012 | )) | ||
| 1013 | .tag(Tag::custom( | ||
| 1014 | TagKind::custom("clone"), | ||
| 1015 | vec![format!( | ||
| 1016 | "{}/{}/{}.git", | ||
| 1017 | http_url, recursive_maintainer_npub, repo_id | ||
| 1018 | )], | ||
| 1019 | )) | ||
| 1020 | .tag(Tag::custom(TagKind::custom("relays"), vec![relay_url])) | ||
| 1021 | .tag(Tag::custom( | ||
| 1022 | TagKind::custom("maintainers"), | ||
| 1023 | vec![ | ||
| 1024 | self.client.public_key().to_hex(), | ||
| 1025 | self.client.maintainer_pubkey_hex(), | ||
| 1026 | ], | ||
| 1027 | )) | ||
| 1028 | .build(self.client.recursive_maintainer_keys()) | ||
| 1029 | .map_err(|e| { | ||
| 1030 | anyhow::anyhow!( | ||
| 1031 | "Failed to build recursive maintainer repo announcement: {}", | ||
| 1032 | e | ||
| 1033 | ) | ||
| 1034 | }) | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | /// Build recursive maintainer state event for the given repo_id | ||
| 1038 | fn build_recursive_maintainer_state(&self, repo_id: &str) -> Result<Event> { | ||
| 1039 | use nostr_sdk::prelude::*; | ||
| 1040 | |||
| 1041 | // Create state announcement 2 seconds in the past, signed by recursive maintainer | ||
| 1042 | let base_time = Timestamp::now().as_u64(); | ||
| 1043 | let older_timestamp = Timestamp::from(base_time - 2); // 2 seconds ago | ||
| 1044 | |||
| 1045 | self.client | ||
| 1046 | .event_builder(Kind::Custom(30618), "") | ||
| 1047 | .tag(Tag::identifier(repo_id)) | ||
| 1048 | .tag(Tag::custom( | ||
| 1049 | TagKind::custom("refs/heads/main"), | ||
| 1050 | vec![RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH.to_string()], | ||
| 1051 | )) | ||
| 1052 | .tag(Tag::custom( | ||
| 1053 | TagKind::custom("HEAD"), | ||
| 1054 | vec!["ref: refs/heads/main".to_string()], | ||
| 1055 | )) | ||
| 1056 | .custom_time(older_timestamp) | ||
| 1057 | .build(self.client.recursive_maintainer_keys()) | ||
| 1058 | .map_err(|e| { | ||
| 1059 | anyhow::anyhow!( | ||
| 1060 | "Failed to build recursive maintainer state announcement: {}", | ||
| 1061 | e | ||
| 1062 | ) | ||
| 1063 | }) | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | /// Extract repo_id from a repo announcement event | 842 | /// Extract repo_id from a repo announcement event |
| 1067 | fn extract_repo_id(&self, repo: &Event) -> Result<String> { | 843 | fn extract_repo_id(&self, repo: &Event) -> Result<String> { |
| 1068 | use nostr_sdk::prelude::*; | 844 | use nostr_sdk::prelude::*; |
| @@ -1124,7 +900,7 @@ impl<'a> TestContext<'a> { | |||
| 1124 | // ============================================================ | 900 | // ============================================================ |
| 1125 | // Stage 4: DataPushed - Clone repo, create commit, push | 901 | // Stage 4: DataPushed - Clone repo, create commit, push |
| 1126 | // ============================================================ | 902 | // ============================================================ |
| 1127 | 903 | ||
| 1128 | // Get relay domain from connected relay | 904 | // Get relay domain from connected relay |
| 1129 | let relay_domain = self.get_relay_domain().await?; | 905 | let relay_domain = self.get_relay_domain().await?; |
| 1130 | 906 | ||
| @@ -1147,7 +923,10 @@ impl<'a> TestContext<'a> { | |||
| 1147 | Ok(h) => h, | 923 | Ok(h) => h, |
| 1148 | Err(e) => { | 924 | Err(e) => { |
| 1149 | cleanup(&clone_path); | 925 | cleanup(&clone_path); |
| 1150 | return Err(anyhow::anyhow!("Failed to create deterministic commit: {}", e)); | 926 | return Err(anyhow::anyhow!( |
| 927 | "Failed to create deterministic commit: {}", | ||
| 928 | e | ||
| 929 | )); | ||
| 1151 | } | 930 | } |
| 1152 | }; | 931 | }; |
| 1153 | 932 | ||
| @@ -1236,10 +1015,10 @@ impl<'a> TestContext<'a> { | |||
| 1236 | // The owner's repo and state event are already on the relay, and git data is pushed | 1015 | // The owner's repo and state event are already on the relay, and git data is pushed |
| 1237 | // ============================================================ | 1016 | // ============================================================ |
| 1238 | let owner_state = self.get_cached_dependency(FixtureKind::OwnerStateDataPushed)?; | 1017 | let owner_state = self.get_cached_dependency(FixtureKind::OwnerStateDataPushed)?; |
| 1239 | 1018 | ||
| 1240 | // Extract repo_id from owner's state event (same d-tag structure) | 1019 | // Extract repo_id from owner's state event (same d-tag structure) |
| 1241 | let repo_id = self.extract_repo_id(&owner_state)?; | 1020 | let repo_id = self.extract_repo_id(&owner_state)?; |
| 1242 | 1021 | ||
| 1243 | // Get the repo (ValidRepo, also cached) for the owner's npub | 1022 | // Get the repo (ValidRepo, also cached) for the owner's npub |
| 1244 | let repo = self.get_cached_dependency(FixtureKind::ValidRepo)?; | 1023 | let repo = self.get_cached_dependency(FixtureKind::ValidRepo)?; |
| 1245 | 1024 | ||
| @@ -1264,7 +1043,9 @@ impl<'a> TestContext<'a> { | |||
| 1264 | .map_err(|e| anyhow::anyhow!("Failed to build maintainer state event: {}", e))?; | 1043 | .map_err(|e| anyhow::anyhow!("Failed to build maintainer state event: {}", e))?; |
| 1265 | 1044 | ||
| 1266 | // Send maintainer state event to relay | 1045 | // Send maintainer state event to relay |
| 1267 | self.client.send_event(maintainer_state_event.clone()).await?; | 1046 | self.client |
| 1047 | .send_event(maintainer_state_event.clone()) | ||
| 1048 | .await?; | ||
| 1268 | 1049 | ||
| 1269 | // ============================================================ | 1050 | // ============================================================ |
| 1270 | // Stage 3: Verify state event was accepted | 1051 | // Stage 3: Verify state event was accepted |
| @@ -1274,7 +1055,7 @@ impl<'a> TestContext<'a> { | |||
| 1274 | // ============================================================ | 1055 | // ============================================================ |
| 1275 | // Stage 4: DataPushed - Clone repo, create maintainer commit, push | 1056 | // Stage 4: DataPushed - Clone repo, create maintainer commit, push |
| 1276 | // ============================================================ | 1057 | // ============================================================ |
| 1277 | 1058 | ||
| 1278 | // Get relay domain from connected relay | 1059 | // Get relay domain from connected relay |
| 1279 | let relay_domain = self.get_relay_domain().await?; | 1060 | let relay_domain = self.get_relay_domain().await?; |
| 1280 | 1061 | ||
| @@ -1377,11 +1158,12 @@ impl<'a> TestContext<'a> { | |||
| 1377 | // The owner's repo, owner's state event, and maintainer's state event are already on the relay, | 1158 | // The owner's repo, owner's state event, and maintainer's state event are already on the relay, |
| 1378 | // and maintainer's git data is pushed | 1159 | // and maintainer's git data is pushed |
| 1379 | // ============================================================ | 1160 | // ============================================================ |
| 1380 | let maintainer_state = self.get_cached_dependency(FixtureKind::MaintainerStateDataPushed)?; | 1161 | let maintainer_state = |
| 1381 | 1162 | self.get_cached_dependency(FixtureKind::MaintainerStateDataPushed)?; | |
| 1163 | |||
| 1382 | // Extract repo_id from maintainer's state event (same d-tag structure) | 1164 | // Extract repo_id from maintainer's state event (same d-tag structure) |
| 1383 | let repo_id = self.extract_repo_id(&maintainer_state)?; | 1165 | let repo_id = self.extract_repo_id(&maintainer_state)?; |
| 1384 | 1166 | ||
| 1385 | // Get the repo (ValidRepo, also cached) for the owner's npub | 1167 | // Get the repo (ValidRepo, also cached) for the owner's npub |
| 1386 | let repo = self.get_cached_dependency(FixtureKind::ValidRepo)?; | 1168 | let repo = self.get_cached_dependency(FixtureKind::ValidRepo)?; |
| 1387 | 1169 | ||
| @@ -1409,10 +1191,14 @@ impl<'a> TestContext<'a> { | |||
| 1409 | )) | 1191 | )) |
| 1410 | .custom_time(recursive_maintainer_timestamp) | 1192 | .custom_time(recursive_maintainer_timestamp) |
| 1411 | .build(self.client.recursive_maintainer_keys()) | 1193 | .build(self.client.recursive_maintainer_keys()) |
| 1412 | .map_err(|e| anyhow::anyhow!("Failed to build recursive maintainer state event: {}", e))?; | 1194 | .map_err(|e| { |
| 1195 | anyhow::anyhow!("Failed to build recursive maintainer state event: {}", e) | ||
| 1196 | })?; | ||
| 1413 | 1197 | ||
| 1414 | // Send recursive maintainer state event to relay | 1198 | // Send recursive maintainer state event to relay |
| 1415 | self.client.send_event(recursive_maintainer_state_event.clone()).await?; | 1199 | self.client |
| 1200 | .send_event(recursive_maintainer_state_event.clone()) | ||
| 1201 | .await?; | ||
| 1416 | 1202 | ||
| 1417 | // ============================================================ | 1203 | // ============================================================ |
| 1418 | // Stage 3: Verify state event was accepted | 1204 | // Stage 3: Verify state event was accepted |
| @@ -1422,7 +1208,7 @@ impl<'a> TestContext<'a> { | |||
| 1422 | // ============================================================ | 1208 | // ============================================================ |
| 1423 | // Stage 4: DataPushed - Clone repo, create recursive maintainer commit, push | 1209 | // Stage 4: DataPushed - Clone repo, create recursive maintainer commit, push |
| 1424 | // ============================================================ | 1210 | // ============================================================ |
| 1425 | 1211 | ||
| 1426 | // Get relay domain from connected relay | 1212 | // Get relay domain from connected relay |
| 1427 | let relay_domain = self.get_relay_domain().await?; | 1213 | let relay_domain = self.get_relay_domain().await?; |
| 1428 | 1214 | ||
| @@ -1462,7 +1248,10 @@ impl<'a> TestContext<'a> { | |||
| 1462 | Ok(h) => h, | 1248 | Ok(h) => h, |
| 1463 | Err(e) => { | 1249 | Err(e) => { |
| 1464 | cleanup(&clone_path); | 1250 | cleanup(&clone_path); |
| 1465 | return Err(anyhow::anyhow!("Failed to create recursive maintainer commit: {}", e)); | 1251 | return Err(anyhow::anyhow!( |
| 1252 | "Failed to create recursive maintainer commit: {}", | ||
| 1253 | e | ||
| 1254 | )); | ||
| 1466 | } | 1255 | } |
| 1467 | }; | 1256 | }; |
| 1468 | 1257 | ||
| @@ -1525,8 +1314,9 @@ impl<'a> TestContext<'a> { | |||
| 1525 | // Stage 1: RecursiveMaintainerStateDataPushed is ensured by ensure_fixture before this is called | 1314 | // Stage 1: RecursiveMaintainerStateDataPushed is ensured by ensure_fixture before this is called |
| 1526 | // All git data already exists on the relay (main branch with RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH) | 1315 | // All git data already exists on the relay (main branch with RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH) |
| 1527 | // ============================================================ | 1316 | // ============================================================ |
| 1528 | let recursive_state = self.get_cached_dependency(FixtureKind::RecursiveMaintainerStateDataPushed)?; | 1317 | let recursive_state = |
| 1529 | 1318 | self.get_cached_dependency(FixtureKind::RecursiveMaintainerStateDataPushed)?; | |
| 1319 | |||
| 1530 | // Extract repo_id from the recursive maintainer's state event | 1320 | // Extract repo_id from the recursive maintainer's state event |
| 1531 | let repo_id = self.extract_repo_id(&recursive_state)?; | 1321 | let repo_id = self.extract_repo_id(&recursive_state)?; |
| 1532 | 1322 | ||
| @@ -1607,16 +1397,14 @@ impl<'a> TestContext<'a> { | |||
| 1607 | 1397 | ||
| 1608 | // Create a WRONG commit (Owner variant, not PRTestCommit) | 1398 | // Create a WRONG commit (Owner variant, not PRTestCommit) |
| 1609 | // This commit hash will NOT match what's in the PR event's `c` tag | 1399 | // This commit hash will NOT match what's in the PR event's `c` tag |
| 1610 | let wrong_commit_hash = match create_deterministic_commit_with_variant( | 1400 | let wrong_commit_hash = |
| 1611 | &clone_path, | 1401 | match create_deterministic_commit_with_variant(&clone_path, CommitVariant::Owner) { |
| 1612 | CommitVariant::Owner, | 1402 | Ok(h) => h, |
| 1613 | ) { | 1403 | Err(e) => { |
| 1614 | Ok(h) => h, | 1404 | cleanup(&clone_path); |
| 1615 | Err(e) => { | 1405 | return Err(anyhow::anyhow!("Failed to create wrong commit: {}", e)); |
| 1616 | cleanup(&clone_path); | 1406 | } |
| 1617 | return Err(anyhow::anyhow!("Failed to create wrong commit: {}", e)); | 1407 | }; |
| 1618 | } | ||
| 1619 | }; | ||
| 1620 | 1408 | ||
| 1621 | // Verify it's actually different from expected PR commit | 1409 | // Verify it's actually different from expected PR commit |
| 1622 | if wrong_commit_hash == PR_TEST_COMMIT_HASH { | 1410 | if wrong_commit_hash == PR_TEST_COMMIT_HASH { |