From 43e53b4ccd9fcebf20cc9c1bdbfe568ddd8051b9 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Fri, 28 Nov 2025 02:19:47 +0000 Subject: grasp-audit improve cli output eg. add colors and condense --- grasp-audit/README.md | 29 +++++++++--------- grasp-audit/src/result.rs | 76 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 76 insertions(+), 29 deletions(-) diff --git a/grasp-audit/README.md b/grasp-audit/README.md index 44deec2..ab3bddc 100644 --- a/grasp-audit/README.md +++ b/grasp-audit/README.md @@ -21,13 +21,13 @@ nix develop -c bash test-ngit-relay.sh --mode test ``` This automatically: + - ✅ Starts ngit-relay in an isolated Docker container - ✅ Runs all GRASP-01 compliance tests - ✅ Cleans up resources when finished -**Currently Passing:** 4/18 tests (14 tests stubbed with "Not implemented yet") - For more options: + ```bash ./test-ngit-relay.sh --help ``` @@ -218,16 +218,16 @@ pub async fn test_something(client: &AuditClient) -> TestResult { .run(|| async { // 1. Context let ctx = TestContext::new(client); - + // 2. Prerequisites (cached per-TestContext) let repo = ctx.get_fixture(FixtureKind::ValidRepo).await?; - + // 3. Test-specific event let my_event = client.create_issue(&repo, "Title", "Content", vec![])?; - + // 4. Verify send_and_verify_accepted(client, my_event, "description").await?; - + Ok(()) }) .await @@ -252,14 +252,14 @@ pub async fn test_something(client: &AuditClient) -> TestResult { ### Available Fixtures -| FixtureKind | Provides | Use When | -|-------------|----------|----------| -| `ValidRepo` | Accepted repo announcement (kind 30617) | Need a repo as prerequisite | -| `RepoState` | Repo + state event (kind 30618) | Testing owner push authorization | -| `MaintainerAnnouncement` | Maintainer's repo announcement | Testing maintainer chain setup | -| `MaintainerState` | Maintainer's state event | Testing maintainer push authorization | -| `RepoWithIssue` | Repo + accepted issue (kind 1621) | Testing issue-dependent events | -| `RepoWithComment` | Repo + issue + comment | Testing comment-dependent events | +| FixtureKind | Provides | Use When | +| ------------------------ | --------------------------------------- | ------------------------------------- | +| `ValidRepo` | Accepted repo announcement (kind 30617) | Need a repo as prerequisite | +| `RepoState` | Repo + state event (kind 30618) | Testing owner push authorization | +| `MaintainerAnnouncement` | Maintainer's repo announcement | Testing maintainer chain setup | +| `MaintainerState` | Maintainer's state event | Testing maintainer push authorization | +| `RepoWithIssue` | Repo + accepted issue (kind 1621) | Testing issue-dependent events | +| `RepoWithComment` | Repo + issue + comment | Testing comment-dependent events | ### Fixture Lifecycle: Generate → Send → Verify @@ -283,6 +283,7 @@ Each TestContext shares a `run_id` with all events: ``` This enables: + - Event correlation within a test run - Production relay cleanup scripts - Test isolation between runs diff --git a/grasp-audit/src/result.rs b/grasp-audit/src/result.rs index de377e5..2bec5c8 100644 --- a/grasp-audit/src/result.rs +++ b/grasp-audit/src/result.rs @@ -1,7 +1,32 @@ //! Test result types +use std::collections::BTreeMap; use std::time::{Duration, Instant}; +// ANSI color codes +const GREEN: &str = "\x1b[32m"; +const RED: &str = "\x1b[31m"; +const RESET: &str = "\x1b[0m"; +const BOLD: &str = "\x1b[1m"; + +/// Extract spec category from a spec_ref by removing trailing test number +/// e.g., "GRASP-01:event-acceptance:1.1" -> "GRASP-01:event-acceptance" +/// e.g., "NIP-01:basic:2" -> "NIP-01:basic" +fn extract_spec_category(spec_ref: &str) -> String { + let parts: Vec<&str> = spec_ref.split(':').collect(); + if parts.len() >= 2 { + // Check if the last part looks like a test number (starts with digit) + if let Some(last) = parts.last() { + if last.chars().next().map(|c| c.is_ascii_digit()).unwrap_or(false) { + // Remove the trailing number part + return parts[..parts.len() - 1].join(":"); + } + } + } + // Return as-is if no trailing number found + spec_ref.to_string() +} + /// Result of a single test #[derive(Debug, Clone)] pub struct TestResult { @@ -108,34 +133,55 @@ impl AuditResult { self.results.len() } - /// Print a detailed report + /// Print a detailed report with tests grouped by spec_ref pub fn print_report(&self) { - println!("\n{}", self.spec); + println!("\n{}{}{}", BOLD, self.spec, RESET); println!("{}", "═".repeat(60)); - println!(); let passed = self.passed_count(); let total = self.total_count(); + // Group results by spec category (strip trailing test number like ":1.1") + let mut grouped: BTreeMap> = BTreeMap::new(); for result in &self.results { - let status = if result.passed { "✓" } else { "✗" }; + // Extract category from spec_ref (e.g., "GRASP-01:event-acceptance:1.1" -> "GRASP-01:event-acceptance") + let category = extract_spec_category(&result.spec_ref); + grouped + .entry(category) + .or_default() + .push(result); + } - println!("{} {} ({})", status, result.name, result.spec_ref); - println!(" Requirement: {}", result.requirement); + // Print grouped results + for (category, results) in &grouped { + println!("\n{}[{}]{}", BOLD, category, RESET); - if let Some(error) = &result.error { - println!(" Error: {}", error); - } + for result in results { + let (color, status) = if result.passed { + (GREEN, "✓") + } else { + (RED, "✗") + }; + + println!(" {}{} {}{}", color, status, result.name, RESET); - println!(" Duration: {:?}", result.duration); - println!(); + if let Some(error) = &result.error { + println!(" {}Error: {}{}", RED, error, RESET); + } + } } - println!( - "Results: {}/{} passed ({:.1}%)", - passed, - total, + println!(); + let pass_rate = if total > 0 { (passed as f64 / total as f64) * 100.0 + } else { + 0.0 + }; + + let summary_color = if passed == total { GREEN } else { RED }; + println!( + "{}Results: {}/{} passed ({:.1}%){}", + summary_color, passed, total, pass_rate, RESET ); println!(); } -- cgit v1.2.3