upleb.uk

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

summaryrefslogtreecommitdiff
path: root/grasp-audit/src
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-11-28 01:44:58 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-11-28 01:44:58 +0000
commitf053827e0a157f348d9cf834f026a8de322abfe2 (patch)
tree4dcde0f1e92dfe26fde131ef0f3f35e677e56b5b /grasp-audit/src
parent0c1d60a2ad69e79e83d36ed8a001743fde3d6666 (diff)
grasp-audit run all tests in audit mode
Diffstat (limited to 'grasp-audit/src')
-rw-r--r--grasp-audit/src/bin/grasp-audit.rs105
-rw-r--r--grasp-audit/src/specs/grasp01/git_clone.rs14
-rw-r--r--grasp-audit/src/specs/grasp01/mod.rs14
-rw-r--r--grasp-audit/src/specs/grasp01/push_authorization.rs16
-rw-r--r--grasp-audit/src/specs/grasp01/repository_creation.rs14
-rw-r--r--grasp-audit/src/specs/mod.rs7
6 files changed, 163 insertions, 7 deletions
diff --git a/grasp-audit/src/bin/grasp-audit.rs b/grasp-audit/src/bin/grasp-audit.rs
index b56a8e3..b810bea 100644
--- a/grasp-audit/src/bin/grasp-audit.rs
+++ b/grasp-audit/src/bin/grasp-audit.rs
@@ -2,6 +2,7 @@
2 2
3use clap::{Parser, Subcommand}; 3use clap::{Parser, Subcommand};
4use grasp_audit::*; 4use grasp_audit::*;
5use std::path::PathBuf;
5 6
6#[derive(Parser)] 7#[derive(Parser)]
7#[command(name = "grasp-audit")] 8#[command(name = "grasp-audit")]
@@ -23,9 +24,13 @@ enum Commands {
23 #[arg(short, long, default_value = "ci")] 24 #[arg(short, long, default_value = "ci")]
24 mode: String, 25 mode: String,
25 26
26 /// Spec to test (nip01-smoke, all) 27 /// Spec to test (nip01-smoke, nip11, event-acceptance, cors, git-clone, push-auth, repo-creation, all)
27 #[arg(short, long, default_value = "nip01-smoke")] 28 #[arg(short, long, default_value = "all")]
28 spec: String, 29 spec: String,
30
31 /// Git data directory (required for cors, git-clone, push-auth, repo-creation specs)
32 #[arg(short, long)]
33 git_data_dir: Option<PathBuf>,
29 }, 34 },
30} 35}
31 36
@@ -42,19 +47,29 @@ async fn main() -> Result<()> {
42 let cli = Cli::parse(); 47 let cli = Cli::parse();
43 48
44 match cli.command { 49 match cli.command {
45 Commands::Audit { relay, mode, spec } => { 50 Commands::Audit { relay, mode, spec, git_data_dir } => {
46 let config = match mode.as_str() { 51 let config = match mode.as_str() {
47 "ci" => AuditConfig::ci(), 52 "ci" => AuditConfig::ci(),
48 "production" => AuditConfig::production(), 53 "production" => AuditConfig::production(),
49 _ => return Err(anyhow!("Invalid mode: {}. Use 'ci' or 'production'", mode)), 54 _ => return Err(anyhow!("Invalid mode: {}. Use 'ci' or 'production'", mode)),
50 }; 55 };
51 56
57 // Derive relay_domain from relay URL (e.g., "ws://localhost:8081" -> "localhost:8081")
58 let relay_domain = relay
59 .replace("ws://", "")
60 .replace("wss://", "")
61 .trim_end_matches('/')
62 .to_string();
63
52 println!("🔍 GRASP Audit Tool"); 64 println!("🔍 GRASP Audit Tool");
53 println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); 65 println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
54 println!("Relay: {}", relay); 66 println!("Relay: {}", relay);
55 println!("Mode: {}", mode); 67 println!("Mode: {}", mode);
56 println!("Spec: {}", spec); 68 println!("Spec: {}", spec);
57 println!("Run ID: {}", config.run_id); 69 println!("Run ID: {}", config.run_id);
70 if let Some(ref dir) = git_data_dir {
71 println!("Git Dir: {}", dir.display());
72 }
58 println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); 73 println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
59 println!(); 74 println!();
60 75
@@ -69,18 +84,98 @@ async fn main() -> Result<()> {
69 84
70 println!("✓ Connected\n"); 85 println!("✓ Connected\n");
71 86
87 // Helper to check if git_data_dir is required
88 let require_git_data_dir = |spec_name: &str| -> Result<PathBuf> {
89 git_data_dir.clone().ok_or_else(|| {
90 anyhow!(
91 "The '{}' spec requires --git-data-dir to be specified",
92 spec_name
93 )
94 })
95 };
96
72 let results = match spec.as_str() { 97 let results = match spec.as_str() {
73 "nip01-smoke" => { 98 "nip01-smoke" => {
74 println!("Running NIP-01 smoke tests...\n"); 99 println!("Running NIP-01 smoke tests...\n");
75 specs::Nip01SmokeTests::run_all(&client).await 100 specs::Nip01SmokeTests::run_all(&client).await
76 } 101 }
102 "nip11" => {
103 println!("Running NIP-11 document tests...\n");
104 specs::Nip11DocumentTests::run_all(&client).await
105 }
106 "event-acceptance" => {
107 println!("Running event acceptance policy tests...\n");
108 specs::EventAcceptancePolicyTests::run_all(&client).await
109 }
110 "cors" => {
111 println!("Running CORS tests...\n");
112 specs::CorsTests::run_all(&client, &relay_domain).await
113 }
114 "git-clone" => {
115 let dir = require_git_data_dir("git-clone")?;
116 println!("Running Git clone tests...\n");
117 specs::GitCloneTests::run_all(&client, &dir, &relay_domain).await
118 }
119 "push-auth" => {
120 let dir = require_git_data_dir("push-auth")?;
121 println!("Running push authorization tests...\n");
122 specs::PushAuthorizationTests::run_all(&client, &dir, &relay_domain).await
123 }
124 "repo-creation" => {
125 let dir = require_git_data_dir("repo-creation")?;
126 println!("Running repository creation tests...\n");
127 specs::RepositoryCreationTests::run_all(&client, &dir).await
128 }
77 "all" => { 129 "all" => {
78 println!("Running all tests...\n"); 130 println!("Running all tests...\n");
79 specs::Nip01SmokeTests::run_all(&client).await 131 let mut all_results = AuditResult::new("All GRASP-01 Tests");
132
133 // NIP-01 smoke tests
134 println!(" → NIP-01 smoke tests...");
135 let nip01_results = specs::Nip01SmokeTests::run_all(&client).await;
136 all_results.merge(nip01_results);
137
138 // NIP-11 document tests
139 println!(" → NIP-11 document tests...");
140 let nip11_results = specs::Nip11DocumentTests::run_all(&client).await;
141 all_results.merge(nip11_results);
142
143 // Event acceptance policy tests
144 println!(" → Event acceptance policy tests...");
145 let event_results = specs::EventAcceptancePolicyTests::run_all(&client).await;
146 all_results.merge(event_results);
147
148 // CORS tests
149 println!(" → CORS tests...");
150 let cors_results = specs::CorsTests::run_all(&client, &relay_domain).await;
151 all_results.merge(cors_results);
152
153 // Tests that require git_data_dir
154 if let Some(ref dir) = git_data_dir {
155 // Git clone tests
156 println!(" → Git clone tests...");
157 let clone_results = specs::GitCloneTests::run_all(&client, dir, &relay_domain).await;
158 all_results.merge(clone_results);
159
160 // Push authorization tests
161 println!(" → Push authorization tests...");
162 let push_results = specs::PushAuthorizationTests::run_all(&client, dir, &relay_domain).await;
163 all_results.merge(push_results);
164
165 // Repository creation tests
166 println!(" → Repository creation tests...");
167 let repo_results = specs::RepositoryCreationTests::run_all(&client, dir).await;
168 all_results.merge(repo_results);
169 } else {
170 println!(" ⚠ Skipping git-clone, push-auth, repo-creation tests (no --git-data-dir)");
171 }
172
173 println!();
174 all_results
80 } 175 }
81 _ => { 176 _ => {
82 return Err(anyhow!( 177 return Err(anyhow!(
83 "Unknown spec: {}. Use 'nip01-smoke' or 'all'", 178 "Unknown spec: {}. Use 'nip01-smoke', 'nip11', 'event-acceptance', 'cors', 'git-clone', 'push-auth', 'repo-creation', or 'all'",
84 spec 179 spec
85 )) 180 ))
86 } 181 }
diff --git a/grasp-audit/src/specs/grasp01/git_clone.rs b/grasp-audit/src/specs/grasp01/git_clone.rs
index da60f26..8c91c04 100644
--- a/grasp-audit/src/specs/grasp01/git_clone.rs
+++ b/grasp-audit/src/specs/grasp01/git_clone.rs
@@ -24,6 +24,20 @@ use std::process::Command;
24pub struct GitCloneTests; 24pub struct GitCloneTests;
25 25
26impl GitCloneTests { 26impl GitCloneTests {
27 /// Run all Git clone tests
28 pub async fn run_all(
29 client: &AuditClient,
30 git_data_dir: &Path,
31 relay_domain: &str,
32 ) -> crate::AuditResult {
33 let mut results = crate::AuditResult::new("GRASP-01 Git Clone Tests");
34
35 results.add(Self::test_basic_git_clone(client, git_data_dir, relay_domain).await);
36 results.add(Self::test_clone_url_format(client, git_data_dir, relay_domain).await);
37
38 results
39 }
40
27 /// Test that a repository can be cloned via Git HTTP backend 41 /// Test that a repository can be cloned via Git HTTP backend
28 /// 42 ///
29 /// This test: 43 /// This test:
diff --git a/grasp-audit/src/specs/grasp01/mod.rs b/grasp-audit/src/specs/grasp01/mod.rs
index 0d0bd9c..b5471d1 100644
--- a/grasp-audit/src/specs/grasp01/mod.rs
+++ b/grasp-audit/src/specs/grasp01/mod.rs
@@ -1,4 +1,16 @@
1//! GRASP-01 specification tests 1//! GRASP-01 specification tests
2//!
3//! This module contains all test suites for GRASP-01 compliance testing.
4//!
5//! ## Test Suites
6//!
7//! - [`Nip01SmokeTests`] - Basic NIP-01 relay functionality (WebSocket-only)
8//! - [`Nip11DocumentTests`] - NIP-11 relay information document (WebSocket-only)
9//! - [`EventAcceptancePolicyTests`] - Event acceptance rules (WebSocket-only)
10//! - [`CorsTests`] - CORS headers on Git HTTP endpoints (requires git-data-dir)
11//! - [`GitCloneTests`] - Git clone operations (requires git-data-dir)
12//! - [`PushAuthorizationTests`] - Push authorization (requires git-data-dir)
13//! - [`RepositoryCreationTests`] - Repository creation (requires git-data-dir)
2 14
3pub mod cors; 15pub mod cors;
4pub mod event_acceptance_policy; 16pub mod event_acceptance_policy;
@@ -14,4 +26,4 @@ pub use git_clone::GitCloneTests;
14pub use nip01_smoke::Nip01SmokeTests; 26pub use nip01_smoke::Nip01SmokeTests;
15pub use nip11_document::Nip11DocumentTests; 27pub use nip11_document::Nip11DocumentTests;
16pub use push_authorization::PushAuthorizationTests; 28pub use push_authorization::PushAuthorizationTests;
17pub use repository_creation::RepositoryCreationTests; 29pub use repository_creation::{is_bare_repository, RepositoryCreationTests};
diff --git a/grasp-audit/src/specs/grasp01/push_authorization.rs b/grasp-audit/src/specs/grasp01/push_authorization.rs
index fad77fb..4599ea5 100644
--- a/grasp-audit/src/specs/grasp01/push_authorization.rs
+++ b/grasp-audit/src/specs/grasp01/push_authorization.rs
@@ -30,6 +30,22 @@ use std::path::Path;
30pub struct PushAuthorizationTests; 30pub struct PushAuthorizationTests;
31 31
32impl PushAuthorizationTests { 32impl PushAuthorizationTests {
33 /// Run all push authorization tests
34 pub async fn run_all(
35 client: &AuditClient,
36 git_data_dir: &Path,
37 relay_domain: &str,
38 ) -> crate::AuditResult {
39 let mut results = crate::AuditResult::new("GRASP-01 Push Authorization Tests");
40
41 results.add(Self::test_push_authorized_by_owner_state(client, git_data_dir, relay_domain).await);
42 results.add(Self::test_push_rejected_without_state_event(client, git_data_dir, relay_domain).await);
43 results.add(Self::test_push_rejected_wrong_commit(client, git_data_dir, relay_domain).await);
44 results.add(Self::test_push_authorized_by_maintainer_state_only(client, git_data_dir, relay_domain).await);
45
46 results
47 }
48
33 /// Test that push is authorized when state event matches the commit 49 /// Test that push is authorized when state event matches the commit
34 /// 50 ///
35 /// GRASP-01: "MUST accept pushes via this service that match the latest 51 /// GRASP-01: "MUST accept pushes via this service that match the latest
diff --git a/grasp-audit/src/specs/grasp01/repository_creation.rs b/grasp-audit/src/specs/grasp01/repository_creation.rs
index 31ef400..2eaf32f 100644
--- a/grasp-audit/src/specs/grasp01/repository_creation.rs
+++ b/grasp-audit/src/specs/grasp01/repository_creation.rs
@@ -24,6 +24,20 @@ use std::path::Path;
24pub struct RepositoryCreationTests; 24pub struct RepositoryCreationTests;
25 25
26impl RepositoryCreationTests { 26impl RepositoryCreationTests {
27 /// Run all repository creation tests
28 pub async fn run_all(
29 client: &AuditClient,
30 git_data_dir: &Path,
31 ) -> crate::AuditResult {
32 let mut results = crate::AuditResult::new("GRASP-01 Repository Creation Tests");
33
34 results.add(Self::test_bare_repo_created_on_announcement(client, git_data_dir).await);
35 results.add(Self::test_repo_creation_idempotent(client, git_data_dir).await);
36 results.add(Self::test_bare_repo_structure(client, git_data_dir).await);
37
38 results
39 }
40
27 /// Test that a bare repository is created when a valid announcement is accepted 41 /// Test that a bare repository is created when a valid announcement is accepted
28 /// 42 ///
29 /// This test: 43 /// This test:
diff --git a/grasp-audit/src/specs/mod.rs b/grasp-audit/src/specs/mod.rs
index a502866..1444c80 100644
--- a/grasp-audit/src/specs/mod.rs
+++ b/grasp-audit/src/specs/mod.rs
@@ -1,6 +1,11 @@
1//! Test specifications 1//! Test specifications
2//!
3//! This module contains all GRASP specification test suites.
2 4
3pub mod grasp01; 5pub mod grasp01;
4 6
5// Re-export all test structs from grasp01 module 7// Re-export all test structs from grasp01 module
6pub use grasp01::{EventAcceptancePolicyTests, Nip01SmokeTests, Nip11DocumentTests}; 8pub use grasp01::{
9 CorsTests, EventAcceptancePolicyTests, GitCloneTests, Nip01SmokeTests, Nip11DocumentTests,
10 PushAuthorizationTests, RepositoryCreationTests,
11};