diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-12-01 14:31:32 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-12-01 15:22:38 +0000 |
| commit | d2ac69816567f092fe0d4661723bc43778cb481b (patch) | |
| tree | e8b51b61a6a7b0ab1a214adebe4e237143b01f0b /grasp-audit/src/specs | |
| parent | 7a78815e29b01c83f3d0ec195ba717a2eba8cd37 (diff) | |
fix cargo clippy and fmt warnings
Diffstat (limited to 'grasp-audit/src/specs')
| -rw-r--r-- | grasp-audit/src/specs/grasp01/cors.rs | 51 | ||||
| -rw-r--r-- | grasp-audit/src/specs/grasp01/event_acceptance_policy.rs | 62 | ||||
| -rw-r--r-- | grasp-audit/src/specs/grasp01/git_clone.rs | 54 | ||||
| -rw-r--r-- | grasp-audit/src/specs/grasp01/mod.rs | 2 | ||||
| -rw-r--r-- | grasp-audit/src/specs/grasp01/nip01_smoke.rs | 32 | ||||
| -rw-r--r-- | grasp-audit/src/specs/grasp01/nip11_document.rs | 61 | ||||
| -rw-r--r-- | grasp-audit/src/specs/grasp01/push_authorization.rs | 10 | ||||
| -rw-r--r-- | grasp-audit/src/specs/grasp01/repository_creation.rs | 20 |
8 files changed, 114 insertions, 178 deletions
diff --git a/grasp-audit/src/specs/grasp01/cors.rs b/grasp-audit/src/specs/grasp01/cors.rs index 08c5ab6..c877c04 100644 --- a/grasp-audit/src/specs/grasp01/cors.rs +++ b/grasp-audit/src/specs/grasp01/cors.rs | |||
| @@ -16,7 +16,6 @@ | |||
| 16 | 16 | ||
| 17 | use crate::{AuditClient, AuditResult, FixtureKind, TestContext, TestResult}; | 17 | use crate::{AuditClient, AuditResult, FixtureKind, TestContext, TestResult}; |
| 18 | use nostr_sdk::prelude::*; | 18 | use nostr_sdk::prelude::*; |
| 19 | use std::path::Path; | ||
| 20 | 19 | ||
| 21 | pub struct CorsTests; | 20 | pub struct CorsTests; |
| 22 | 21 | ||
| @@ -42,10 +41,7 @@ impl CorsTests { | |||
| 42 | /// | 41 | /// |
| 43 | /// Spec: Line 44 of ../grasp/01.md | 42 | /// Spec: Line 44 of ../grasp/01.md |
| 44 | /// Requirement: Set `Access-Control-Allow-Origin: *` on ALL responses | 43 | /// Requirement: Set `Access-Control-Allow-Origin: *` on ALL responses |
| 45 | pub async fn test_cors_allow_origin( | 44 | pub async fn test_cors_allow_origin(_client: &AuditClient, relay_domain: &str) -> TestResult { |
| 46 | _client: &AuditClient, | ||
| 47 | relay_domain: &str, | ||
| 48 | ) -> TestResult { | ||
| 49 | TestResult::new( | 45 | TestResult::new( |
| 50 | "cors_allow_origin", | 46 | "cors_allow_origin", |
| 51 | "GRASP-01:git-http:cors:44", | 47 | "GRASP-01:git-http:cors:44", |
| @@ -91,10 +87,7 @@ impl CorsTests { | |||
| 91 | /// | 87 | /// |
| 92 | /// Spec: Line 45 of ../grasp/01.md | 88 | /// Spec: Line 45 of ../grasp/01.md |
| 93 | /// Requirement: Set `Access-Control-Allow-Methods: GET, POST` on ALL responses | 89 | /// Requirement: Set `Access-Control-Allow-Methods: GET, POST` on ALL responses |
| 94 | pub async fn test_cors_allow_methods( | 90 | pub async fn test_cors_allow_methods(_client: &AuditClient, relay_domain: &str) -> TestResult { |
| 95 | _client: &AuditClient, | ||
| 96 | relay_domain: &str, | ||
| 97 | ) -> TestResult { | ||
| 98 | TestResult::new( | 91 | TestResult::new( |
| 99 | "cors_allow_methods", | 92 | "cors_allow_methods", |
| 100 | "GRASP-01:git-http:cors:45", | 93 | "GRASP-01:git-http:cors:45", |
| @@ -138,10 +131,7 @@ impl CorsTests { | |||
| 138 | /// | 131 | /// |
| 139 | /// Spec: Line 46 of ../grasp/01.md | 132 | /// Spec: Line 46 of ../grasp/01.md |
| 140 | /// Requirement: Set `Access-Control-Allow-Headers: Content-Type` on ALL responses | 133 | /// Requirement: Set `Access-Control-Allow-Headers: Content-Type` on ALL responses |
| 141 | pub async fn test_cors_allow_headers( | 134 | pub async fn test_cors_allow_headers(_client: &AuditClient, relay_domain: &str) -> TestResult { |
| 142 | _client: &AuditClient, | ||
| 143 | relay_domain: &str, | ||
| 144 | ) -> TestResult { | ||
| 145 | TestResult::new( | 135 | TestResult::new( |
| 146 | "cors_allow_headers", | 136 | "cors_allow_headers", |
| 147 | "GRASP-01:git-http:cors:46", | 137 | "GRASP-01:git-http:cors:46", |
| @@ -212,10 +202,8 @@ impl CorsTests { | |||
| 212 | check_options_response(&response, "root endpoint")?; | 202 | check_options_response(&response, "root endpoint")?; |
| 213 | 203 | ||
| 214 | // 2. Test OPTIONS on git-upload-pack endpoint | 204 | // 2. Test OPTIONS on git-upload-pack endpoint |
| 215 | let repo_url = format!( | 205 | let repo_url = |
| 216 | "http://{}/npub1test/test.git/git-upload-pack", | 206 | format!("http://{}/npub1test/test.git/git-upload-pack", relay_domain); |
| 217 | relay_domain | ||
| 218 | ); | ||
| 219 | let response = http_client | 207 | let response = http_client |
| 220 | .request(reqwest::Method::OPTIONS, &repo_url) | 208 | .request(reqwest::Method::OPTIONS, &repo_url) |
| 221 | .header("Origin", "https://example.com") | 209 | .header("Origin", "https://example.com") |
| @@ -227,10 +215,7 @@ impl CorsTests { | |||
| 227 | check_options_response(&response, "git-upload-pack endpoint")?; | 215 | check_options_response(&response, "git-upload-pack endpoint")?; |
| 228 | 216 | ||
| 229 | // 3. Test OPTIONS on info/refs endpoint | 217 | // 3. Test OPTIONS on info/refs endpoint |
| 230 | let refs_url = format!( | 218 | let refs_url = format!("http://{}/npub1test/test.git/info/refs", relay_domain); |
| 231 | "http://{}/npub1test/test.git/info/refs", | ||
| 232 | relay_domain | ||
| 233 | ); | ||
| 234 | let response = http_client | 219 | let response = http_client |
| 235 | .request(reqwest::Method::OPTIONS, &refs_url) | 220 | .request(reqwest::Method::OPTIONS, &refs_url) |
| 236 | .header("Origin", "https://example.com") | 221 | .header("Origin", "https://example.com") |
| @@ -255,10 +240,7 @@ impl CorsTests { | |||
| 255 | /// Integration test: CORS Allow-Origin header with repository creation | 240 | /// Integration test: CORS Allow-Origin header with repository creation |
| 256 | /// | 241 | /// |
| 257 | /// For integration tests that want to test against real repositories | 242 | /// For integration tests that want to test against real repositories |
| 258 | pub async fn test_cors_on_real_repo( | 243 | pub async fn test_cors_on_real_repo(client: &AuditClient, relay_domain: &str) -> TestResult { |
| 259 | client: &AuditClient, | ||
| 260 | relay_domain: &str, | ||
| 261 | ) -> TestResult { | ||
| 262 | let test_name = "test_cors_on_real_repo"; | 244 | let test_name = "test_cors_on_real_repo"; |
| 263 | let ctx = TestContext::new(client); | 245 | let ctx = TestContext::new(client); |
| 264 | 246 | ||
| @@ -271,7 +253,7 @@ impl CorsTests { | |||
| 271 | "GRASP-01", | 253 | "GRASP-01", |
| 272 | "CORS headers on real repository endpoint", | 254 | "CORS headers on real repository endpoint", |
| 273 | ) | 255 | ) |
| 274 | .fail(&format!("Failed to create repo fixture: {}", e)) | 256 | .fail(format!("Failed to create repo fixture: {}", e)) |
| 275 | } | 257 | } |
| 276 | }; | 258 | }; |
| 277 | 259 | ||
| @@ -304,7 +286,7 @@ impl CorsTests { | |||
| 304 | "GRASP-01", | 286 | "GRASP-01", |
| 305 | "CORS headers on real repository endpoint", | 287 | "CORS headers on real repository endpoint", |
| 306 | ) | 288 | ) |
| 307 | .fail(&format!("Failed to convert pubkey to npub: {}", e)) | 289 | .fail(format!("Failed to convert pubkey to npub: {}", e)) |
| 308 | } | 290 | } |
| 309 | }; | 291 | }; |
| 310 | 292 | ||
| @@ -323,7 +305,7 @@ impl CorsTests { | |||
| 323 | "GRASP-01", | 305 | "GRASP-01", |
| 324 | "CORS headers on real repository endpoint", | 306 | "CORS headers on real repository endpoint", |
| 325 | ) | 307 | ) |
| 326 | .fail(&format!("Failed to GET info/refs: {}", e)) | 308 | .fail(format!("Failed to GET info/refs: {}", e)) |
| 327 | } | 309 | } |
| 328 | }; | 310 | }; |
| 329 | 311 | ||
| @@ -492,15 +474,6 @@ mod tests { | |||
| 492 | results.print_report(); | 474 | results.print_report(); |
| 493 | 475 | ||
| 494 | // Assert all tests passed | 476 | // Assert all tests passed |
| 495 | assert!( | 477 | assert!(results.all_passed(), "Some GRASP-01 CORS tests failed"); |
| 496 | results.all_passed(), | ||
| 497 | "Some GRASP-01 CORS tests failed" | ||
| 498 | ); | ||
| 499 | } | 478 | } |
| 500 | 479 | } | |
| 501 | #[test] | ||
| 502 | fn test_module_exists() { | ||
| 503 | // Simple compilation test | ||
| 504 | assert!(true); | ||
| 505 | } | ||
| 506 | } \ No newline at end of file | ||
diff --git a/grasp-audit/src/specs/grasp01/event_acceptance_policy.rs b/grasp-audit/src/specs/grasp01/event_acceptance_policy.rs index 3a8f18d..1fc7f73 100644 --- a/grasp-audit/src/specs/grasp01/event_acceptance_policy.rs +++ b/grasp-audit/src/specs/grasp01/event_acceptance_policy.rs | |||
| @@ -247,7 +247,9 @@ impl EventAcceptancePolicyTests { | |||
| 247 | /// | 247 | /// |
| 248 | /// Spec: Line 5 of ../grasp/01.md | 248 | /// Spec: Line 5 of ../grasp/01.md |
| 249 | /// Requirement: MUST reject announcements not listing service (unless GRASP-05) | 249 | /// Requirement: MUST reject announcements not listing service (unless GRASP-05) |
| 250 | pub async fn test_reject_repo_announcement_missing_clone_tag(client: &AuditClient) -> TestResult { | 250 | pub async fn test_reject_repo_announcement_missing_clone_tag( |
| 251 | client: &AuditClient, | ||
| 252 | ) -> TestResult { | ||
| 251 | TestResult::new( | 253 | TestResult::new( |
| 252 | "reject_repo_announcement_missing_clone_tag", | 254 | "reject_repo_announcement_missing_clone_tag", |
| 253 | "GRASP-01:nostr-relay:5", | 255 | "GRASP-01:nostr-relay:5", |
| @@ -321,7 +323,9 @@ impl EventAcceptancePolicyTests { | |||
| 321 | /// | 323 | /// |
| 322 | /// Spec: Line 5 of ../grasp/01.md | 324 | /// Spec: Line 5 of ../grasp/01.md |
| 323 | /// Requirement: MUST reject announcements not listing service in relays | 325 | /// Requirement: MUST reject announcements not listing service in relays |
| 324 | pub async fn test_reject_repo_announcement_missing_relays_tag(client: &AuditClient) -> TestResult { | 326 | pub async fn test_reject_repo_announcement_missing_relays_tag( |
| 327 | client: &AuditClient, | ||
| 328 | ) -> TestResult { | ||
| 325 | TestResult::new( | 329 | TestResult::new( |
| 326 | "reject_repo_announcement_missing_relays_tag", | 330 | "reject_repo_announcement_missing_relays_tag", |
| 327 | "GRASP-01:nostr-relay:5", | 331 | "GRASP-01:nostr-relay:5", |
| @@ -546,8 +550,7 @@ impl EventAcceptancePolicyTests { | |||
| 546 | let issue = Self::create_issue_for_repo(client, &repo, "Test Issue 1")?; | 550 | let issue = Self::create_issue_for_repo(client, &repo, "Test Issue 1")?; |
| 547 | 551 | ||
| 548 | // 3. Send issue and verify it's accepted | 552 | // 3. Send issue and verify it's accepted |
| 549 | send_and_verify_accepted(client, issue, "issue referencing repo via 'a' tag") | 553 | send_and_verify_accepted(client, issue, "issue referencing repo via 'a' tag").await?; |
| 550 | .await?; | ||
| 551 | 554 | ||
| 552 | Ok(()) | 555 | Ok(()) |
| 553 | }) | 556 | }) |
| @@ -693,8 +696,7 @@ impl EventAcceptancePolicyTests { | |||
| 693 | .map_err(|e| format!("Failed to build issue B: {}", e))?; | 696 | .map_err(|e| format!("Failed to build issue B: {}", e))?; |
| 694 | 697 | ||
| 695 | // Send Issue B and verify it's ACCEPTED (via transitive quote to Issue A) | 698 | // Send Issue B and verify it's ACCEPTED (via transitive quote to Issue A) |
| 696 | send_and_verify_accepted(client, issue_b, "issue B quoting accepted issue A") | 699 | send_and_verify_accepted(client, issue_b, "issue B quoting accepted issue A").await?; |
| 697 | .await?; | ||
| 698 | 700 | ||
| 699 | Ok(()) | 701 | Ok(()) |
| 700 | }) | 702 | }) |
| @@ -772,8 +774,7 @@ impl EventAcceptancePolicyTests { | |||
| 772 | .build(client.keys()) | 774 | .build(client.keys()) |
| 773 | .map_err(|e| format!("Failed to build kind1 A: {}", e))?; | 775 | .map_err(|e| format!("Failed to build kind1 A: {}", e))?; |
| 774 | 776 | ||
| 775 | send_and_verify_accepted(client, kind1_a.clone(), "kind 1 A quoting repo") | 777 | send_and_verify_accepted(client, kind1_a.clone(), "kind 1 A quoting repo").await?; |
| 776 | .await?; | ||
| 777 | 778 | ||
| 778 | // Create Kind 1 B that replies to Kind 1 A via 'e' tag | 779 | // Create Kind 1 B that replies to Kind 1 A via 'e' tag |
| 779 | let kind1_b = client | 780 | let kind1_b = client |
| @@ -783,12 +784,8 @@ impl EventAcceptancePolicyTests { | |||
| 783 | .map_err(|e| format!("Failed to build kind1 B: {}", e))?; | 784 | .map_err(|e| format!("Failed to build kind1 B: {}", e))?; |
| 784 | 785 | ||
| 785 | // Send Kind 1 B and verify it's accepted (via 'e' tag to accepted kind 1 A) | 786 | // Send Kind 1 B and verify it's accepted (via 'e' tag to accepted kind 1 A) |
| 786 | send_and_verify_accepted( | 787 | send_and_verify_accepted(client, kind1_b, "kind 1 B replying to accepted kind 1 A") |
| 787 | client, | 788 | .await?; |
| 788 | kind1_b, | ||
| 789 | "kind 1 B replying to accepted kind 1 A", | ||
| 790 | ) | ||
| 791 | .await?; | ||
| 792 | 789 | ||
| 793 | Ok(()) | 790 | Ok(()) |
| 794 | }) | 791 | }) |
| @@ -828,17 +825,19 @@ impl EventAcceptancePolicyTests { | |||
| 828 | .kind(Kind::GitRepoAnnouncement) | 825 | .kind(Kind::GitRepoAnnouncement) |
| 829 | .author(repo.pubkey) | 826 | .author(repo.pubkey) |
| 830 | .identifier(repo_id); | 827 | .identifier(repo_id); |
| 831 | 828 | ||
| 832 | // Poll until repo is available (with timeout) | 829 | // Poll until repo is available (with timeout) |
| 833 | for _ in 0..10 { | 830 | for _ in 0..10 { |
| 834 | let events = client.query(verify_filter.clone()).await | 831 | let events = client |
| 832 | .query(verify_filter.clone()) | ||
| 833 | .await | ||
| 835 | .map_err(|e| format!("Failed to verify repo: {}", e))?; | 834 | .map_err(|e| format!("Failed to verify repo: {}", e))?; |
| 836 | if !events.is_empty() { | 835 | if !events.is_empty() { |
| 837 | break; | 836 | break; |
| 838 | } | 837 | } |
| 839 | tokio::time::sleep(Duration::from_millis(50)).await; | 838 | tokio::time::sleep(Duration::from_millis(50)).await; |
| 840 | } | 839 | } |
| 841 | 840 | ||
| 842 | // Extra delay to ensure relay's internal database is fully synchronized | 841 | // Extra delay to ensure relay's internal database is fully synchronized |
| 843 | tokio::time::sleep(Duration::from_millis(200)).await; | 842 | tokio::time::sleep(Duration::from_millis(200)).await; |
| 844 | 843 | ||
| @@ -907,12 +906,7 @@ impl EventAcceptancePolicyTests { | |||
| 907 | let issue = ctx | 906 | let issue = ctx |
| 908 | .get_fixture(FixtureKind::RepoWithIssue) | 907 | .get_fixture(FixtureKind::RepoWithIssue) |
| 909 | .await | 908 | .await |
| 910 | .map_err(|e| { | 909 | .map_err(|e| format!("Test setup failed: could not get issue fixture: {}", e))?; |
| 911 | format!( | ||
| 912 | "Test setup failed: could not get issue fixture: {}", | ||
| 913 | e | ||
| 914 | ) | ||
| 915 | })?; | ||
| 916 | 910 | ||
| 917 | // Create Comment A locally but DON'T send it yet | 911 | // Create Comment A locally but DON'T send it yet |
| 918 | let comment_a = Self::create_comment_for_event(client, &issue, "Comment A")?; | 912 | let comment_a = Self::create_comment_for_event(client, &issue, "Comment A")?; |
| @@ -997,16 +991,11 @@ impl EventAcceptancePolicyTests { | |||
| 997 | .build(client.keys()) | 991 | .build(client.keys()) |
| 998 | .map_err(|e| format!("Failed to build kind1 B: {}", e))?; | 992 | .map_err(|e| format!("Failed to build kind1 B: {}", e))?; |
| 999 | 993 | ||
| 1000 | send_and_verify_accepted(client, kind1_b, "kind1 B mentioning unsent kind1 A") | 994 | send_and_verify_accepted(client, kind1_b, "kind1 B mentioning unsent kind1 A").await?; |
| 1001 | .await?; | ||
| 1002 | 995 | ||
| 1003 | // NOW send Kind 1 A - should be accepted because accepted Kind 1 B mentions it | 996 | // NOW send Kind 1 A - should be accepted because accepted Kind 1 B mentions it |
| 1004 | send_and_verify_accepted( | 997 | send_and_verify_accepted(client, kind1_a, "kind1 A referenced by accepted kind1 B") |
| 1005 | client, | 998 | .await?; |
| 1006 | kind1_a, | ||
| 1007 | "kind1 A referenced by accepted kind1 B", | ||
| 1008 | ) | ||
| 1009 | .await?; | ||
| 1010 | 999 | ||
| 1011 | Ok(()) | 1000 | Ok(()) |
| 1012 | }) | 1001 | }) |
| @@ -1033,12 +1022,8 @@ impl EventAcceptancePolicyTests { | |||
| 1033 | Self::create_issue_for_repo(client, &unaccepted_repo, "Orphan Issue")?; | 1022 | Self::create_issue_for_repo(client, &unaccepted_repo, "Orphan Issue")?; |
| 1034 | 1023 | ||
| 1035 | // 3. Send issue and verify it's REJECTED | 1024 | // 3. Send issue and verify it's REJECTED |
| 1036 | send_and_verify_rejected( | 1025 | send_and_verify_rejected(client, orphan_issue, "issue referencing unaccepted repo") |
| 1037 | client, | 1026 | .await?; |
| 1038 | orphan_issue, | ||
| 1039 | "issue referencing unaccepted repo", | ||
| 1040 | ) | ||
| 1041 | .await?; | ||
| 1042 | 1027 | ||
| 1043 | Ok(()) | 1028 | Ok(()) |
| 1044 | }) | 1029 | }) |
| @@ -1060,8 +1045,7 @@ impl EventAcceptancePolicyTests { | |||
| 1060 | .map_err(|e| format!("Failed to build note: {}", e))?; | 1045 | .map_err(|e| format!("Failed to build note: {}", e))?; |
| 1061 | 1046 | ||
| 1062 | // 2. Send note and verify it's REJECTED | 1047 | // 2. Send note and verify it's REJECTED |
| 1063 | send_and_verify_rejected(client, orphan_note, "kind 1 with no repo references") | 1048 | send_and_verify_rejected(client, orphan_note, "kind 1 with no repo references").await?; |
| 1064 | .await?; | ||
| 1065 | 1049 | ||
| 1066 | Ok(()) | 1050 | Ok(()) |
| 1067 | }) | 1051 | }) |
diff --git a/grasp-audit/src/specs/grasp01/git_clone.rs b/grasp-audit/src/specs/grasp01/git_clone.rs index 9ee6ed7..95338e4 100644 --- a/grasp-audit/src/specs/grasp01/git_clone.rs +++ b/grasp-audit/src/specs/grasp01/git_clone.rs | |||
| @@ -25,10 +25,7 @@ pub struct GitCloneTests; | |||
| 25 | 25 | ||
| 26 | impl GitCloneTests { | 26 | impl GitCloneTests { |
| 27 | /// Run all Git clone tests | 27 | /// Run all Git clone tests |
| 28 | pub async fn run_all( | 28 | pub async fn run_all(client: &AuditClient, relay_domain: &str) -> crate::AuditResult { |
| 29 | client: &AuditClient, | ||
| 30 | relay_domain: &str, | ||
| 31 | ) -> crate::AuditResult { | ||
| 32 | let mut results = crate::AuditResult::new("GRASP-01 Git Clone Tests"); | 29 | let mut results = crate::AuditResult::new("GRASP-01 Git Clone Tests"); |
| 33 | 30 | ||
| 34 | results.add(Self::test_basic_git_clone(client, relay_domain).await); | 31 | results.add(Self::test_basic_git_clone(client, relay_domain).await); |
| @@ -45,10 +42,7 @@ impl GitCloneTests { | |||
| 45 | /// 2. Waits for repository creation | 42 | /// 2. Waits for repository creation |
| 46 | /// 3. Attempts to clone the repository using git clone | 43 | /// 3. Attempts to clone the repository using git clone |
| 47 | /// 4. Verifies the clone succeeded | 44 | /// 4. Verifies the clone succeeded |
| 48 | pub async fn test_basic_git_clone( | 45 | pub async fn test_basic_git_clone(client: &AuditClient, relay_domain: &str) -> TestResult { |
| 49 | client: &AuditClient, | ||
| 50 | relay_domain: &str, | ||
| 51 | ) -> TestResult { | ||
| 52 | let test_name = "test_basic_git_clone"; | 46 | let test_name = "test_basic_git_clone"; |
| 53 | let ctx = TestContext::new(client); | 47 | let ctx = TestContext::new(client); |
| 54 | 48 | ||
| @@ -61,7 +55,7 @@ impl GitCloneTests { | |||
| 61 | "GRASP-01", | 55 | "GRASP-01", |
| 62 | "Repository must be cloneable via Git HTTP backend", | 56 | "Repository must be cloneable via Git HTTP backend", |
| 63 | ) | 57 | ) |
| 64 | .fail(&format!("Failed to create repo fixture: {}", e)) | 58 | .fail(format!("Failed to create repo fixture: {}", e)) |
| 65 | } | 59 | } |
| 66 | }; | 60 | }; |
| 67 | 61 | ||
| @@ -94,7 +88,7 @@ impl GitCloneTests { | |||
| 94 | "GRASP-01", | 88 | "GRASP-01", |
| 95 | "Repository must be cloneable via Git HTTP backend", | 89 | "Repository must be cloneable via Git HTTP backend", |
| 96 | ) | 90 | ) |
| 97 | .fail(&format!("Failed to convert pubkey to npub: {}", e)) | 91 | .fail(format!("Failed to convert pubkey to npub: {}", e)) |
| 98 | } | 92 | } |
| 99 | }; | 93 | }; |
| 100 | 94 | ||
| @@ -102,7 +96,7 @@ impl GitCloneTests { | |||
| 102 | let temp_base = std::env::temp_dir(); | 96 | let temp_base = std::env::temp_dir(); |
| 103 | let clone_dir_name = format!("grasp-test-clone-{}", uuid::Uuid::new_v4()); | 97 | let clone_dir_name = format!("grasp-test-clone-{}", uuid::Uuid::new_v4()); |
| 104 | let clone_path = temp_base.join(&clone_dir_name); | 98 | let clone_path = temp_base.join(&clone_dir_name); |
| 105 | 99 | ||
| 106 | // Ensure clean state | 100 | // Ensure clean state |
| 107 | let _ = fs::remove_dir_all(&clone_path); | 101 | let _ = fs::remove_dir_all(&clone_path); |
| 108 | 102 | ||
| @@ -114,7 +108,7 @@ impl GitCloneTests { | |||
| 114 | .args(["clone", &clone_url, clone_path.to_str().unwrap()]) | 108 | .args(["clone", &clone_url, clone_path.to_str().unwrap()]) |
| 115 | .env("GIT_TERMINAL_PROMPT", "0") // Disable password prompts | 109 | .env("GIT_TERMINAL_PROMPT", "0") // Disable password prompts |
| 116 | .output(); | 110 | .output(); |
| 117 | 111 | ||
| 118 | // Clean up on success or failure | 112 | // Clean up on success or failure |
| 119 | let cleanup = || { | 113 | let cleanup = || { |
| 120 | let _ = fs::remove_dir_all(&clone_path); | 114 | let _ = fs::remove_dir_all(&clone_path); |
| @@ -129,7 +123,7 @@ impl GitCloneTests { | |||
| 129 | "GRASP-01", | 123 | "GRASP-01", |
| 130 | "Repository must be cloneable via Git HTTP backend", | 124 | "Repository must be cloneable via Git HTTP backend", |
| 131 | ) | 125 | ) |
| 132 | .fail(&format!("Failed to execute git clone: {}", e)) | 126 | .fail(format!("Failed to execute git clone: {}", e)); |
| 133 | } | 127 | } |
| 134 | }; | 128 | }; |
| 135 | 129 | ||
| @@ -141,7 +135,7 @@ impl GitCloneTests { | |||
| 141 | "GRASP-01", | 135 | "GRASP-01", |
| 142 | "Repository must be cloneable via Git HTTP backend", | 136 | "Repository must be cloneable via Git HTTP backend", |
| 143 | ) | 137 | ) |
| 144 | .fail(&format!("Git clone failed: {}", stderr)); | 138 | .fail(format!("Git clone failed: {}", stderr)); |
| 145 | } | 139 | } |
| 146 | 140 | ||
| 147 | // Verify clone succeeded by checking for .git directory | 141 | // Verify clone succeeded by checking for .git directory |
| @@ -169,10 +163,7 @@ impl GitCloneTests { | |||
| 169 | /// This test verifies: | 163 | /// This test verifies: |
| 170 | /// 1. URLs follow the pattern http://domain/npub/identifier.git | 164 | /// 1. URLs follow the pattern http://domain/npub/identifier.git |
| 171 | /// 2. Invalid URLs are rejected properly | 165 | /// 2. Invalid URLs are rejected properly |
| 172 | pub async fn test_clone_url_format( | 166 | pub async fn test_clone_url_format(client: &AuditClient, relay_domain: &str) -> TestResult { |
| 173 | client: &AuditClient, | ||
| 174 | relay_domain: &str, | ||
| 175 | ) -> TestResult { | ||
| 176 | let test_name = "test_clone_url_format"; | 167 | let test_name = "test_clone_url_format"; |
| 177 | let ctx = TestContext::new(client); | 168 | let ctx = TestContext::new(client); |
| 178 | 169 | ||
| @@ -185,7 +176,7 @@ impl GitCloneTests { | |||
| 185 | "GRASP-01", | 176 | "GRASP-01", |
| 186 | "Clone URL must follow correct format", | 177 | "Clone URL must follow correct format", |
| 187 | ) | 178 | ) |
| 188 | .fail(&format!("Failed to create repo fixture: {}", e)) | 179 | .fail(format!("Failed to create repo fixture: {}", e)) |
| 189 | } | 180 | } |
| 190 | }; | 181 | }; |
| 191 | 182 | ||
| @@ -205,7 +196,7 @@ impl GitCloneTests { | |||
| 205 | 196 | ||
| 206 | // Test valid URL format | 197 | // Test valid URL format |
| 207 | let valid_url = format!("http://{}/{}/{}.git", relay_domain, npub, repo_id); | 198 | let valid_url = format!("http://{}/{}/{}.git", relay_domain, npub, repo_id); |
| 208 | 199 | ||
| 209 | // Verify URL contains expected components | 200 | // Verify URL contains expected components |
| 210 | if !valid_url.contains(&npub) { | 201 | if !valid_url.contains(&npub) { |
| 211 | return TestResult::new( | 202 | return TestResult::new( |
| @@ -229,10 +220,10 @@ impl GitCloneTests { | |||
| 229 | let temp_base = std::env::temp_dir(); | 220 | let temp_base = std::env::temp_dir(); |
| 230 | let clone_dir_name = format!("grasp-test-invalid-{}", uuid::Uuid::new_v4()); | 221 | let clone_dir_name = format!("grasp-test-invalid-{}", uuid::Uuid::new_v4()); |
| 231 | let clone_path = temp_base.join(&clone_dir_name); | 222 | let clone_path = temp_base.join(&clone_dir_name); |
| 232 | 223 | ||
| 233 | // Ensure clean state | 224 | // Ensure clean state |
| 234 | let _ = fs::remove_dir_all(&clone_path); | 225 | let _ = fs::remove_dir_all(&clone_path); |
| 235 | 226 | ||
| 236 | let invalid_url = format!("http://{}/invalid/path", relay_domain); | 227 | let invalid_url = format!("http://{}/invalid/path", relay_domain); |
| 237 | 228 | ||
| 238 | let output = Command::new("git") | 229 | let output = Command::new("git") |
| @@ -287,7 +278,7 @@ impl GitCloneTests { | |||
| 287 | "GRASP-01", | 278 | "GRASP-01", |
| 288 | "MUST include allow-reachable-sha1-in-want and allow-tip-sha1-in-want in advertisement", | 279 | "MUST include allow-reachable-sha1-in-want and allow-tip-sha1-in-want in advertisement", |
| 289 | ) | 280 | ) |
| 290 | .fail(&format!("Failed to create repo fixture: {}", e)) | 281 | .fail(format!("Failed to create repo fixture: {}", e)) |
| 291 | } | 282 | } |
| 292 | }; | 283 | }; |
| 293 | 284 | ||
| @@ -320,7 +311,7 @@ impl GitCloneTests { | |||
| 320 | "GRASP-01", | 311 | "GRASP-01", |
| 321 | "MUST include allow-reachable-sha1-in-want and allow-tip-sha1-in-want in advertisement", | 312 | "MUST include allow-reachable-sha1-in-want and allow-tip-sha1-in-want in advertisement", |
| 322 | ) | 313 | ) |
| 323 | .fail(&format!("Failed to convert pubkey to npub: {}", e)) | 314 | .fail(format!("Failed to convert pubkey to npub: {}", e)) |
| 324 | } | 315 | } |
| 325 | }; | 316 | }; |
| 326 | 317 | ||
| @@ -340,7 +331,7 @@ impl GitCloneTests { | |||
| 340 | "GRASP-01", | 331 | "GRASP-01", |
| 341 | "MUST include allow-reachable-sha1-in-want and allow-tip-sha1-in-want in advertisement", | 332 | "MUST include allow-reachable-sha1-in-want and allow-tip-sha1-in-want in advertisement", |
| 342 | ) | 333 | ) |
| 343 | .fail(&format!("HTTP request failed: {}", e)) | 334 | .fail(format!("HTTP request failed: {}", e)) |
| 344 | } | 335 | } |
| 345 | }; | 336 | }; |
| 346 | 337 | ||
| @@ -350,7 +341,7 @@ impl GitCloneTests { | |||
| 350 | "GRASP-01", | 341 | "GRASP-01", |
| 351 | "MUST include allow-reachable-sha1-in-want and allow-tip-sha1-in-want in advertisement", | 342 | "MUST include allow-reachable-sha1-in-want and allow-tip-sha1-in-want in advertisement", |
| 352 | ) | 343 | ) |
| 353 | .fail(&format!( | 344 | .fail(format!( |
| 354 | "info/refs request failed with status: {}", | 345 | "info/refs request failed with status: {}", |
| 355 | response.status() | 346 | response.status() |
| 356 | )); | 347 | )); |
| @@ -365,7 +356,7 @@ impl GitCloneTests { | |||
| 365 | "GRASP-01", | 356 | "GRASP-01", |
| 366 | "MUST include allow-reachable-sha1-in-want and allow-tip-sha1-in-want in advertisement", | 357 | "MUST include allow-reachable-sha1-in-want and allow-tip-sha1-in-want in advertisement", |
| 367 | ) | 358 | ) |
| 368 | .fail(&format!("Failed to read response body: {}", e)) | 359 | .fail(format!("Failed to read response body: {}", e)) |
| 369 | } | 360 | } |
| 370 | }; | 361 | }; |
| 371 | 362 | ||
| @@ -399,12 +390,3 @@ impl GitCloneTests { | |||
| 399 | .pass() | 390 | .pass() |
| 400 | } | 391 | } |
| 401 | } | 392 | } |
| 402 | |||
| 403 | #[cfg(test)] | ||
| 404 | mod tests { | ||
| 405 | #[test] | ||
| 406 | fn test_module_exists() { | ||
| 407 | // Simple compilation test | ||
| 408 | assert!(true); | ||
| 409 | } | ||
| 410 | } \ No newline at end of file | ||
diff --git a/grasp-audit/src/specs/grasp01/mod.rs b/grasp-audit/src/specs/grasp01/mod.rs index 6f58b96..e16a351 100644 --- a/grasp-audit/src/specs/grasp01/mod.rs +++ b/grasp-audit/src/specs/grasp01/mod.rs | |||
| @@ -26,4 +26,4 @@ pub use git_clone::GitCloneTests; | |||
| 26 | pub use nip01_smoke::Nip01SmokeTests; | 26 | pub use nip01_smoke::Nip01SmokeTests; |
| 27 | pub use nip11_document::Nip11DocumentTests; | 27 | pub use nip11_document::Nip11DocumentTests; |
| 28 | pub use push_authorization::PushAuthorizationTests; | 28 | pub use push_authorization::PushAuthorizationTests; |
| 29 | pub use repository_creation::{RepositoryCreationTests}; | 29 | pub use repository_creation::RepositoryCreationTests; |
diff --git a/grasp-audit/src/specs/grasp01/nip01_smoke.rs b/grasp-audit/src/specs/grasp01/nip01_smoke.rs index b16d61a..5161da8 100644 --- a/grasp-audit/src/specs/grasp01/nip01_smoke.rs +++ b/grasp-audit/src/specs/grasp01/nip01_smoke.rs | |||
| @@ -163,27 +163,23 @@ impl Nip01SmokeTests { | |||
| 163 | /// Spec: NIP-01 CLOSE message | 163 | /// Spec: NIP-01 CLOSE message |
| 164 | /// Requirement: Relay MUST support CLOSE to end subscriptions | 164 | /// Requirement: Relay MUST support CLOSE to end subscriptions |
| 165 | pub async fn test_close_subscription(client: &AuditClient) -> TestResult { | 165 | pub async fn test_close_subscription(client: &AuditClient) -> TestResult { |
| 166 | TestResult::new( | 166 | TestResult::new("close_subscription", "NIP-01", "Can close subscriptions") |
| 167 | "close_subscription", | 167 | .run(|| async { |
| 168 | "NIP-01", | 168 | // For now, we just verify we can query events |
| 169 | "Can close subscriptions", | 169 | // Full subscription management with CLOSE would require |
| 170 | ) | 170 | // lower-level WebSocket access |
| 171 | .run(|| async { | ||
| 172 | // For now, we just verify we can query events | ||
| 173 | // Full subscription management with CLOSE would require | ||
| 174 | // lower-level WebSocket access | ||
| 175 | 171 | ||
| 176 | let filter = Filter::new().kind(Kind::TextNote).limit(1); | 172 | let filter = Filter::new().kind(Kind::TextNote).limit(1); |
| 177 | 173 | ||
| 178 | let _events = client | 174 | let _events = client |
| 179 | .subscribe(vec![filter], Some(std::time::Duration::from_secs(2))) | 175 | .subscribe(vec![filter], Some(std::time::Duration::from_secs(2))) |
| 180 | .await | 176 | .await |
| 181 | .map_err(|e| format!("Failed to subscribe: {}", e))?; | 177 | .map_err(|e| format!("Failed to subscribe: {}", e))?; |
| 182 | 178 | ||
| 183 | // If we got here, subscription worked | 179 | // If we got here, subscription worked |
| 184 | Ok(()) | 180 | Ok(()) |
| 185 | }) | 181 | }) |
| 186 | .await | 182 | .await |
| 187 | } | 183 | } |
| 188 | 184 | ||
| 189 | /// Test 5: Rejects events with invalid signatures | 185 | /// Test 5: Rejects events with invalid signatures |
diff --git a/grasp-audit/src/specs/grasp01/nip11_document.rs b/grasp-audit/src/specs/grasp01/nip11_document.rs index bb864f2..51b147d 100644 --- a/grasp-audit/src/specs/grasp01/nip11_document.rs +++ b/grasp-audit/src/specs/grasp01/nip11_document.rs | |||
| @@ -42,7 +42,9 @@ impl Nip11DocumentTests { | |||
| 42 | ) | 42 | ) |
| 43 | .run(|| async { | 43 | .run(|| async { |
| 44 | // 1. Extract HTTP(S) URL from client's WebSocket URL | 44 | // 1. Extract HTTP(S) URL from client's WebSocket URL |
| 45 | let ws_url = client.relay_url().await | 45 | let ws_url = client |
| 46 | .relay_url() | ||
| 47 | .await | ||
| 46 | .map_err(|e| format!("Failed to get relay URL: {}", e))?; | 48 | .map_err(|e| format!("Failed to get relay URL: {}", e))?; |
| 47 | let http_url = AuditClient::ws_to_http_url(&ws_url) | 49 | let http_url = AuditClient::ws_to_http_url(&ws_url) |
| 48 | .map_err(|e| format!("Failed to convert WebSocket URL to HTTP: {}", e))?; | 50 | .map_err(|e| format!("Failed to convert WebSocket URL to HTTP: {}", e))?; |
| @@ -66,16 +68,18 @@ impl Nip11DocumentTests { | |||
| 66 | } | 68 | } |
| 67 | 69 | ||
| 68 | // 4. Verify response is valid JSON | 70 | // 4. Verify response is valid JSON |
| 69 | let json_text = response.text().await | 71 | let json_text = response |
| 72 | .text() | ||
| 73 | .await | ||
| 70 | .map_err(|e| format!("Failed to read response body: {}", e))?; | 74 | .map_err(|e| format!("Failed to read response body: {}", e))?; |
| 71 | 75 | ||
| 72 | let doc: serde_json::Value = serde_json::from_str(&json_text) | 76 | let doc: serde_json::Value = serde_json::from_str(&json_text) |
| 73 | .map_err(|e| format!("Response is not valid JSON: {}", e))?; | 77 | .map_err(|e| format!("Response is not valid JSON: {}", e))?; |
| 74 | 78 | ||
| 75 | // 5. Verify has required NIP-11 fields | 79 | // 5. Verify has required NIP-11 fields |
| 76 | let required_fields = ["name", "description", "software", "version"]; | 80 | let required_fields = ["name", "description", "software", "version"]; |
| 77 | for field in &required_fields { | 81 | for field in &required_fields { |
| 78 | if !doc.get(field).is_some() { | 82 | if doc.get(field).is_none() { |
| 79 | return Err(format!("Missing required NIP-11 field: {}", field)); | 83 | return Err(format!("Missing required NIP-11 field: {}", field)); |
| 80 | } | 84 | } |
| 81 | } | 85 | } |
| @@ -97,7 +101,9 @@ impl Nip11DocumentTests { | |||
| 97 | ) | 101 | ) |
| 98 | .run(|| async { | 102 | .run(|| async { |
| 99 | // 1. Fetch NIP-11 document | 103 | // 1. Fetch NIP-11 document |
| 100 | let ws_url = client.relay_url().await | 104 | let ws_url = client |
| 105 | .relay_url() | ||
| 106 | .await | ||
| 101 | .map_err(|e| format!("Failed to get relay URL: {}", e))?; | 107 | .map_err(|e| format!("Failed to get relay URL: {}", e))?; |
| 102 | let http_url = AuditClient::ws_to_http_url(&ws_url) | 108 | let http_url = AuditClient::ws_to_http_url(&ws_url) |
| 103 | .map_err(|e| format!("Failed to convert WebSocket URL to HTTP: {}", e))?; | 109 | .map_err(|e| format!("Failed to convert WebSocket URL to HTTP: {}", e))?; |
| @@ -110,18 +116,22 @@ impl Nip11DocumentTests { | |||
| 110 | .await | 116 | .await |
| 111 | .map_err(|e| format!("Failed to fetch NIP-11 document: {}", e))?; | 117 | .map_err(|e| format!("Failed to fetch NIP-11 document: {}", e))?; |
| 112 | 118 | ||
| 113 | let json_text = response.text().await | 119 | let json_text = response |
| 120 | .text() | ||
| 121 | .await | ||
| 114 | .map_err(|e| format!("Failed to read response body: {}", e))?; | 122 | .map_err(|e| format!("Failed to read response body: {}", e))?; |
| 115 | 123 | ||
| 116 | let doc: serde_json::Value = serde_json::from_str(&json_text) | 124 | let doc: serde_json::Value = serde_json::from_str(&json_text) |
| 117 | .map_err(|e| format!("Response is not valid JSON: {}", e))?; | 125 | .map_err(|e| format!("Response is not valid JSON: {}", e))?; |
| 118 | 126 | ||
| 119 | // 2. Verify `supported_grasps` field exists | 127 | // 2. Verify `supported_grasps` field exists |
| 120 | let supported_grasps = doc.get("supported_grasps") | 128 | let supported_grasps = doc |
| 129 | .get("supported_grasps") | ||
| 121 | .ok_or_else(|| "Missing required field: supported_grasps".to_string())?; | 130 | .ok_or_else(|| "Missing required field: supported_grasps".to_string())?; |
| 122 | 131 | ||
| 123 | // 3. Verify it's a JSON array | 132 | // 3. Verify it's a JSON array |
| 124 | let grasps_array = supported_grasps.as_array() | 133 | let grasps_array = supported_grasps |
| 134 | .as_array() | ||
| 125 | .ok_or_else(|| "supported_grasps must be an array".to_string())?; | 135 | .ok_or_else(|| "supported_grasps must be an array".to_string())?; |
| 126 | 136 | ||
| 127 | // 4. Verify array includes "GRASP-01" | 137 | // 4. Verify array includes "GRASP-01" |
| @@ -140,7 +150,7 @@ impl Nip11DocumentTests { | |||
| 140 | // 5. Verify format: each entry should match pattern "GRASP-\d{2}" | 150 | // 5. Verify format: each entry should match pattern "GRASP-\d{2}" |
| 141 | let grasp_pattern = regex::Regex::new(r"^GRASP-\d{2}$") | 151 | let grasp_pattern = regex::Regex::new(r"^GRASP-\d{2}$") |
| 142 | .map_err(|e| format!("Failed to compile regex: {}", e))?; | 152 | .map_err(|e| format!("Failed to compile regex: {}", e))?; |
| 143 | 153 | ||
| 144 | for grasp in &grasp_strings { | 154 | for grasp in &grasp_strings { |
| 145 | if !grasp_pattern.is_match(grasp) { | 155 | if !grasp_pattern.is_match(grasp) { |
| 146 | return Err(format!( | 156 | return Err(format!( |
| @@ -167,7 +177,9 @@ impl Nip11DocumentTests { | |||
| 167 | ) | 177 | ) |
| 168 | .run(|| async { | 178 | .run(|| async { |
| 169 | // 1. Fetch NIP-11 document | 179 | // 1. Fetch NIP-11 document |
| 170 | let ws_url = client.relay_url().await | 180 | let ws_url = client |
| 181 | .relay_url() | ||
| 182 | .await | ||
| 171 | .map_err(|e| format!("Failed to get relay URL: {}", e))?; | 183 | .map_err(|e| format!("Failed to get relay URL: {}", e))?; |
| 172 | let http_url = AuditClient::ws_to_http_url(&ws_url) | 184 | let http_url = AuditClient::ws_to_http_url(&ws_url) |
| 173 | .map_err(|e| format!("Failed to convert WebSocket URL to HTTP: {}", e))?; | 185 | .map_err(|e| format!("Failed to convert WebSocket URL to HTTP: {}", e))?; |
| @@ -180,18 +192,22 @@ impl Nip11DocumentTests { | |||
| 180 | .await | 192 | .await |
| 181 | .map_err(|e| format!("Failed to fetch NIP-11 document: {}", e))?; | 193 | .map_err(|e| format!("Failed to fetch NIP-11 document: {}", e))?; |
| 182 | 194 | ||
| 183 | let json_text = response.text().await | 195 | let json_text = response |
| 196 | .text() | ||
| 197 | .await | ||
| 184 | .map_err(|e| format!("Failed to read response body: {}", e))?; | 198 | .map_err(|e| format!("Failed to read response body: {}", e))?; |
| 185 | 199 | ||
| 186 | let doc: serde_json::Value = serde_json::from_str(&json_text) | 200 | let doc: serde_json::Value = serde_json::from_str(&json_text) |
| 187 | .map_err(|e| format!("Response is not valid JSON: {}", e))?; | 201 | .map_err(|e| format!("Response is not valid JSON: {}", e))?; |
| 188 | 202 | ||
| 189 | // 2. Verify `repo_acceptance_criteria` field exists | 203 | // 2. Verify `repo_acceptance_criteria` field exists |
| 190 | let criteria = doc.get("repo_acceptance_criteria") | 204 | let criteria = doc |
| 205 | .get("repo_acceptance_criteria") | ||
| 191 | .ok_or_else(|| "Missing required field: repo_acceptance_criteria".to_string())?; | 206 | .ok_or_else(|| "Missing required field: repo_acceptance_criteria".to_string())?; |
| 192 | 207 | ||
| 193 | // 3. Verify it's a string | 208 | // 3. Verify it's a string |
| 194 | let criteria_str = criteria.as_str() | 209 | let criteria_str = criteria |
| 210 | .as_str() | ||
| 195 | .ok_or_else(|| "repo_acceptance_criteria must be a string".to_string())?; | 211 | .ok_or_else(|| "repo_acceptance_criteria must be a string".to_string())?; |
| 196 | 212 | ||
| 197 | // 4. Verify non-empty | 213 | // 4. Verify non-empty |
| @@ -216,7 +232,9 @@ impl Nip11DocumentTests { | |||
| 216 | ) | 232 | ) |
| 217 | .run(|| async { | 233 | .run(|| async { |
| 218 | // 1. Fetch NIP-11 document | 234 | // 1. Fetch NIP-11 document |
| 219 | let ws_url = client.relay_url().await | 235 | let ws_url = client |
| 236 | .relay_url() | ||
| 237 | .await | ||
| 220 | .map_err(|e| format!("Failed to get relay URL: {}", e))?; | 238 | .map_err(|e| format!("Failed to get relay URL: {}", e))?; |
| 221 | let http_url = AuditClient::ws_to_http_url(&ws_url) | 239 | let http_url = AuditClient::ws_to_http_url(&ws_url) |
| 222 | .map_err(|e| format!("Failed to convert WebSocket URL to HTTP: {}", e))?; | 240 | .map_err(|e| format!("Failed to convert WebSocket URL to HTTP: {}", e))?; |
| @@ -229,16 +247,19 @@ impl Nip11DocumentTests { | |||
| 229 | .await | 247 | .await |
| 230 | .map_err(|e| format!("Failed to fetch NIP-11 document: {}", e))?; | 248 | .map_err(|e| format!("Failed to fetch NIP-11 document: {}", e))?; |
| 231 | 249 | ||
| 232 | let json_text = response.text().await | 250 | let json_text = response |
| 251 | .text() | ||
| 252 | .await | ||
| 233 | .map_err(|e| format!("Failed to read response body: {}", e))?; | 253 | .map_err(|e| format!("Failed to read response body: {}", e))?; |
| 234 | 254 | ||
| 235 | let doc: serde_json::Value = serde_json::from_str(&json_text) | 255 | let doc: serde_json::Value = serde_json::from_str(&json_text) |
| 236 | .map_err(|e| format!("Response is not valid JSON: {}", e))?; | 256 | .map_err(|e| format!("Response is not valid JSON: {}", e))?; |
| 237 | 257 | ||
| 238 | // 2. Check if `curation` field exists | 258 | // 2. Check if `curation` field exists |
| 239 | if let Some(curation) = doc.get("curation") { | 259 | if let Some(curation) = doc.get("curation") { |
| 240 | // 3. If present: verify it's a non-empty string | 260 | // 3. If present: verify it's a non-empty string |
| 241 | let curation_str = curation.as_str() | 261 | let curation_str = curation |
| 262 | .as_str() | ||
| 242 | .ok_or_else(|| "curation field must be a string when present".to_string())?; | 263 | .ok_or_else(|| "curation field must be a string when present".to_string())?; |
| 243 | 264 | ||
| 244 | if curation_str.trim().is_empty() { | 265 | if curation_str.trim().is_empty() { |
| @@ -284,4 +305,4 @@ mod tests { | |||
| 284 | // Don't assert all passed yet - tests not implemented | 305 | // Don't assert all passed yet - tests not implemented |
| 285 | // assert!(results.all_passed(), "Some GRASP-01 NIP-11 document tests failed"); | 306 | // assert!(results.all_passed(), "Some GRASP-01 NIP-11 document tests failed"); |
| 286 | } | 307 | } |
| 287 | } \ No newline at end of file | 308 | } |
diff --git a/grasp-audit/src/specs/grasp01/push_authorization.rs b/grasp-audit/src/specs/grasp01/push_authorization.rs index d8652ae..24eae1d 100644 --- a/grasp-audit/src/specs/grasp01/push_authorization.rs +++ b/grasp-audit/src/specs/grasp01/push_authorization.rs | |||
| @@ -351,10 +351,7 @@ async fn setup_repo_with_wrong_commit_pushed( | |||
| 351 | /// IMPORTANT: We must publish the EXACT same event that was used during setup, | 351 | /// IMPORTANT: We must publish the EXACT same event that was used during setup, |
| 352 | /// otherwise the event ID won't match the refs/nostr/<event-id> ref that was pushed. | 352 | /// otherwise the event ID won't match the refs/nostr/<event-id> ref that was pushed. |
| 353 | #[allow(dead_code)] | 353 | #[allow(dead_code)] |
| 354 | async fn publish_pr_event_and_wait( | 354 | async fn publish_pr_event_and_wait(ctx: &TestContext<'_>, pr_event: &Event) -> Result<(), String> { |
| 355 | ctx: &TestContext<'_>, | ||
| 356 | pr_event: &Event, | ||
| 357 | ) -> Result<(), String> { | ||
| 358 | // Publish the exact same PR event that was created during setup | 355 | // Publish the exact same PR event that was created during setup |
| 359 | ctx.client() | 356 | ctx.client() |
| 360 | .send_event(pr_event.clone()) | 357 | .send_event(pr_event.clone()) |
| @@ -1892,11 +1889,6 @@ impl PushAuthorizationTests { | |||
| 1892 | mod tests { | 1889 | mod tests { |
| 1893 | use super::*; | 1890 | use super::*; |
| 1894 | 1891 | ||
| 1895 | #[test] | ||
| 1896 | fn test_module_exists() { | ||
| 1897 | assert!(true); | ||
| 1898 | } | ||
| 1899 | |||
| 1900 | /// Test to discover the PR test commit hash | 1892 | /// Test to discover the PR test commit hash |
| 1901 | /// | 1893 | /// |
| 1902 | /// This test creates a deterministic commit with PR-specific parameters | 1894 | /// This test creates a deterministic commit with PR-specific parameters |
diff --git a/grasp-audit/src/specs/grasp01/repository_creation.rs b/grasp-audit/src/specs/grasp01/repository_creation.rs index 63b3dee..0b3eed5 100644 --- a/grasp-audit/src/specs/grasp01/repository_creation.rs +++ b/grasp-audit/src/specs/grasp01/repository_creation.rs | |||
| @@ -23,10 +23,7 @@ pub struct RepositoryCreationTests; | |||
| 23 | 23 | ||
| 24 | impl RepositoryCreationTests { | 24 | impl RepositoryCreationTests { |
| 25 | /// Run all repository creation tests | 25 | /// Run all repository creation tests |
| 26 | pub async fn run_all( | 26 | pub async fn run_all(client: &AuditClient, relay_domain: &str) -> crate::AuditResult { |
| 27 | client: &AuditClient, | ||
| 28 | relay_domain: &str, | ||
| 29 | ) -> crate::AuditResult { | ||
| 30 | let mut results = crate::AuditResult::new("GRASP-01 Repository Creation Tests"); | 27 | let mut results = crate::AuditResult::new("GRASP-01 Repository Creation Tests"); |
| 31 | 28 | ||
| 32 | results.add(Self::test_bare_repo_created_on_announcement(client, relay_domain).await); | 29 | results.add(Self::test_bare_repo_created_on_announcement(client, relay_domain).await); |
| @@ -58,7 +55,7 @@ impl RepositoryCreationTests { | |||
| 58 | "GRASP-01", | 55 | "GRASP-01", |
| 59 | "Bare repository must be created and accessible via Smart HTTP when announcement is accepted", | 56 | "Bare repository must be created and accessible via Smart HTTP when announcement is accepted", |
| 60 | ) | 57 | ) |
| 61 | .fail(&format!("Failed to create repo fixture: {}", e)) | 58 | .fail(format!("Failed to create repo fixture: {}", e)) |
| 62 | } | 59 | } |
| 63 | }; | 60 | }; |
| 64 | 61 | ||
| @@ -91,7 +88,7 @@ impl RepositoryCreationTests { | |||
| 91 | "GRASP-01", | 88 | "GRASP-01", |
| 92 | "Bare repository must be created and accessible via Smart HTTP when announcement is accepted", | 89 | "Bare repository must be created and accessible via Smart HTTP when announcement is accepted", |
| 93 | ) | 90 | ) |
| 94 | .fail(&format!("Failed to convert pubkey to npub: {}", e)) | 91 | .fail(format!("Failed to convert pubkey to npub: {}", e)) |
| 95 | } | 92 | } |
| 96 | }; | 93 | }; |
| 97 | 94 | ||
| @@ -102,7 +99,7 @@ impl RepositoryCreationTests { | |||
| 102 | "GRASP-01", | 99 | "GRASP-01", |
| 103 | "Bare repository must be created and accessible via Smart HTTP when announcement is accepted", | 100 | "Bare repository must be created and accessible via Smart HTTP when announcement is accepted", |
| 104 | ) | 101 | ) |
| 105 | .fail(&format!("Repository not accessible via HTTP: {}", e)); | 102 | .fail(format!("Repository not accessible via HTTP: {}", e)); |
| 106 | } | 103 | } |
| 107 | 104 | ||
| 108 | TestResult::new( | 105 | TestResult::new( |
| @@ -159,12 +156,3 @@ async fn check_repo_accessible_via_http( | |||
| 159 | 156 | ||
| 160 | Ok(()) | 157 | Ok(()) |
| 161 | } | 158 | } |
| 162 | |||
| 163 | #[cfg(test)] | ||
| 164 | mod tests { | ||
| 165 | #[test] | ||
| 166 | fn test_module_exists() { | ||
| 167 | // Simple compilation test | ||
| 168 | assert!(true); | ||
| 169 | } | ||
| 170 | } | ||