From f053827e0a157f348d9cf834f026a8de322abfe2 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Fri, 28 Nov 2025 01:44:58 +0000 Subject: grasp-audit run all tests in audit mode --- grasp-audit/src/bin/grasp-audit.rs | 105 ++++++++++++++++++++- grasp-audit/src/specs/grasp01/git_clone.rs | 14 +++ grasp-audit/src/specs/grasp01/mod.rs | 14 ++- .../src/specs/grasp01/push_authorization.rs | 16 ++++ .../src/specs/grasp01/repository_creation.rs | 14 +++ grasp-audit/src/specs/mod.rs | 7 +- 6 files changed, 163 insertions(+), 7 deletions(-) (limited to 'grasp-audit/src') 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 @@ use clap::{Parser, Subcommand}; use grasp_audit::*; +use std::path::PathBuf; #[derive(Parser)] #[command(name = "grasp-audit")] @@ -23,9 +24,13 @@ enum Commands { #[arg(short, long, default_value = "ci")] mode: String, - /// Spec to test (nip01-smoke, all) - #[arg(short, long, default_value = "nip01-smoke")] + /// Spec to test (nip01-smoke, nip11, event-acceptance, cors, git-clone, push-auth, repo-creation, all) + #[arg(short, long, default_value = "all")] spec: String, + + /// Git data directory (required for cors, git-clone, push-auth, repo-creation specs) + #[arg(short, long)] + git_data_dir: Option, }, } @@ -42,19 +47,29 @@ async fn main() -> Result<()> { let cli = Cli::parse(); match cli.command { - Commands::Audit { relay, mode, spec } => { + Commands::Audit { relay, mode, spec, git_data_dir } => { let config = match mode.as_str() { "ci" => AuditConfig::ci(), "production" => AuditConfig::production(), _ => return Err(anyhow!("Invalid mode: {}. Use 'ci' or 'production'", mode)), }; + // Derive relay_domain from relay URL (e.g., "ws://localhost:8081" -> "localhost:8081") + let relay_domain = relay + .replace("ws://", "") + .replace("wss://", "") + .trim_end_matches('/') + .to_string(); + println!("🔍 GRASP Audit Tool"); println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); println!("Relay: {}", relay); println!("Mode: {}", mode); println!("Spec: {}", spec); println!("Run ID: {}", config.run_id); + if let Some(ref dir) = git_data_dir { + println!("Git Dir: {}", dir.display()); + } println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); println!(); @@ -69,18 +84,98 @@ async fn main() -> Result<()> { println!("✓ Connected\n"); + // Helper to check if git_data_dir is required + let require_git_data_dir = |spec_name: &str| -> Result { + git_data_dir.clone().ok_or_else(|| { + anyhow!( + "The '{}' spec requires --git-data-dir to be specified", + spec_name + ) + }) + }; + let results = match spec.as_str() { "nip01-smoke" => { println!("Running NIP-01 smoke tests...\n"); specs::Nip01SmokeTests::run_all(&client).await } + "nip11" => { + println!("Running NIP-11 document tests...\n"); + specs::Nip11DocumentTests::run_all(&client).await + } + "event-acceptance" => { + println!("Running event acceptance policy tests...\n"); + specs::EventAcceptancePolicyTests::run_all(&client).await + } + "cors" => { + println!("Running CORS tests...\n"); + specs::CorsTests::run_all(&client, &relay_domain).await + } + "git-clone" => { + let dir = require_git_data_dir("git-clone")?; + println!("Running Git clone tests...\n"); + specs::GitCloneTests::run_all(&client, &dir, &relay_domain).await + } + "push-auth" => { + let dir = require_git_data_dir("push-auth")?; + println!("Running push authorization tests...\n"); + specs::PushAuthorizationTests::run_all(&client, &dir, &relay_domain).await + } + "repo-creation" => { + let dir = require_git_data_dir("repo-creation")?; + println!("Running repository creation tests...\n"); + specs::RepositoryCreationTests::run_all(&client, &dir).await + } "all" => { println!("Running all tests...\n"); - specs::Nip01SmokeTests::run_all(&client).await + let mut all_results = AuditResult::new("All GRASP-01 Tests"); + + // NIP-01 smoke tests + println!(" → NIP-01 smoke tests..."); + let nip01_results = specs::Nip01SmokeTests::run_all(&client).await; + all_results.merge(nip01_results); + + // NIP-11 document tests + println!(" → NIP-11 document tests..."); + let nip11_results = specs::Nip11DocumentTests::run_all(&client).await; + all_results.merge(nip11_results); + + // Event acceptance policy tests + println!(" → Event acceptance policy tests..."); + let event_results = specs::EventAcceptancePolicyTests::run_all(&client).await; + all_results.merge(event_results); + + // CORS tests + println!(" → CORS tests..."); + let cors_results = specs::CorsTests::run_all(&client, &relay_domain).await; + all_results.merge(cors_results); + + // Tests that require git_data_dir + if let Some(ref dir) = git_data_dir { + // Git clone tests + println!(" → Git clone tests..."); + let clone_results = specs::GitCloneTests::run_all(&client, dir, &relay_domain).await; + all_results.merge(clone_results); + + // Push authorization tests + println!(" → Push authorization tests..."); + let push_results = specs::PushAuthorizationTests::run_all(&client, dir, &relay_domain).await; + all_results.merge(push_results); + + // Repository creation tests + println!(" → Repository creation tests..."); + let repo_results = specs::RepositoryCreationTests::run_all(&client, dir).await; + all_results.merge(repo_results); + } else { + println!(" ⚠ Skipping git-clone, push-auth, repo-creation tests (no --git-data-dir)"); + } + + println!(); + all_results } _ => { return Err(anyhow!( - "Unknown spec: {}. Use 'nip01-smoke' or 'all'", + "Unknown spec: {}. Use 'nip01-smoke', 'nip11', 'event-acceptance', 'cors', 'git-clone', 'push-auth', 'repo-creation', or 'all'", spec )) } 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; pub struct GitCloneTests; impl GitCloneTests { + /// Run all Git clone tests + pub async fn run_all( + client: &AuditClient, + git_data_dir: &Path, + relay_domain: &str, + ) -> crate::AuditResult { + let mut results = crate::AuditResult::new("GRASP-01 Git Clone Tests"); + + results.add(Self::test_basic_git_clone(client, git_data_dir, relay_domain).await); + results.add(Self::test_clone_url_format(client, git_data_dir, relay_domain).await); + + results + } + /// Test that a repository can be cloned via Git HTTP backend /// /// 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 @@ //! GRASP-01 specification tests +//! +//! This module contains all test suites for GRASP-01 compliance testing. +//! +//! ## Test Suites +//! +//! - [`Nip01SmokeTests`] - Basic NIP-01 relay functionality (WebSocket-only) +//! - [`Nip11DocumentTests`] - NIP-11 relay information document (WebSocket-only) +//! - [`EventAcceptancePolicyTests`] - Event acceptance rules (WebSocket-only) +//! - [`CorsTests`] - CORS headers on Git HTTP endpoints (requires git-data-dir) +//! - [`GitCloneTests`] - Git clone operations (requires git-data-dir) +//! - [`PushAuthorizationTests`] - Push authorization (requires git-data-dir) +//! - [`RepositoryCreationTests`] - Repository creation (requires git-data-dir) pub mod cors; pub mod event_acceptance_policy; @@ -14,4 +26,4 @@ pub use git_clone::GitCloneTests; pub use nip01_smoke::Nip01SmokeTests; pub use nip11_document::Nip11DocumentTests; pub use push_authorization::PushAuthorizationTests; -pub use repository_creation::RepositoryCreationTests; +pub 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; pub struct PushAuthorizationTests; impl PushAuthorizationTests { + /// Run all push authorization tests + pub async fn run_all( + client: &AuditClient, + git_data_dir: &Path, + relay_domain: &str, + ) -> crate::AuditResult { + let mut results = crate::AuditResult::new("GRASP-01 Push Authorization Tests"); + + results.add(Self::test_push_authorized_by_owner_state(client, git_data_dir, relay_domain).await); + results.add(Self::test_push_rejected_without_state_event(client, git_data_dir, relay_domain).await); + results.add(Self::test_push_rejected_wrong_commit(client, git_data_dir, relay_domain).await); + results.add(Self::test_push_authorized_by_maintainer_state_only(client, git_data_dir, relay_domain).await); + + results + } + /// Test that push is authorized when state event matches the commit /// /// 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; pub struct RepositoryCreationTests; impl RepositoryCreationTests { + /// Run all repository creation tests + pub async fn run_all( + client: &AuditClient, + git_data_dir: &Path, + ) -> crate::AuditResult { + let mut results = crate::AuditResult::new("GRASP-01 Repository Creation Tests"); + + results.add(Self::test_bare_repo_created_on_announcement(client, git_data_dir).await); + results.add(Self::test_repo_creation_idempotent(client, git_data_dir).await); + results.add(Self::test_bare_repo_structure(client, git_data_dir).await); + + results + } + /// Test that a bare repository is created when a valid announcement is accepted /// /// 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 @@ //! Test specifications +//! +//! This module contains all GRASP specification test suites. pub mod grasp01; // Re-export all test structs from grasp01 module -pub use grasp01::{EventAcceptancePolicyTests, Nip01SmokeTests, Nip11DocumentTests}; +pub use grasp01::{ + CorsTests, EventAcceptancePolicyTests, GitCloneTests, Nip01SmokeTests, Nip11DocumentTests, + PushAuthorizationTests, RepositoryCreationTests, +}; -- cgit v1.2.3