diff options
| -rw-r--r-- | grasp-audit/src/specs/grasp01/event_acceptance_policy.rs | 114 |
1 files changed, 17 insertions, 97 deletions
diff --git a/grasp-audit/src/specs/grasp01/event_acceptance_policy.rs b/grasp-audit/src/specs/grasp01/event_acceptance_policy.rs index c1977f9..3a8f18d 100644 --- a/grasp-audit/src/specs/grasp01/event_acceptance_policy.rs +++ b/grasp-audit/src/specs/grasp01/event_acceptance_policy.rs | |||
| @@ -88,6 +88,7 @@ | |||
| 88 | //! - Forward reference tests verify out-of-order event acceptance | 88 | //! - Forward reference tests verify out-of-order event acceptance |
| 89 | //! - Transitive tests verify multi-hop acceptance chains | 89 | //! - Transitive tests verify multi-hop acceptance chains |
| 90 | 90 | ||
| 91 | use crate::fixtures::{send_and_verify_accepted, send_and_verify_rejected}; | ||
| 91 | use crate::{AuditClient, AuditResult, FixtureKind, TestContext, TestResult}; | 92 | use crate::{AuditClient, AuditResult, FixtureKind, TestContext, TestResult}; |
| 92 | use nostr_sdk::{Event, Filter, Kind, Tag, TagKind, Timestamp}; | 93 | use nostr_sdk::{Event, Filter, Kind, Tag, TagKind, Timestamp}; |
| 93 | use std::time::Duration; | 94 | use std::time::Duration; |
| @@ -515,87 +516,6 @@ impl EventAcceptancePolicyTests { | |||
| 515 | .map_err(|e| format!("Test setup failed: could not create test comment: {}", e)) | 516 | .map_err(|e| format!("Test setup failed: could not create test comment: {}", e)) |
| 516 | } | 517 | } |
| 517 | 518 | ||
| 518 | /// Send event and verify it was accepted (stored by relay) | ||
| 519 | async fn send_and_verify_accepted( | ||
| 520 | client: &AuditClient, | ||
| 521 | event: Event, | ||
| 522 | description: &str, | ||
| 523 | ) -> Result<(), String> { | ||
| 524 | let event_id = event.id; | ||
| 525 | |||
| 526 | client | ||
| 527 | .send_event(event) | ||
| 528 | .await | ||
| 529 | .map_err(|e| format!("Failed to send event to relay: {}", e))?; | ||
| 530 | |||
| 531 | tokio::time::sleep(Duration::from_millis(100)).await; | ||
| 532 | |||
| 533 | let filter = Filter::new().id(event_id); | ||
| 534 | let events = client | ||
| 535 | .query(filter) | ||
| 536 | .await | ||
| 537 | .map_err(|e| format!("Failed to query relay for verification: {}", e))?; | ||
| 538 | |||
| 539 | if events.is_empty() { | ||
| 540 | return Err(format!("Event should be accepted: {}", description)); | ||
| 541 | } | ||
| 542 | |||
| 543 | Ok(()) | ||
| 544 | } | ||
| 545 | |||
| 546 | /// Send event and verify it was rejected (NOT stored by relay) | ||
| 547 | async fn send_and_verify_rejected( | ||
| 548 | client: &AuditClient, | ||
| 549 | event: Event, | ||
| 550 | description: &str, | ||
| 551 | ) -> Result<(), String> { | ||
| 552 | let event_id = event.id; | ||
| 553 | |||
| 554 | // Try to send event - rejection may cause send_event to fail with an error | ||
| 555 | let send_result = client.send_event(event).await; | ||
| 556 | |||
| 557 | // If send succeeded, the relay might have accepted it (we'll verify below) | ||
| 558 | // If send failed, check if it's a rejection error (expected) | ||
| 559 | if let Err(e) = send_result { | ||
| 560 | let err_msg = e.to_string().to_lowercase(); | ||
| 561 | // Check if error message indicates rejection (not network/other errors) | ||
| 562 | if err_msg.contains("rejected") || err_msg.contains("blocked") { | ||
| 563 | // Expected rejection - verify event is NOT in database | ||
| 564 | tokio::time::sleep(Duration::from_millis(100)).await; | ||
| 565 | |||
| 566 | let filter = Filter::new().id(event_id); | ||
| 567 | let events = client | ||
| 568 | .query(filter) | ||
| 569 | .await | ||
| 570 | .map_err(|e| format!("Failed to query relay for verification: {}", e))?; | ||
| 571 | |||
| 572 | if !events.is_empty() { | ||
| 573 | return Err(format!("Event was rejected but still stored: {}", description)); | ||
| 574 | } | ||
| 575 | |||
| 576 | return Ok(()); // Rejected as expected | ||
| 577 | } else { | ||
| 578 | // Unexpected error (network, etc.) | ||
| 579 | return Err(format!("Failed to send event to relay: {}", e)); | ||
| 580 | } | ||
| 581 | } | ||
| 582 | |||
| 583 | // Send succeeded, verify event was NOT stored (relay should have rejected) | ||
| 584 | tokio::time::sleep(Duration::from_millis(100)).await; | ||
| 585 | |||
| 586 | let filter = Filter::new().id(event_id); | ||
| 587 | let events = client | ||
| 588 | .query(filter) | ||
| 589 | .await | ||
| 590 | .map_err(|e| format!("Failed to query relay for verification: {}", e))?; | ||
| 591 | |||
| 592 | if !events.is_empty() { | ||
| 593 | return Err(format!("Event should be rejected: {}", description)); | ||
| 594 | } | ||
| 595 | |||
| 596 | Ok(()) | ||
| 597 | } | ||
| 598 | |||
| 599 | // ============================================================ | 519 | // ============================================================ |
| 600 | // Group 1: Accept Events Tagging Accepted Repositories (3 tests) | 520 | // Group 1: Accept Events Tagging Accepted Repositories (3 tests) |
| 601 | // ============================================================ | 521 | // ============================================================ |
| @@ -626,7 +546,7 @@ impl EventAcceptancePolicyTests { | |||
| 626 | let issue = Self::create_issue_for_repo(client, &repo, "Test Issue 1")?; | 546 | let issue = Self::create_issue_for_repo(client, &repo, "Test Issue 1")?; |
| 627 | 547 | ||
| 628 | // 3. Send issue and verify it's accepted | 548 | // 3. Send issue and verify it's accepted |
| 629 | Self::send_and_verify_accepted(client, issue, "issue referencing repo via 'a' tag") | 549 | send_and_verify_accepted(client, issue, "issue referencing repo via 'a' tag") |
| 630 | .await?; | 550 | .await?; |
| 631 | 551 | ||
| 632 | Ok(()) | 552 | Ok(()) |
| @@ -679,7 +599,7 @@ impl EventAcceptancePolicyTests { | |||
| 679 | .map_err(|e| format!("Failed to build comment: {}", e))?; | 599 | .map_err(|e| format!("Failed to build comment: {}", e))?; |
| 680 | 600 | ||
| 681 | // Send comment and verify it's accepted | 601 | // Send comment and verify it's accepted |
| 682 | Self::send_and_verify_accepted(client, comment, "comment with 'A' tag to repo").await?; | 602 | send_and_verify_accepted(client, comment, "comment with 'A' tag to repo").await?; |
| 683 | 603 | ||
| 684 | Ok(()) | 604 | Ok(()) |
| 685 | }) | 605 | }) |
| @@ -724,7 +644,7 @@ impl EventAcceptancePolicyTests { | |||
| 724 | .map_err(|e| format!("Failed to build note: {}", e))?; | 644 | .map_err(|e| format!("Failed to build note: {}", e))?; |
| 725 | 645 | ||
| 726 | // Send note and verify it's accepted | 646 | // Send note and verify it's accepted |
| 727 | Self::send_and_verify_accepted(client, note, "kind 1 with 'q' tag to repo").await?; | 647 | send_and_verify_accepted(client, note, "kind 1 with 'q' tag to repo").await?; |
| 728 | 648 | ||
| 729 | Ok(()) | 649 | Ok(()) |
| 730 | }) | 650 | }) |
| @@ -773,7 +693,7 @@ impl EventAcceptancePolicyTests { | |||
| 773 | .map_err(|e| format!("Failed to build issue B: {}", e))?; | 693 | .map_err(|e| format!("Failed to build issue B: {}", e))?; |
| 774 | 694 | ||
| 775 | // Send Issue B and verify it's ACCEPTED (via transitive quote to Issue A) | 695 | // Send Issue B and verify it's ACCEPTED (via transitive quote to Issue A) |
| 776 | Self::send_and_verify_accepted(client, issue_b, "issue B quoting accepted issue A") | 696 | send_and_verify_accepted(client, issue_b, "issue B quoting accepted issue A") |
| 777 | .await?; | 697 | .await?; |
| 778 | 698 | ||
| 779 | Ok(()) | 699 | Ok(()) |
| @@ -811,7 +731,7 @@ impl EventAcceptancePolicyTests { | |||
| 811 | let comment = Self::create_comment_for_event(client, &issue, "Comment content")?; | 731 | let comment = Self::create_comment_for_event(client, &issue, "Comment content")?; |
| 812 | 732 | ||
| 813 | // Send comment and verify it's accepted (via E tag to accepted issue) | 733 | // Send comment and verify it's accepted (via E tag to accepted issue) |
| 814 | Self::send_and_verify_accepted(client, comment, "comment with E tag to accepted issue") | 734 | send_and_verify_accepted(client, comment, "comment with E tag to accepted issue") |
| 815 | .await?; | 735 | .await?; |
| 816 | 736 | ||
| 817 | Ok(()) | 737 | Ok(()) |
| @@ -852,7 +772,7 @@ impl EventAcceptancePolicyTests { | |||
| 852 | .build(client.keys()) | 772 | .build(client.keys()) |
| 853 | .map_err(|e| format!("Failed to build kind1 A: {}", e))?; | 773 | .map_err(|e| format!("Failed to build kind1 A: {}", e))?; |
| 854 | 774 | ||
| 855 | Self::send_and_verify_accepted(client, kind1_a.clone(), "kind 1 A quoting repo") | 775 | send_and_verify_accepted(client, kind1_a.clone(), "kind 1 A quoting repo") |
| 856 | .await?; | 776 | .await?; |
| 857 | 777 | ||
| 858 | // Create Kind 1 B that replies to Kind 1 A via 'e' tag | 778 | // Create Kind 1 B that replies to Kind 1 A via 'e' tag |
| @@ -863,7 +783,7 @@ impl EventAcceptancePolicyTests { | |||
| 863 | .map_err(|e| format!("Failed to build kind1 B: {}", e))?; | 783 | .map_err(|e| format!("Failed to build kind1 B: {}", e))?; |
| 864 | 784 | ||
| 865 | // Send Kind 1 B and verify it's accepted (via 'e' tag to accepted kind 1 A) | 785 | // Send Kind 1 B and verify it's accepted (via 'e' tag to accepted kind 1 A) |
| 866 | Self::send_and_verify_accepted( | 786 | send_and_verify_accepted( |
| 867 | client, | 787 | client, |
| 868 | kind1_b, | 788 | kind1_b, |
| 869 | "kind 1 B replying to accepted kind 1 A", | 789 | "kind 1 B replying to accepted kind 1 A", |
| @@ -953,10 +873,10 @@ impl EventAcceptancePolicyTests { | |||
| 953 | .build(client.keys()) | 873 | .build(client.keys()) |
| 954 | .map_err(|e| format!("Failed to build issue: {}", e))?; | 874 | .map_err(|e| format!("Failed to build issue: {}", e))?; |
| 955 | 875 | ||
| 956 | Self::send_and_verify_accepted(client, issue, "issue quoting unsent kind1").await?; | 876 | send_and_verify_accepted(client, issue, "issue quoting unsent kind1").await?; |
| 957 | 877 | ||
| 958 | // NOW send the Kind 1 note - should be accepted because accepted issue quotes it | 878 | // NOW send the Kind 1 note - should be accepted because accepted issue quotes it |
| 959 | Self::send_and_verify_accepted( | 879 | send_and_verify_accepted( |
| 960 | client, | 880 | client, |
| 961 | kind1_note, | 881 | kind1_note, |
| 962 | "kind1 note referenced by accepted issue", | 882 | "kind1 note referenced by accepted issue", |
| @@ -1017,11 +937,11 @@ impl EventAcceptancePolicyTests { | |||
| 1017 | .build(client.keys()) | 937 | .build(client.keys()) |
| 1018 | .map_err(|e| format!("Failed to build comment B: {}", e))?; | 938 | .map_err(|e| format!("Failed to build comment B: {}", e))?; |
| 1019 | 939 | ||
| 1020 | Self::send_and_verify_accepted(client, comment_b, "comment B quoting unsent comment A") | 940 | send_and_verify_accepted(client, comment_b, "comment B quoting unsent comment A") |
| 1021 | .await?; | 941 | .await?; |
| 1022 | 942 | ||
| 1023 | // NOW send Comment A - should be accepted because accepted Comment B quotes it | 943 | // NOW send Comment A - should be accepted because accepted Comment B quotes it |
| 1024 | Self::send_and_verify_accepted( | 944 | send_and_verify_accepted( |
| 1025 | client, | 945 | client, |
| 1026 | comment_a, | 946 | comment_a, |
| 1027 | "comment A referenced by accepted comment B", | 947 | "comment A referenced by accepted comment B", |
| @@ -1077,11 +997,11 @@ impl EventAcceptancePolicyTests { | |||
| 1077 | .build(client.keys()) | 997 | .build(client.keys()) |
| 1078 | .map_err(|e| format!("Failed to build kind1 B: {}", e))?; | 998 | .map_err(|e| format!("Failed to build kind1 B: {}", e))?; |
| 1079 | 999 | ||
| 1080 | Self::send_and_verify_accepted(client, kind1_b, "kind1 B mentioning unsent kind1 A") | 1000 | send_and_verify_accepted(client, kind1_b, "kind1 B mentioning unsent kind1 A") |
| 1081 | .await?; | 1001 | .await?; |
| 1082 | 1002 | ||
| 1083 | // NOW send Kind 1 A - should be accepted because accepted Kind 1 B mentions it | 1003 | // NOW send Kind 1 A - should be accepted because accepted Kind 1 B mentions it |
| 1084 | Self::send_and_verify_accepted( | 1004 | send_and_verify_accepted( |
| 1085 | client, | 1005 | client, |
| 1086 | kind1_a, | 1006 | kind1_a, |
| 1087 | "kind1 A referenced by accepted kind1 B", | 1007 | "kind1 A referenced by accepted kind1 B", |
| @@ -1113,7 +1033,7 @@ impl EventAcceptancePolicyTests { | |||
| 1113 | Self::create_issue_for_repo(client, &unaccepted_repo, "Orphan Issue")?; | 1033 | Self::create_issue_for_repo(client, &unaccepted_repo, "Orphan Issue")?; |
| 1114 | 1034 | ||
| 1115 | // 3. Send issue and verify it's REJECTED | 1035 | // 3. Send issue and verify it's REJECTED |
| 1116 | Self::send_and_verify_rejected( | 1036 | send_and_verify_rejected( |
| 1117 | client, | 1037 | client, |
| 1118 | orphan_issue, | 1038 | orphan_issue, |
| 1119 | "issue referencing unaccepted repo", | 1039 | "issue referencing unaccepted repo", |
| @@ -1140,7 +1060,7 @@ impl EventAcceptancePolicyTests { | |||
| 1140 | .map_err(|e| format!("Failed to build note: {}", e))?; | 1060 | .map_err(|e| format!("Failed to build note: {}", e))?; |
| 1141 | 1061 | ||
| 1142 | // 2. Send note and verify it's REJECTED | 1062 | // 2. Send note and verify it's REJECTED |
| 1143 | Self::send_and_verify_rejected(client, orphan_note, "kind 1 with no repo references") | 1063 | send_and_verify_rejected(client, orphan_note, "kind 1 with no repo references") |
| 1144 | .await?; | 1064 | .await?; |
| 1145 | 1065 | ||
| 1146 | Ok(()) | 1066 | Ok(()) |
| @@ -1196,7 +1116,7 @@ impl EventAcceptancePolicyTests { | |||
| 1196 | .map_err(|e| format!("Failed to build comment: {}", e))?; | 1116 | .map_err(|e| format!("Failed to build comment: {}", e))?; |
| 1197 | 1117 | ||
| 1198 | // Send comment and verify it's REJECTED (only references unaccepted repo B) | 1118 | // Send comment and verify it's REJECTED (only references unaccepted repo B) |
| 1199 | Self::send_and_verify_rejected(client, comment, "comment quoting only unaccepted repo") | 1119 | send_and_verify_rejected(client, comment, "comment quoting only unaccepted repo") |
| 1200 | .await?; | 1120 | .await?; |
| 1201 | 1121 | ||
| 1202 | Ok(()) | 1122 | Ok(()) |