upleb.uk

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

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-11-28 02:19:47 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-11-28 02:19:47 +0000
commit43e53b4ccd9fcebf20cc9c1bdbfe568ddd8051b9 (patch)
tree4aea212e5b1d307affb8e0ce5db1cc898e217889
parentf053827e0a157f348d9cf834f026a8de322abfe2 (diff)
grasp-audit improve cli output eg. add colors and condense
-rw-r--r--grasp-audit/README.md29
-rw-r--r--grasp-audit/src/result.rs76
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
21``` 21```
22 22
23This automatically: 23This automatically:
24
24- ✅ Starts ngit-relay in an isolated Docker container 25- ✅ Starts ngit-relay in an isolated Docker container
25- ✅ Runs all GRASP-01 compliance tests 26- ✅ Runs all GRASP-01 compliance tests
26- ✅ Cleans up resources when finished 27- ✅ Cleans up resources when finished
27 28
28**Currently Passing:** 4/18 tests (14 tests stubbed with "Not implemented yet")
29
30For more options: 29For more options:
30
31```bash 31```bash
32./test-ngit-relay.sh --help 32./test-ngit-relay.sh --help
33``` 33```
@@ -218,16 +218,16 @@ pub async fn test_something(client: &AuditClient) -> TestResult {
218 .run(|| async { 218 .run(|| async {
219 // 1. Context 219 // 1. Context
220 let ctx = TestContext::new(client); 220 let ctx = TestContext::new(client);
221 221
222 // 2. Prerequisites (cached per-TestContext) 222 // 2. Prerequisites (cached per-TestContext)
223 let repo = ctx.get_fixture(FixtureKind::ValidRepo).await?; 223 let repo = ctx.get_fixture(FixtureKind::ValidRepo).await?;
224 224
225 // 3. Test-specific event 225 // 3. Test-specific event
226 let my_event = client.create_issue(&repo, "Title", "Content", vec![])?; 226 let my_event = client.create_issue(&repo, "Title", "Content", vec![])?;
227 227
228 // 4. Verify 228 // 4. Verify
229 send_and_verify_accepted(client, my_event, "description").await?; 229 send_and_verify_accepted(client, my_event, "description").await?;
230 230
231 Ok(()) 231 Ok(())
232 }) 232 })
233 .await 233 .await
@@ -252,14 +252,14 @@ pub async fn test_something(client: &AuditClient) -> TestResult {
252 252
253### Available Fixtures 253### Available Fixtures
254 254
255| FixtureKind | Provides | Use When | 255| FixtureKind | Provides | Use When |
256|-------------|----------|----------| 256| ------------------------ | --------------------------------------- | ------------------------------------- |
257| `ValidRepo` | Accepted repo announcement (kind 30617) | Need a repo as prerequisite | 257| `ValidRepo` | Accepted repo announcement (kind 30617) | Need a repo as prerequisite |
258| `RepoState` | Repo + state event (kind 30618) | Testing owner push authorization | 258| `RepoState` | Repo + state event (kind 30618) | Testing owner push authorization |
259| `MaintainerAnnouncement` | Maintainer's repo announcement | Testing maintainer chain setup | 259| `MaintainerAnnouncement` | Maintainer's repo announcement | Testing maintainer chain setup |
260| `MaintainerState` | Maintainer's state event | Testing maintainer push authorization | 260| `MaintainerState` | Maintainer's state event | Testing maintainer push authorization |
261| `RepoWithIssue` | Repo + accepted issue (kind 1621) | Testing issue-dependent events | 261| `RepoWithIssue` | Repo + accepted issue (kind 1621) | Testing issue-dependent events |
262| `RepoWithComment` | Repo + issue + comment | Testing comment-dependent events | 262| `RepoWithComment` | Repo + issue + comment | Testing comment-dependent events |
263 263
264### Fixture Lifecycle: Generate → Send → Verify 264### Fixture Lifecycle: Generate → Send → Verify
265 265
@@ -283,6 +283,7 @@ Each TestContext shares a `run_id` with all events:
283``` 283```
284 284
285This enables: 285This enables:
286
286- Event correlation within a test run 287- Event correlation within a test run
287- Production relay cleanup scripts 288- Production relay cleanup scripts
288- Test isolation between runs 289- 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 @@
1//! Test result types 1//! Test result types
2 2
3use std::collections::BTreeMap;
3use std::time::{Duration, Instant}; 4use std::time::{Duration, Instant};
4 5
6// ANSI color codes
7const GREEN: &str = "\x1b[32m";
8const RED: &str = "\x1b[31m";
9const RESET: &str = "\x1b[0m";
10const BOLD: &str = "\x1b[1m";
11
12/// Extract spec category from a spec_ref by removing trailing test number
13/// e.g., "GRASP-01:event-acceptance:1.1" -> "GRASP-01:event-acceptance"
14/// e.g., "NIP-01:basic:2" -> "NIP-01:basic"
15fn extract_spec_category(spec_ref: &str) -> String {
16 let parts: Vec<&str> = spec_ref.split(':').collect();
17 if parts.len() >= 2 {
18 // Check if the last part looks like a test number (starts with digit)
19 if let Some(last) = parts.last() {
20 if last.chars().next().map(|c| c.is_ascii_digit()).unwrap_or(false) {
21 // Remove the trailing number part
22 return parts[..parts.len() - 1].join(":");
23 }
24 }
25 }
26 // Return as-is if no trailing number found
27 spec_ref.to_string()
28}
29
5/// Result of a single test 30/// Result of a single test
6#[derive(Debug, Clone)] 31#[derive(Debug, Clone)]
7pub struct TestResult { 32pub struct TestResult {
@@ -108,34 +133,55 @@ impl AuditResult {
108 self.results.len() 133 self.results.len()
109 } 134 }
110 135
111 /// Print a detailed report 136 /// Print a detailed report with tests grouped by spec_ref
112 pub fn print_report(&self) { 137 pub fn print_report(&self) {
113 println!("\n{}", self.spec); 138 println!("\n{}{}{}", BOLD, self.spec, RESET);
114 println!("{}", "═".repeat(60)); 139 println!("{}", "═".repeat(60));
115 println!();
116 140
117 let passed = self.passed_count(); 141 let passed = self.passed_count();
118 let total = self.total_count(); 142 let total = self.total_count();
119 143
144 // Group results by spec category (strip trailing test number like ":1.1")
145 let mut grouped: BTreeMap<String, Vec<&TestResult>> = BTreeMap::new();
120 for result in &self.results { 146 for result in &self.results {
121 let status = if result.passed { "✓" } else { "✗" }; 147 // Extract category from spec_ref (e.g., "GRASP-01:event-acceptance:1.1" -> "GRASP-01:event-acceptance")
148 let category = extract_spec_category(&result.spec_ref);
149 grouped
150 .entry(category)
151 .or_default()
152 .push(result);
153 }
122 154
123 println!("{} {} ({})", status, result.name, result.spec_ref); 155 // Print grouped results
124 println!(" Requirement: {}", result.requirement); 156 for (category, results) in &grouped {
157 println!("\n{}[{}]{}", BOLD, category, RESET);
125 158
126 if let Some(error) = &result.error { 159 for result in results {
127 println!(" Error: {}", error); 160 let (color, status) = if result.passed {
128 } 161 (GREEN, "✓")
162 } else {
163 (RED, "✗")
164 };
165
166 println!(" {}{} {}{}", color, status, result.name, RESET);
129 167
130 println!(" Duration: {:?}", result.duration); 168 if let Some(error) = &result.error {
131 println!(); 169 println!(" {}Error: {}{}", RED, error, RESET);
170 }
171 }
132 } 172 }
133 173
134 println!( 174 println!();
135 "Results: {}/{} passed ({:.1}%)", 175 let pass_rate = if total > 0 {
136 passed,
137 total,
138 (passed as f64 / total as f64) * 100.0 176 (passed as f64 / total as f64) * 100.0
177 } else {
178 0.0
179 };
180
181 let summary_color = if passed == total { GREEN } else { RED };
182 println!(
183 "{}Results: {}/{} passed ({:.1}%){}",
184 summary_color, passed, total, pass_rate, RESET
139 ); 185 );
140 println!(); 186 println!();
141 } 187 }