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:
Diffstat (limited to 'grasp-audit/src/specs/grasp01/push_authorization.rs')
-rw-r--r--grasp-audit/src/specs/grasp01/push_authorization.rs194
1 files changed, 14 insertions, 180 deletions
diff --git a/grasp-audit/src/specs/grasp01/push_authorization.rs b/grasp-audit/src/specs/grasp01/push_authorization.rs
index cd422d2..1e28f8c 100644
--- a/grasp-audit/src/specs/grasp01/push_authorization.rs
+++ b/grasp-audit/src/specs/grasp01/push_authorization.rs
@@ -587,197 +587,31 @@ impl PushAuthorizationTests {
587 /// GRASP-01: "MUST accept pushes via this service that match the latest 587 /// GRASP-01: "MUST accept pushes via this service that match the latest
588 /// repo state announcement on the relay" 588 /// repo state announcement on the relay"
589 /// 589 ///
590 /// ## Fixture-First Pattern 590 /// This test uses the OwnerStateDataPushed fixture which handles all 4 stages:
591 /// 1. **Generated**: Creates RepoState (repo announcement + state event)
592 /// 2. **Sent**: Sends events to relay
593 /// 3. **Verified**: Confirms events accepted by relay
594 /// 4. **DataPushed**: Clones repo, creates deterministic commit, pushes to relay
591 /// 595 ///
592 /// 1. **Generate**: Create TestContext and get RepoState fixture 596 /// The test wraps the fixture result in pass/fail using the error message.
593 /// (repo announcement + state event pointing to deterministic commit) 597 #[allow(unused_variables)] // relay_domain is now handled by fixture
594 /// 2. **Send**: Clone repo, create deterministic commit locally, push to relay
595 /// 3. **Verify**: Push should succeed because state event authorizes this commit
596 pub async fn test_push_authorized_by_owner_state( 598 pub async fn test_push_authorized_by_owner_state(
597 client: &AuditClient, 599 client: &AuditClient,
598 relay_domain: &str, 600 relay_domain: &str,
599 ) -> TestResult { 601 ) -> TestResult {
600 use std::process::Command;
601
602 let test_name = "test_push_authorized_by_owner_state"; 602 let test_name = "test_push_authorized_by_owner_state";
603
604 // ============================================================
605 // Step 1: GENERATE - Create TestContext and get RepoState fixture
606 // ============================================================
607 let ctx = TestContext::new(client); 603 let ctx = TestContext::new(client);
608 604
609 let state_event = match ctx.get_fixture(FixtureKind::RepoState).await { 605 // The OwnerStateDataPushed fixture handles all stages:
610 Ok(e) => e, 606 // Generate → Send → Verify → DataPush
611 Err(e) => { 607 match ctx.get_fixture(FixtureKind::OwnerStateDataPushed).await {
612 return TestResult::new( 608 Ok(_state_event) => {
613 test_name,
614 "GRASP-01",
615 "Push authorized with matching state",
616 )
617 .fail(format!("Failed to create RepoState fixture: {}", e));
618 }
619 };
620
621 tokio::time::sleep(std::time::Duration::from_millis(200)).await;
622
623 // Extract repo_id and npub from state event
624 let repo_id = match state_event
625 .tags
626 .iter()
627 .find(|t| t.kind() == TagKind::d())
628 .and_then(|t| t.content())
629 {
630 Some(id) => id.to_string(),
631 None => {
632 return TestResult::new(
633 test_name,
634 "GRASP-01",
635 "Push authorized with matching state",
636 )
637 .fail("Missing repo_id in state event");
638 }
639 };
640
641 let npub = match state_event.pubkey.to_bech32() {
642 Ok(n) => n,
643 Err(e) => {
644 return TestResult::new(
645 test_name,
646 "GRASP-01",
647 "Push authorized with matching state",
648 )
649 .fail(format!("Failed to convert pubkey to bech32: {}", e));
650 }
651 };
652
653 // ============================================================
654 // Step 2: SEND - Clone repo, create deterministic commit, push
655 // ============================================================
656 let clone_path = match clone_repo(relay_domain, &npub, &repo_id) {
657 Ok(p) => p,
658 Err(e) => {
659 return TestResult::new(
660 test_name,
661 "GRASP-01",
662 "Push authorized with matching state",
663 )
664 .fail(format!("Failed to clone repo: {}", e));
665 }
666 };
667
668 // Cleanup helper
669 let cleanup = || {
670 let _ = fs::remove_dir_all(&clone_path);
671 };
672
673 // Create deterministic commit locally
674 let commit_hash = match create_deterministic_commit(&clone_path, "Initial commit") {
675 Ok(h) => h,
676 Err(e) => {
677 cleanup();
678 return TestResult::new(
679 test_name,
680 "GRASP-01",
681 "Push authorized with matching state",
682 )
683 .fail(format!("Failed to create deterministic commit: {}", e));
684 }
685 };
686
687 // Verify commit hash matches expected
688 if commit_hash != DETERMINISTIC_COMMIT_HASH {
689 cleanup();
690 return TestResult::new(test_name, "GRASP-01", "Push authorized with matching state")
691 .fail(format!(
692 "Commit hash mismatch: got {}, expected {}",
693 commit_hash, DETERMINISTIC_COMMIT_HASH
694 ));
695 }
696
697 // Create main branch pointing to our deterministic commit
698 let branch_output = Command::new("git")
699 .args(["branch", "main"])
700 .current_dir(&clone_path)
701 .output();
702
703 match branch_output {
704 Err(e) => {
705 cleanup();
706 return TestResult::new(
707 test_name,
708 "GRASP-01",
709 "Push authorized with matching state",
710 )
711 .fail(format!("Failed to create main branch: {}", e));
712 }
713 Ok(output) if !output.status.success() => {
714 cleanup();
715 return TestResult::new(
716 test_name,
717 "GRASP-01",
718 "Push authorized with matching state",
719 )
720 .fail(format!(
721 "Failed to create main branch: {}",
722 String::from_utf8_lossy(&output.stderr)
723 ));
724 }
725 _ => {}
726 }
727
728 // Checkout main branch
729 let checkout_output = Command::new("git")
730 .args(["checkout", "main"])
731 .current_dir(&clone_path)
732 .output();
733
734 match checkout_output {
735 Err(e) => {
736 cleanup();
737 return TestResult::new(
738 test_name,
739 "GRASP-01",
740 "Push authorized with matching state",
741 )
742 .fail(format!("Failed to checkout main branch: {}", e));
743 }
744 Ok(output) if !output.status.success() => {
745 cleanup();
746 return TestResult::new(
747 test_name,
748 "GRASP-01",
749 "Push authorized with matching state",
750 )
751 .fail(format!(
752 "Failed to checkout main branch: {}",
753 String::from_utf8_lossy(&output.stderr)
754 ));
755 }
756 _ => {}
757 }
758
759 // ============================================================
760 // Step 3: VERIFY - Push should succeed because state event
761 // authorizes this commit
762 // ============================================================
763 let push_result = try_push(&clone_path);
764 cleanup();
765
766 match push_result {
767 Ok(true) => {
768 TestResult::new(test_name, "GRASP-01", "Push authorized with matching state").pass() 609 TestResult::new(test_name, "GRASP-01", "Push authorized with matching state").pass()
769 } 610 }
770 Ok(false) => { 611 Err(e) => {
771 TestResult::new(test_name, "GRASP-01", "Push authorized with matching state").fail( 612 TestResult::new(test_name, "GRASP-01", "Push authorized with matching state")
772 format!( 613 .fail(format!("{}", e))
773 "Push was rejected but should have been accepted. \
774 The state event points to commit {} which matches the pushed commit.",
775 DETERMINISTIC_COMMIT_HASH
776 ),
777 )
778 } 614 }
779 Err(e) => TestResult::new(test_name, "GRASP-01", "Push authorized with matching state")
780 .fail(format!("Push error: {}", e)),
781 } 615 }
782 } 616 }
783 617