diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-12-01 11:56:49 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-12-01 11:58:34 +0000 |
| commit | 7a78815e29b01c83f3d0ec195ba717a2eba8cd37 (patch) | |
| tree | 4c5ccd9b812f1d1d75ed218501192ddc5459fd12 /grasp-audit/src/fixtures.rs | |
| parent | e6ceab90de1acad154624022a6036efac18abab6 (diff) | |
reject push when refs/nostr/<event-id> doesnt match known event and delete incorrect ref on event receive
Diffstat (limited to 'grasp-audit/src/fixtures.rs')
| -rw-r--r-- | grasp-audit/src/fixtures.rs | 172 |
1 files changed, 112 insertions, 60 deletions
diff --git a/grasp-audit/src/fixtures.rs b/grasp-audit/src/fixtures.rs index dc4e638..4b5014d 100644 --- a/grasp-audit/src/fixtures.rs +++ b/grasp-audit/src/fixtures.rs | |||
| @@ -59,7 +59,7 @@ pub const DETERMINISTIC_COMMIT_HASH: &str = "64ea71d79a57a7acb334cd9651f8aec067c | |||
| 59 | /// - GPG signing: disabled | 59 | /// - GPG signing: disabled |
| 60 | /// - User: "GRASP Audit Test <test@grasp-audit.local>" | 60 | /// - User: "GRASP Audit Test <test@grasp-audit.local>" |
| 61 | /// - Parent: none (root commit) | 61 | /// - Parent: none (root commit) |
| 62 | /// NOTE: This value is different from DETERMINISTIC_COMMIT_HASH due to different content | 62 | /// NOTE: This value is different from DETERMINISTIC_COMMIT_HASH due to different content |
| 63 | pub const MAINTAINER_DETERMINISTIC_COMMIT_HASH: &str = "1c2d472c9b71ed51968a66500281a3c4a6840464"; | 63 | pub const MAINTAINER_DETERMINISTIC_COMMIT_HASH: &str = "1c2d472c9b71ed51968a66500281a3c4a6840464"; |
| 64 | 64 | ||
| 65 | /// Deterministic commit hash for recursive maintainer fixtures (RecursiveMaintainer variant) | 65 | /// Deterministic commit hash for recursive maintainer fixtures (RecursiveMaintainer variant) |
| @@ -71,8 +71,9 @@ pub const MAINTAINER_DETERMINISTIC_COMMIT_HASH: &str = "1c2d472c9b71ed51968a6650 | |||
| 71 | /// - GPG signing: disabled | 71 | /// - GPG signing: disabled |
| 72 | /// - User: "GRASP Audit Test <test@grasp-audit.local>" | 72 | /// - User: "GRASP Audit Test <test@grasp-audit.local>" |
| 73 | /// - Parent: none (root commit) | 73 | /// - Parent: none (root commit) |
| 74 | /// NOTE: This value is different from DETERMINISTIC_COMMIT_HASH due to different content | 74 | /// NOTE: This value is different from DETERMINISTIC_COMMIT_HASH due to different content |
| 75 | pub const RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH: &str = "05939b82de66fbdb9c077d0a64fc68522f3cb8e0"; | 75 | pub const RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH: &str = |
| 76 | "05939b82de66fbdb9c077d0a64fc68522f3cb8e0"; | ||
| 76 | 77 | ||
| 77 | /// Deterministic commit hash for PR test fixtures (PRTestCommit variant) | 78 | /// Deterministic commit hash for PR test fixtures (PRTestCommit variant) |
| 78 | /// This is the hash produced by creating a commit with: | 79 | /// This is the hash produced by creating a commit with: |
| @@ -83,7 +84,7 @@ pub const RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH: &str = "05939b82de66fb | |||
| 83 | /// - GPG signing: disabled | 84 | /// - GPG signing: disabled |
| 84 | /// - User: "GRASP Audit Test <test@grasp-audit.local>" | 85 | /// - User: "GRASP Audit Test <test@grasp-audit.local>" |
| 85 | /// - Parent: none (root commit) | 86 | /// - Parent: none (root commit) |
| 86 | pub const PR_TEST_COMMIT_HASH: &str = "8935183ff722bf04e861928c6a7e50868c6ca4a6"; | 87 | pub const PR_TEST_COMMIT_HASH: &str = "5d40fb1555a0c28bf4d650515a73aaa54d4d9bfb"; |
| 87 | 88 | ||
| 88 | /// Types of test fixtures available | 89 | /// Types of test fixtures available |
| 89 | /// | 90 | /// |
| @@ -157,10 +158,10 @@ pub enum FixtureKind { | |||
| 157 | /// - Timestamp: 2 seconds in the past (most recent) | 158 | /// - Timestamp: 2 seconds in the past (most recent) |
| 158 | RecursiveMaintainerRepoAndState, | 159 | RecursiveMaintainerRepoAndState, |
| 159 | 160 | ||
| 160 | /// PR (Patch/Pull Request) event for the SAME repo_id as ValidRepo | 161 | /// PR (Pull Request) event for the SAME repo_id as ValidRepo |
| 161 | /// - Requires ValidRepo (uses same repo_id) | 162 | /// - Requires ValidRepo (uses same repo_id) |
| 162 | /// - Signed by `client.pr_author_keys()` | 163 | /// - Signed by `client.pr_author_keys()` |
| 163 | /// - Kind 1617 (NIP-34 patch) | 164 | /// - Kind 1618 (NIP-34 PR) |
| 164 | /// - Includes `a` tag referencing the repo | 165 | /// - Includes `a` tag referencing the repo |
| 165 | /// - Includes `c` tag pointing to PR_TEST_COMMIT_HASH | 166 | /// - Includes `c` tag pointing to PR_TEST_COMMIT_HASH |
| 166 | /// - Timestamp: 1 second in the past | 167 | /// - Timestamp: 1 second in the past |
| @@ -290,6 +291,37 @@ impl<'a> TestContext<'a> { | |||
| 290 | self.mode | 291 | self.mode |
| 291 | } | 292 | } |
| 292 | 293 | ||
| 294 | /// Build a fixture event WITHOUT publishing it to the relay. | ||
| 295 | /// | ||
| 296 | /// This is useful for tests that need to get a fixture's event ID before | ||
| 297 | /// actually publishing it. For example, testing refs/nostr/<event-id> | ||
| 298 | /// behavior before the corresponding event exists on the relay. | ||
| 299 | /// | ||
| 300 | /// Note: This may still create and publish dependencies (e.g., ValidRepo | ||
| 301 | /// will be created/published if PREvent needs it), but the requested | ||
| 302 | /// fixture itself will NOT be published. | ||
| 303 | /// | ||
| 304 | /// # Example | ||
| 305 | /// | ||
| 306 | /// ```no_run | ||
| 307 | /// # use grasp_audit::*; | ||
| 308 | /// # async fn example(ctx: &TestContext<'_>) -> anyhow::Result<()> { | ||
| 309 | /// // Build PR event to get its ID without publishing | ||
| 310 | /// let pr_event = ctx.build_fixture_only(FixtureKind::PREvent).await?; | ||
| 311 | /// let pr_event_id = pr_event.id.to_hex(); | ||
| 312 | /// | ||
| 313 | /// // Now push to refs/nostr/<pr_event_id> before event exists | ||
| 314 | /// // ... git push ... | ||
| 315 | /// | ||
| 316 | /// // Later, publish the PR event when ready | ||
| 317 | /// ctx.client().send_event(pr_event).await?; | ||
| 318 | /// # Ok(()) | ||
| 319 | /// # } | ||
| 320 | /// ``` | ||
| 321 | pub async fn build_fixture_only(&self, kind: FixtureKind) -> Result<Event> { | ||
| 322 | self.build_fixture(kind).await | ||
| 323 | } | ||
| 324 | |||
| 293 | /// Create a fresh fixture (always creates new) | 325 | /// Create a fresh fixture (always creates new) |
| 294 | async fn create_fresh(&self, kind: FixtureKind) -> Result<Event> { | 326 | async fn create_fresh(&self, kind: FixtureKind) -> Result<Event> { |
| 295 | let event = self | 327 | let event = self |
| @@ -348,7 +380,11 @@ impl<'a> TestContext<'a> { | |||
| 348 | { | 380 | { |
| 349 | let mut cache = self.client.fixture_cache().lock().unwrap(); | 381 | let mut cache = self.client.fixture_cache().lock().unwrap(); |
| 350 | cache.insert(kind, event.clone()); | 382 | cache.insert(kind, event.clone()); |
| 351 | tracing::debug!("get_or_create_shared({:?}) stored in client cache ({} entries)", kind, cache.len()); | 383 | tracing::debug!( |
| 384 | "get_or_create_shared({:?}) stored in client cache ({} entries)", | ||
| 385 | kind, | ||
| 386 | cache.len() | ||
| 387 | ); | ||
| 352 | } | 388 | } |
| 353 | 389 | ||
| 354 | Ok(event) | 390 | Ok(event) |
| @@ -398,12 +434,17 @@ impl<'a> TestContext<'a> { | |||
| 398 | FixtureKind::ValidRepo, | 434 | FixtureKind::ValidRepo, |
| 399 | &uuid::Uuid::new_v4().to_string()[..8] | 435 | &uuid::Uuid::new_v4().to_string()[..8] |
| 400 | ); | 436 | ); |
| 401 | 437 | ||
| 402 | let repo = self.client.create_repo_announcement(&test_name).await | 438 | let repo = self |
| 439 | .client | ||
| 440 | .create_repo_announcement(&test_name) | ||
| 441 | .await | ||
| 403 | .with_context(|| format!("create_repo_announcement failed for {}", test_name))?; | 442 | .with_context(|| format!("create_repo_announcement failed for {}", test_name))?; |
| 404 | 443 | ||
| 405 | // Send it | 444 | // Send it |
| 406 | self.client.send_event(repo.clone()).await | 445 | self.client |
| 446 | .send_event(repo.clone()) | ||
| 447 | .await | ||
| 407 | .with_context(|| "Failed to send repo announcement to relay")?; | 448 | .with_context(|| "Failed to send repo announcement to relay")?; |
| 408 | 449 | ||
| 409 | // Store in the appropriate cache based on mode | 450 | // Store in the appropriate cache based on mode |
| @@ -412,13 +453,19 @@ impl<'a> TestContext<'a> { | |||
| 412 | // Store in local cache for within-test fixture dependencies | 453 | // Store in local cache for within-test fixture dependencies |
| 413 | let mut cache = self.local_cache.lock().unwrap(); | 454 | let mut cache = self.local_cache.lock().unwrap(); |
| 414 | cache.insert(FixtureKind::ValidRepo, repo.clone()); | 455 | cache.insert(FixtureKind::ValidRepo, repo.clone()); |
| 415 | tracing::debug!("get_or_create_repo() stored in local cache ({} entries)", cache.len()); | 456 | tracing::debug!( |
| 457 | "get_or_create_repo() stored in local cache ({} entries)", | ||
| 458 | cache.len() | ||
| 459 | ); | ||
| 416 | } | 460 | } |
| 417 | ContextMode::Shared => { | 461 | ContextMode::Shared => { |
| 418 | // Store in client cache for cross-test sharing | 462 | // Store in client cache for cross-test sharing |
| 419 | let mut cache = self.client.fixture_cache().lock().unwrap(); | 463 | let mut cache = self.client.fixture_cache().lock().unwrap(); |
| 420 | cache.insert(FixtureKind::ValidRepo, repo.clone()); | 464 | cache.insert(FixtureKind::ValidRepo, repo.clone()); |
| 421 | tracing::debug!("get_or_create_repo() stored in client cache ({} entries)", cache.len()); | 465 | tracing::debug!( |
| 466 | "get_or_create_repo() stored in client cache ({} entries)", | ||
| 467 | cache.len() | ||
| 468 | ); | ||
| 422 | } | 469 | } |
| 423 | } | 470 | } |
| 424 | 471 | ||
| @@ -448,12 +495,9 @@ impl<'a> TestContext<'a> { | |||
| 448 | let repo = self.get_or_create_repo().await?; | 495 | let repo = self.get_or_create_repo().await?; |
| 449 | 496 | ||
| 450 | // Create the issue | 497 | // Create the issue |
| 451 | let issue = self.client.create_issue( | 498 | let issue = |
| 452 | &repo, | 499 | self.client |
| 453 | "Test Issue", | 500 | .create_issue(&repo, "Test Issue", "Issue content for testing", vec![])?; |
| 454 | "Issue content for testing", | ||
| 455 | vec![], | ||
| 456 | )?; | ||
| 457 | 501 | ||
| 458 | // Send it | 502 | // Send it |
| 459 | self.client.send_event(issue.clone()).await?; | 503 | self.client.send_event(issue.clone()).await?; |
| @@ -555,7 +599,7 @@ impl<'a> TestContext<'a> { | |||
| 555 | 599 | ||
| 556 | // Get the owner's repo to use the SAME repo_id | 600 | // Get the owner's repo to use the SAME repo_id |
| 557 | let owner_repo = self.get_or_create_repo().await?; | 601 | let owner_repo = self.get_or_create_repo().await?; |
| 558 | 602 | ||
| 559 | // Extract repo_id from owner's repo announcement | 603 | // Extract repo_id from owner's repo announcement |
| 560 | let repo_id = owner_repo | 604 | let repo_id = owner_repo |
| 561 | .tags | 605 | .tags |
| @@ -573,7 +617,7 @@ impl<'a> TestContext<'a> { | |||
| 573 | 617 | ||
| 574 | // Get the owner's repo to use the SAME repo_id | 618 | // Get the owner's repo to use the SAME repo_id |
| 575 | let owner_repo = self.get_or_create_repo().await?; | 619 | let owner_repo = self.get_or_create_repo().await?; |
| 576 | 620 | ||
| 577 | // Extract repo_id from owner's repo announcement | 621 | // Extract repo_id from owner's repo announcement |
| 578 | let repo_id = owner_repo | 622 | let repo_id = owner_repo |
| 579 | .tags | 623 | .tags |
| @@ -593,7 +637,7 @@ impl<'a> TestContext<'a> { | |||
| 593 | 637 | ||
| 594 | // Get the owner's repo to use the SAME repo_id | 638 | // Get the owner's repo to use the SAME repo_id |
| 595 | let owner_repo = self.get_or_create_repo().await?; | 639 | let owner_repo = self.get_or_create_repo().await?; |
| 596 | 640 | ||
| 597 | // Extract repo_id from owner's repo announcement | 641 | // Extract repo_id from owner's repo announcement |
| 598 | let repo_id = owner_repo | 642 | let repo_id = owner_repo |
| 599 | .tags | 643 | .tags |
| @@ -611,7 +655,7 @@ impl<'a> TestContext<'a> { | |||
| 611 | 655 | ||
| 612 | // Get the owner's repo to use the SAME repo_id | 656 | // Get the owner's repo to use the SAME repo_id |
| 613 | let owner_repo = self.get_or_create_repo().await?; | 657 | let owner_repo = self.get_or_create_repo().await?; |
| 614 | 658 | ||
| 615 | // Extract repo_id from owner's repo announcement | 659 | // Extract repo_id from owner's repo announcement |
| 616 | let repo_id = owner_repo | 660 | let repo_id = owner_repo |
| 617 | .tags | 661 | .tags |
| @@ -630,7 +674,7 @@ impl<'a> TestContext<'a> { | |||
| 630 | 674 | ||
| 631 | // Get the owner's repo to use the SAME repo_id | 675 | // Get the owner's repo to use the SAME repo_id |
| 632 | let owner_repo = self.get_or_create_repo().await?; | 676 | let owner_repo = self.get_or_create_repo().await?; |
| 633 | 677 | ||
| 634 | // Extract repo_id from owner's repo announcement | 678 | // Extract repo_id from owner's repo announcement |
| 635 | let repo_id = owner_repo | 679 | let repo_id = owner_repo |
| 636 | .tags | 680 | .tags |
| @@ -647,8 +691,12 @@ impl<'a> TestContext<'a> { | |||
| 647 | self.client.send_event(maintainer_announcement).await?; | 691 | self.client.send_event(maintainer_announcement).await?; |
| 648 | 692 | ||
| 649 | // Build and send the recursive maintainer's repo announcement | 693 | // Build and send the recursive maintainer's repo announcement |
| 650 | let recursive_maintainer_announcement = self.build_recursive_maintainer_announcement(&repo_id).await?; | 694 | let recursive_maintainer_announcement = self |
| 651 | self.client.send_event(recursive_maintainer_announcement).await?; | 695 | .build_recursive_maintainer_announcement(&repo_id) |
| 696 | .await?; | ||
| 697 | self.client | ||
| 698 | .send_event(recursive_maintainer_announcement) | ||
| 699 | .await?; | ||
| 652 | 700 | ||
| 653 | // Return the state event (caller will send it) | 701 | // Return the state event (caller will send it) |
| 654 | self.build_recursive_maintainer_state(&repo_id) | 702 | self.build_recursive_maintainer_state(&repo_id) |
| @@ -672,10 +720,10 @@ impl<'a> TestContext<'a> { | |||
| 672 | let base_time = Timestamp::now().as_u64(); | 720 | let base_time = Timestamp::now().as_u64(); |
| 673 | let pr_timestamp = Timestamp::from(base_time - 1); | 721 | let pr_timestamp = Timestamp::from(base_time - 1); |
| 674 | 722 | ||
| 675 | // Build NIP-34 patch event (kind 1617) | 723 | // Build NIP-34 PR event (kind 1618) |
| 676 | self.client | 724 | self.client |
| 677 | .event_builder( | 725 | .event_builder( |
| 678 | Kind::Custom(1617), // NIP-34 patch/PR kind | 726 | Kind::Custom(1618), // NIP-34 PR kind (has 'c' tag for commit) |
| 679 | "Test PR for GRASP validation", | 727 | "Test PR for GRASP validation", |
| 680 | ) | 728 | ) |
| 681 | .tag(Tag::custom( | 729 | .tag(Tag::custom( |
| @@ -702,7 +750,8 @@ impl<'a> TestContext<'a> { | |||
| 702 | use nostr_sdk::prelude::*; | 750 | use nostr_sdk::prelude::*; |
| 703 | 751 | ||
| 704 | // Get relay URL for clone tag | 752 | // Get relay URL for clone tag |
| 705 | let relay_url = self.client | 753 | let relay_url = self |
| 754 | .client | ||
| 706 | .client() | 755 | .client() |
| 707 | .relays() | 756 | .relays() |
| 708 | .await | 757 | .await |
| @@ -715,7 +764,8 @@ impl<'a> TestContext<'a> { | |||
| 715 | .replace("wss://", "https://"); | 764 | .replace("wss://", "https://"); |
| 716 | 765 | ||
| 717 | // Create maintainer's repo announcement for the SAME repo_id | 766 | // Create maintainer's repo announcement for the SAME repo_id |
| 718 | let maintainer_npub = self.client | 767 | let maintainer_npub = self |
| 768 | .client | ||
| 719 | .maintainer_keys() | 769 | .maintainer_keys() |
| 720 | .public_key() | 770 | .public_key() |
| 721 | .to_bech32() | 771 | .to_bech32() |
| @@ -735,10 +785,7 @@ impl<'a> TestContext<'a> { | |||
| 735 | TagKind::custom("clone"), | 785 | TagKind::custom("clone"), |
| 736 | vec![format!("{}/{}/{}.git", http_url, maintainer_npub, repo_id)], | 786 | vec![format!("{}/{}/{}.git", http_url, maintainer_npub, repo_id)], |
| 737 | )) | 787 | )) |
| 738 | .tag(Tag::custom( | 788 | .tag(Tag::custom(TagKind::custom("relays"), vec![relay_url])) |
| 739 | TagKind::custom("relays"), | ||
| 740 | vec![relay_url], | ||
| 741 | )) | ||
| 742 | .tag(Tag::custom( | 789 | .tag(Tag::custom( |
| 743 | TagKind::custom("maintainers"), | 790 | TagKind::custom("maintainers"), |
| 744 | vec![self.client.recursive_maintainer_pubkey_hex()], | 791 | vec![self.client.recursive_maintainer_pubkey_hex()], |
| @@ -776,7 +823,8 @@ impl<'a> TestContext<'a> { | |||
| 776 | use nostr_sdk::prelude::*; | 823 | use nostr_sdk::prelude::*; |
| 777 | 824 | ||
| 778 | // Get relay URL for clone tag | 825 | // Get relay URL for clone tag |
| 779 | let relay_url = self.client | 826 | let relay_url = self |
| 827 | .client | ||
| 780 | .client() | 828 | .client() |
| 781 | .relays() | 829 | .relays() |
| 782 | .await | 830 | .await |
| @@ -789,7 +837,8 @@ impl<'a> TestContext<'a> { | |||
| 789 | .replace("wss://", "https://"); | 837 | .replace("wss://", "https://"); |
| 790 | 838 | ||
| 791 | // Create recursive maintainer's repo announcement for the SAME repo_id | 839 | // Create recursive maintainer's repo announcement for the SAME repo_id |
| 792 | let recursive_maintainer_npub = self.client | 840 | let recursive_maintainer_npub = self |
| 841 | .client | ||
| 793 | .recursive_maintainer_keys() | 842 | .recursive_maintainer_keys() |
| 794 | .public_key() | 843 | .public_key() |
| 795 | .to_bech32() | 844 | .to_bech32() |
| @@ -807,12 +856,12 @@ impl<'a> TestContext<'a> { | |||
| 807 | )) | 856 | )) |
| 808 | .tag(Tag::custom( | 857 | .tag(Tag::custom( |
| 809 | TagKind::custom("clone"), | 858 | TagKind::custom("clone"), |
| 810 | vec![format!("{}/{}/{}.git", http_url, recursive_maintainer_npub, repo_id)], | 859 | vec![format!( |
| 811 | )) | 860 | "{}/{}/{}.git", |
| 812 | .tag(Tag::custom( | 861 | http_url, recursive_maintainer_npub, repo_id |
| 813 | TagKind::custom("relays"), | 862 | )], |
| 814 | vec![relay_url], | ||
| 815 | )) | 863 | )) |
| 864 | .tag(Tag::custom(TagKind::custom("relays"), vec![relay_url])) | ||
| 816 | .tag(Tag::custom( | 865 | .tag(Tag::custom( |
| 817 | TagKind::custom("maintainers"), | 866 | TagKind::custom("maintainers"), |
| 818 | vec![ | 867 | vec![ |
| @@ -821,7 +870,12 @@ impl<'a> TestContext<'a> { | |||
| 821 | ], | 870 | ], |
| 822 | )) | 871 | )) |
| 823 | .build(self.client.recursive_maintainer_keys()) | 872 | .build(self.client.recursive_maintainer_keys()) |
| 824 | .map_err(|e| anyhow::anyhow!("Failed to build recursive maintainer repo announcement: {}", e)) | 873 | .map_err(|e| { |
| 874 | anyhow::anyhow!( | ||
| 875 | "Failed to build recursive maintainer repo announcement: {}", | ||
| 876 | e | ||
| 877 | ) | ||
| 878 | }) | ||
| 825 | } | 879 | } |
| 826 | 880 | ||
| 827 | /// Build recursive maintainer state event for the given repo_id | 881 | /// Build recursive maintainer state event for the given repo_id |
| @@ -898,7 +952,7 @@ pub async fn send_and_verify_accepted( | |||
| 898 | ) -> Result<(), String> { | 952 | ) -> Result<(), String> { |
| 899 | use nostr_sdk::prelude::Filter; | 953 | use nostr_sdk::prelude::Filter; |
| 900 | use std::time::Duration; | 954 | use std::time::Duration; |
| 901 | 955 | ||
| 902 | let event_id = event.id; | 956 | let event_id = event.id; |
| 903 | 957 | ||
| 904 | client | 958 | client |
| @@ -952,12 +1006,12 @@ pub async fn send_and_verify_rejected( | |||
| 952 | ) -> Result<(), String> { | 1006 | ) -> Result<(), String> { |
| 953 | use nostr_sdk::prelude::Filter; | 1007 | use nostr_sdk::prelude::Filter; |
| 954 | use std::time::Duration; | 1008 | use std::time::Duration; |
| 955 | 1009 | ||
| 956 | let event_id = event.id; | 1010 | let event_id = event.id; |
| 957 | 1011 | ||
| 958 | // Try to send event - rejection may cause send_event to fail with an error | 1012 | // Try to send event - rejection may cause send_event to fail with an error |
| 959 | let send_result = client.send_event(event).await; | 1013 | let send_result = client.send_event(event).await; |
| 960 | 1014 | ||
| 961 | // If send succeeded, the relay might have accepted it (we'll verify below) | 1015 | // If send succeeded, the relay might have accepted it (we'll verify below) |
| 962 | // If send failed, check if it's a rejection error (expected) | 1016 | // If send failed, check if it's a rejection error (expected) |
| 963 | if let Err(e) = send_result { | 1017 | if let Err(e) = send_result { |
| @@ -966,7 +1020,7 @@ pub async fn send_and_verify_rejected( | |||
| 966 | if err_msg.contains("rejected") || err_msg.contains("blocked") { | 1020 | if err_msg.contains("rejected") || err_msg.contains("blocked") { |
| 967 | // Expected rejection - verify event is NOT in database | 1021 | // Expected rejection - verify event is NOT in database |
| 968 | tokio::time::sleep(Duration::from_millis(100)).await; | 1022 | tokio::time::sleep(Duration::from_millis(100)).await; |
| 969 | 1023 | ||
| 970 | let filter = Filter::new().id(event_id); | 1024 | let filter = Filter::new().id(event_id); |
| 971 | let events = client | 1025 | let events = client |
| 972 | .query(filter) | 1026 | .query(filter) |
| @@ -974,9 +1028,12 @@ pub async fn send_and_verify_rejected( | |||
| 974 | .map_err(|e| format!("Failed to query relay for verification: {}", e))?; | 1028 | .map_err(|e| format!("Failed to query relay for verification: {}", e))?; |
| 975 | 1029 | ||
| 976 | if !events.is_empty() { | 1030 | if !events.is_empty() { |
| 977 | return Err(format!("Event was rejected but still stored: {}", description)); | 1031 | return Err(format!( |
| 1032 | "Event was rejected but still stored: {}", | ||
| 1033 | description | ||
| 1034 | )); | ||
| 978 | } | 1035 | } |
| 979 | 1036 | ||
| 980 | return Ok(()); // Rejected as expected | 1037 | return Ok(()); // Rejected as expected |
| 981 | } else { | 1038 | } else { |
| 982 | // Unexpected error (network, etc.) | 1039 | // Unexpected error (network, etc.) |
| @@ -1029,11 +1086,7 @@ use std::process::Command; | |||
| 1029 | /// # Ok(()) | 1086 | /// # Ok(()) |
| 1030 | /// # } | 1087 | /// # } |
| 1031 | /// ``` | 1088 | /// ``` |
| 1032 | pub fn clone_repo( | 1089 | pub fn clone_repo(relay_domain: &str, npub: &str, repo_id: &str) -> Result<PathBuf, String> { |
| 1033 | relay_domain: &str, | ||
| 1034 | npub: &str, | ||
| 1035 | repo_id: &str, | ||
| 1036 | ) -> Result<PathBuf, String> { | ||
| 1037 | let temp_base = std::env::temp_dir(); | 1090 | let temp_base = std::env::temp_dir(); |
| 1038 | let clone_dir_name = format!("grasp-push-test-{}", uuid::Uuid::new_v4()); | 1091 | let clone_dir_name = format!("grasp-push-test-{}", uuid::Uuid::new_v4()); |
| 1039 | let clone_path = temp_base.join(&clone_dir_name); | 1092 | let clone_path = temp_base.join(&clone_dir_name); |
| @@ -1146,7 +1199,7 @@ impl CommitVariant { | |||
| 1146 | CommitVariant::PRTestCommit => "PR test deterministic commit", | 1199 | CommitVariant::PRTestCommit => "PR test deterministic commit", |
| 1147 | } | 1200 | } |
| 1148 | } | 1201 | } |
| 1149 | 1202 | ||
| 1150 | /// Get the commit message for this variant | 1203 | /// Get the commit message for this variant |
| 1151 | pub fn commit_message(&self) -> &'static str { | 1204 | pub fn commit_message(&self) -> &'static str { |
| 1152 | match self { | 1205 | match self { |
| @@ -1172,11 +1225,14 @@ impl CommitVariant { | |||
| 1172 | /// # Returns | 1225 | /// # Returns |
| 1173 | /// * `Ok(String)` - The deterministic commit hash | 1226 | /// * `Ok(String)` - The deterministic commit hash |
| 1174 | /// * `Err(String)` - Error message if commit failed | 1227 | /// * `Err(String)` - Error message if commit failed |
| 1175 | pub fn create_deterministic_commit_with_variant(clone_path: &Path, variant: CommitVariant) -> Result<String, String> { | 1228 | pub fn create_deterministic_commit_with_variant( |
| 1229 | clone_path: &Path, | ||
| 1230 | variant: CommitVariant, | ||
| 1231 | ) -> Result<String, String> { | ||
| 1176 | let test_file = clone_path.join("test.txt"); | 1232 | let test_file = clone_path.join("test.txt"); |
| 1177 | let content = variant.file_content(); | 1233 | let content = variant.file_content(); |
| 1178 | let message = variant.commit_message(); | 1234 | let message = variant.commit_message(); |
| 1179 | 1235 | ||
| 1180 | fs::write(&test_file, content).map_err(|e| format!("Failed to write file: {}", e))?; | 1236 | fs::write(&test_file, content).map_err(|e| format!("Failed to write file: {}", e))?; |
| 1181 | 1237 | ||
| 1182 | let output = Command::new("git") | 1238 | let output = Command::new("git") |
| @@ -1191,11 +1247,7 @@ pub fn create_deterministic_commit_with_variant(clone_path: &Path, variant: Comm | |||
| 1191 | 1247 | ||
| 1192 | // Create deterministic commit with fixed dates and GPG disabled | 1248 | // Create deterministic commit with fixed dates and GPG disabled |
| 1193 | let output = Command::new("git") | 1249 | let output = Command::new("git") |
| 1194 | .args([ | 1250 | .args(["-c", "commit.gpgsign=false", "commit", "-m", message]) |
| 1195 | "-c", "commit.gpgsign=false", | ||
| 1196 | "commit", | ||
| 1197 | "-m", message, | ||
| 1198 | ]) | ||
| 1199 | .env("GIT_AUTHOR_DATE", "2024-01-01T00:00:00Z") | 1251 | .env("GIT_AUTHOR_DATE", "2024-01-01T00:00:00Z") |
| 1200 | .env("GIT_COMMITTER_DATE", "2024-01-01T00:00:00Z") | 1252 | .env("GIT_COMMITTER_DATE", "2024-01-01T00:00:00Z") |
| 1201 | .current_dir(clone_path) | 1253 | .current_dir(clone_path) |