upleb.uk

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

summaryrefslogtreecommitdiff
path: root/grasp-audit/src/specs/grasp01/push_authorization.rs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-11-27 15:59:58 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-11-27 15:59:58 +0000
commitc4a35fe8421bc0a0e6608f1b153cb6043230e8b5 (patch)
tree435e7b199f1e66f7d92fb2b2bf0c04b00e8af28a /grasp-audit/src/specs/grasp01/push_authorization.rs
parent33a8870b6015fb989430edbbf5810a2d7d1a5247 (diff)
Task 4: Refactor recursive maintainer push test to fixture-first pattern
- Deprecated setup_repo_for_recursive_maintainer helper in fixtures.rs - test_push_authorized_by_recursive_maintainer_state now creates own TestContext - Uses FixtureKind chain: RepoState, MaintainerAnnouncement, MaintainerState, RecursiveMaintainerRepoAndState - Uses git helpers from fixtures.rs (clone_repo, create_deterministic_commit_with_variant, try_push) - Updated imports to include RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH - All unit tests pass: cargo test --lib
Diffstat (limited to 'grasp-audit/src/specs/grasp01/push_authorization.rs')
-rw-r--r--grasp-audit/src/specs/grasp01/push_authorization.rs287
1 files changed, 245 insertions, 42 deletions
diff --git a/grasp-audit/src/specs/grasp01/push_authorization.rs b/grasp-audit/src/specs/grasp01/push_authorization.rs
index 1de3fe1..f1d6970 100644
--- a/grasp-audit/src/specs/grasp01/push_authorization.rs
+++ b/grasp-audit/src/specs/grasp01/push_authorization.rs
@@ -18,9 +18,9 @@
18 18
19use crate::{ 19use crate::{
20 clone_repo, create_commit, create_deterministic_commit, create_deterministic_commit_with_variant, 20 clone_repo, create_commit, create_deterministic_commit, create_deterministic_commit_with_variant,
21 setup_repo_for_recursive_maintainer, setup_repo_with_deterministic_commit, try_push, 21 setup_repo_with_deterministic_commit, try_push,
22 AuditClient, CommitVariant, FixtureKind, TestContext, TestResult, DETERMINISTIC_COMMIT_HASH, 22 AuditClient, CommitVariant, FixtureKind, TestContext, TestResult, DETERMINISTIC_COMMIT_HASH,
23 MAINTAINER_DETERMINISTIC_COMMIT_HASH, 23 MAINTAINER_DETERMINISTIC_COMMIT_HASH, RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH,
24}; 24};
25use nostr_sdk::prelude::*; 25use nostr_sdk::prelude::*;
26use std::fs; 26use std::fs;
@@ -559,65 +559,268 @@ impl PushAuthorizationTests {
559 /// GRASP-01: "respecting the recursive maintainer set" 559 /// GRASP-01: "respecting the recursive maintainer set"
560 /// This tests recursive maintainer chains: Owner -> MaintainerA -> MaintainerB 560 /// This tests recursive maintainer chains: Owner -> MaintainerA -> MaintainerB
561 /// 561 ///
562 /// Scenario: 562 /// ## Fixture-First Pattern
563 /// 1. RecursiveMaintainerRepoAndState fixture creates: 563 ///
564 /// - Repo announcement signed by recursive_maintainer keys 564 /// 1. **Generate**: Create TestContext and get fixture chain:
565 /// - Lists main pubkey and maintainer pubkey in maintainers tag 565 /// - RepoState (owner's repo announcement + state event)
566 /// - State event with RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH (2s in past) 566 /// - MaintainerAnnouncement (maintainer lists recursive-maintainer)
567 /// 2. setup_repo_for_recursive_maintainer() clones, creates recursive maintainer commit, verifies hash, pushes 567 /// - MaintainerState (maintainer's state event)
568 /// 3. The push should be ACCEPTED because recursive maintainer's state event authorizes it 568 /// - RecursiveMaintainerRepoAndState (recursive maintainer's announcement + state)
569 /// 2. **Send**: Clone repo, create recursive maintainer deterministic commit, push
570 /// 3. **Verify**: Push should succeed because recursive maintainer's state event authorizes it
571 ///
572 /// The fixture chain establishes: Owner -> Maintainer -> RecursiveMaintainer
573 /// Each level publishes announcements that authorize the next level.
569 pub async fn test_push_authorized_by_recursive_maintainer_state( 574 pub async fn test_push_authorized_by_recursive_maintainer_state(
570 client: &AuditClient, 575 client: &AuditClient,
571 git_data_dir: &Path, 576 git_data_dir: &Path,
572 relay_domain: &str, 577 relay_domain: &str,
573 ) -> TestResult { 578 ) -> TestResult {
579 use std::process::Command;
580
574 let test_name = "test_push_authorized_by_recursive_maintainer_state"; 581 let test_name = "test_push_authorized_by_recursive_maintainer_state";
575 582
576 // Use setup_repo_for_recursive_maintainer which leverages RecursiveMaintainerRepoAndState fixture 583 // ============================================================
577 // This does all the heavy lifting: 584 // Step 1: GENERATE - Create TestContext and get fixture chain
578 // 1. Creates repo announcement signed by recursive maintainer keys 585 // ============================================================
579 // 2. Creates state event pointing to RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH 586 let ctx = TestContext::new(client);
580 // 3. Clones the repo 587
581 // 4. Creates the recursive maintainer deterministic commit locally 588 // Get RepoState fixture (owner's repo announcement + state event)
582 // 5. Verifies commit hash matches expected 589 let state_event = match ctx.get_fixture(FixtureKind::RepoState).await {
583 // 6. Creates main branch, checks it out, and pushes 590 Ok(e) => e,
584 match setup_repo_for_recursive_maintainer(client, git_data_dir, relay_domain).await { 591 Err(e) => {
585 Ok(_setup) => { 592 return TestResult::new(
586 // Push succeeded in setup - this means the relay accepted the push
587 // authorized by the recursive maintainer's state event
588 TestResult::new(
589 test_name, 593 test_name,
590 "GRASP-01", 594 "GRASP-01",
591 "Push authorized by recursive maintainer state event", 595 "Push authorized by recursive maintainer state event",
592 ) 596 )
593 .pass() 597 .fail(&format!("Failed to create RepoState fixture: {}", e));
594 } 598 }
599 };
600
601 // Get MaintainerAnnouncement fixture (maintainer's repo announcement listing recursive maintainer)
602 match ctx.get_fixture(FixtureKind::MaintainerAnnouncement).await {
603 Ok(_) => {}
595 Err(e) => { 604 Err(e) => {
596 // Check if this was specifically a push rejection 605 return TestResult::new(
597 if e.contains("Failed to push") { 606 test_name,
598 TestResult::new( 607 "GRASP-01",
599 test_name, 608 "Push authorized by recursive maintainer state event",
600 "GRASP-01", 609 )
601 "Push authorized by recursive maintainer state event", 610 .fail(&format!("Failed to create MaintainerAnnouncement fixture: {}", e));
602 ) 611 }
603 .fail(&format!( 612 };
604 "Push was rejected but should have been accepted. \ 613
605 The recursive maintainer published a state event with a commit hash, \ 614 // Get MaintainerState fixture (maintainer's state event)
606 and the relay should authorize pushes matching this state event \ 615 match ctx.get_fixture(FixtureKind::MaintainerState).await {
607 through recursive maintainer traversal. \ 616 Ok(_) => {}
608 Error: {}", 617 Err(e) => {
609 e 618 return TestResult::new(
610 )) 619 test_name,
611 } else { 620 "GRASP-01",
612 // Some other error during setup 621 "Push authorized by recursive maintainer state event",
613 TestResult::new( 622 )
623 .fail(&format!("Failed to create MaintainerState fixture: {}", e));
624 }
625 };
626
627 // Get RecursiveMaintainerRepoAndState fixture (completes 3-level delegation chain)
628 match ctx.get_fixture(FixtureKind::RecursiveMaintainerRepoAndState).await {
629 Ok(_) => {}
630 Err(e) => {
631 return TestResult::new(
632 test_name,
633 "GRASP-01",
634 "Push authorized by recursive maintainer state event",
635 )
636 .fail(&format!("Failed to create RecursiveMaintainerRepoAndState fixture: {}", e));
637 }
638 };
639
640 tokio::time::sleep(std::time::Duration::from_millis(200)).await;
641
642 // Extract repo_id and npub from owner's state event
643 let repo_id = match state_event
644 .tags
645 .iter()
646 .find(|t| t.kind() == TagKind::d())
647 .and_then(|t| t.content())
648 {
649 Some(id) => id.to_string(),
650 None => {
651 return TestResult::new(
652 test_name,
653 "GRASP-01",
654 "Push authorized by recursive maintainer state event",
655 )
656 .fail("Missing repo_id in state event");
657 }
658 };
659
660 let npub = match state_event.pubkey.to_bech32() {
661 Ok(n) => n,
662 Err(e) => {
663 return TestResult::new(
664 test_name,
665 "GRASP-01",
666 "Push authorized by recursive maintainer state event",
667 )
668 .fail(&format!("Failed to convert pubkey to bech32: {}", e));
669 }
670 };
671
672 // Verify repo exists on disk
673 let repo_path = git_data_dir.join(&npub).join(format!("{}.git", repo_id));
674 if !repo_path.exists() {
675 return TestResult::new(
676 test_name,
677 "GRASP-01",
678 "Push authorized by recursive maintainer state event",
679 )
680 .fail(&format!("Repo not found: {}", repo_path.display()));
681 }
682
683 // ============================================================
684 // Step 2: SEND - Clone, create recursive maintainer commit, push
685 // ============================================================
686 let clone_path = match clone_repo(relay_domain, &npub, &repo_id) {
687 Ok(p) => p,
688 Err(e) => {
689 return TestResult::new(
690 test_name,
691 "GRASP-01",
692 "Push authorized by recursive maintainer state event",
693 )
694 .fail(&e);
695 }
696 };
697 let cleanup = || {
698 let _ = fs::remove_dir_all(&clone_path);
699 };
700
701 // Create recursive maintainer deterministic commit
702 let commit_hash =
703 match create_deterministic_commit_with_variant(&clone_path, CommitVariant::RecursiveMaintainer) {
704 Ok(h) => h,
705 Err(e) => {
706 cleanup();
707 return TestResult::new(
614 test_name, 708 test_name,
615 "GRASP-01", 709 "GRASP-01",
616 "Push authorized by recursive maintainer state event", 710 "Push authorized by recursive maintainer state event",
617 ) 711 )
618 .fail(&format!("Setup failed: {}", e)) 712 .fail(&format!("Failed to create recursive maintainer commit: {}", e));
619 } 713 }
714 };
715
716 // Verify commit hash matches expected
717 if commit_hash != RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH {
718 cleanup();
719 return TestResult::new(
720 test_name,
721 "GRASP-01",
722 "Push authorized by recursive maintainer state event",
723 )
724 .fail(&format!(
725 "Recursive maintainer commit hash mismatch: got {}, expected {}",
726 commit_hash, RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH
727 ));
728 }
729
730 // Create main branch
731 let branch_output = Command::new("git")
732 .args(["branch", "main"])
733 .current_dir(&clone_path)
734 .output();
735
736 match branch_output {
737 Err(e) => {
738 cleanup();
739 return TestResult::new(
740 test_name,
741 "GRASP-01",
742 "Push authorized by recursive maintainer state event",
743 )
744 .fail(&format!("Failed to create main branch: {}", e));
745 }
746 Ok(output) if !output.status.success() => {
747 cleanup();
748 return TestResult::new(
749 test_name,
750 "GRASP-01",
751 "Push authorized by recursive maintainer state event",
752 )
753 .fail(&format!(
754 "Failed to create main branch: {}",
755 String::from_utf8_lossy(&output.stderr)
756 ));
757 }
758 _ => {}
759 }
760
761 // Checkout main branch
762 let checkout_output = Command::new("git")
763 .args(["checkout", "main"])
764 .current_dir(&clone_path)
765 .output();
766
767 match checkout_output {
768 Err(e) => {
769 cleanup();
770 return TestResult::new(
771 test_name,
772 "GRASP-01",
773 "Push authorized by recursive maintainer state event",
774 )
775 .fail(&format!("Failed to checkout main branch: {}", e));
776 }
777 Ok(output) if !output.status.success() => {
778 cleanup();
779 return TestResult::new(
780 test_name,
781 "GRASP-01",
782 "Push authorized by recursive maintainer state event",
783 )
784 .fail(&format!(
785 "Failed to checkout main branch: {}",
786 String::from_utf8_lossy(&output.stderr)
787 ));
620 } 788 }
789 _ => {}
790 }
791
792 // ============================================================
793 // Step 3: VERIFY - Push should succeed because recursive
794 // maintainer's state event authorizes this commit
795 // ============================================================
796 let push_result = try_push(&clone_path);
797 cleanup();
798
799 match push_result {
800 Ok(true) => TestResult::new(
801 test_name,
802 "GRASP-01",
803 "Push authorized by recursive maintainer state event",
804 )
805 .pass(),
806 Ok(false) => TestResult::new(
807 test_name,
808 "GRASP-01",
809 "Push authorized by recursive maintainer state event",
810 )
811 .fail(&format!(
812 "Push was rejected but should have been accepted. \
813 The recursive maintainer published a state event with commit {}, \
814 and the relay should authorize pushes matching this state event \
815 through recursive maintainer traversal (Owner -> Maintainer -> RecursiveMaintainer).",
816 RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH
817 )),
818 Err(e) => TestResult::new(
819 test_name,
820 "GRASP-01",
821 "Push authorized by recursive maintainer state event",
822 )
823 .fail(&format!("Push error: {}", e)),
621 } 824 }
622 } 825 }
623 826