grasp-audit run all tests in audit mode

This commit is contained in:
DanConwayDev
2025-11-28 01:44:58 +00:00
parent 0c1d60a2ad
commit f053827e0a
7 changed files with 179 additions and 13 deletions
+100 -5
View File
@@ -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<PathBuf>,
},
}
@@ -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<PathBuf> {
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
))
}
@@ -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:
+13 -1
View File
@@ -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};
@@ -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
@@ -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:
+6 -1
View File
@@ -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,
};
+16 -6
View File
@@ -35,19 +35,27 @@ OPTIONS:
--mode <audit|test> Execution mode (default: audit)
audit: Run grasp-audit CLI tool
test: Run cargo test suite
--spec <spec> Specification to test (default: nip01-smoke)
--spec <spec> Specification to test (default: all)
Available specs: nip01-smoke, nip11, event-acceptance,
cors, git-clone, push-auth, repo-creation, all
Only used in audit mode
--help Show this help message
EXAMPLES:
# Run audit with default settings (current behavior)
# Run audit with all tests (default)
./test-ngit-relay.sh
# Run cargo tests instead
./test-ngit-relay.sh --mode test
# Run audit with specific spec
./test-ngit-relay.sh --mode audit --spec grasp01
./test-ngit-relay.sh --mode audit --spec nip01-smoke
./test-ngit-relay.sh --mode audit --spec nip11
./test-ngit-relay.sh --mode audit --spec event-acceptance
./test-ngit-relay.sh --mode audit --spec cors
./test-ngit-relay.sh --mode audit --spec git-clone
./test-ngit-relay.sh --mode audit --spec push-auth
./test-ngit-relay.sh --mode audit --spec repo-creation
EOF
exit 0
@@ -56,9 +64,9 @@ EOF
# -----------------------------------------------------------------------------
# Argument Parsing
# -----------------------------------------------------------------------------
# Default values maintain backward compatibility
# Default: run audit mode with all specs
MODE="audit"
SPEC="nip01-smoke"
SPEC="all"
# Parse command-line arguments
while [[ $# -gt 0 ]]; do
@@ -184,11 +192,13 @@ if [ "$MODE" = "audit" ]; then
# - --relay: Command-line parameter for relay address
# - --mode ci: Continuous integration mode (structured output)
# - --spec: Which specification to test
# - --git-data-dir: Path to git data directory (for git-clone, push-auth, repo-creation)
# The '|| { }' block provides user-friendly messaging on failure
RELAY_URL="ws://localhost:$PORT" cargo run -- audit \
--relay "ws://localhost:$PORT" \
--mode ci \
--spec "$SPEC" || {
--spec "$SPEC" \
--git-data-dir "$TEST_DIR/repos" || {
echo "⚠️ Some tests failed (expected for ngit-relay)"
echo " Validation tests should have passed"
}