From bf7f4d5381203d5c27b2811d62c5b1781533aa2b Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Wed, 19 Nov 2025 17:01:36 +0000 Subject: fix some clippy fmt warnings --- grasp-audit/src/client.rs | 174 +++++++++++++++++++++++++++------------------- 1 file changed, 104 insertions(+), 70 deletions(-) (limited to 'grasp-audit/src/client.rs') diff --git a/grasp-audit/src/client.rs b/grasp-audit/src/client.rs index 1f6f0fb..019f4cb 100644 --- a/grasp-audit/src/client.rs +++ b/grasp-audit/src/client.rs @@ -24,32 +24,32 @@ impl AuditClient { keys, } } - + /// Create a new audit client pub async fn new(relay_url: &str, config: AuditConfig) -> Result { let keys = Keys::generate(); let client = Client::new(keys.clone()); - + // Add relay and connect client.add_relay(relay_url).await?; client.connect().await; - + // Wait for connection to establish (with retries) let mut attempts = 0; let mut connected = false; while attempts < 20 { tokio::time::sleep(Duration::from_millis(100)).await; - + let relays = client.relays().await; connected = relays.values().any(|r| r.is_connected()); - + if connected { break; } - + attempts += 1; } - + // Verify we actually connected if !connected { return Err(anyhow!( @@ -68,22 +68,22 @@ impl AuditClient { relay_url )); } - + // Give it a bit more time to stabilize tokio::time::sleep(Duration::from_millis(200)).await; - + Ok(Self { client, config, keys, }) } - + /// Get the public key for this audit client pub fn public_key(&self) -> PublicKey { self.keys.public_key() } - + /// Check if connected to relay pub async fn is_connected(&self) -> bool { // Check if we have any connected relays @@ -95,29 +95,29 @@ impl AuditClient { } false } - + /// Send an event (with audit tags automatically added) pub async fn send_event(&self, event: Event) -> Result { if self.config.read_only { return Err(anyhow!("Client is in read-only mode")); } - + let output = self.client.send_event(&event).await?; let event_id = *output.id(); - + // Check if any relay rejected the event and return the error message if !output.failed.is_empty() { // Get the first failed relay error message let (relay_url, error) = output.failed.iter().next().unwrap(); return Err(anyhow!("Relay {} rejected event: {}", relay_url, error)); } - + // Wait a bit for event to propagate tokio::time::sleep(Duration::from_millis(100)).await; - + Ok(event_id) } - + /// Create an event builder that automatically includes audit tags /// /// All events built through this method will automatically have audit tags appended @@ -153,11 +153,11 @@ impl AuditClient { pub fn event_builder(&self, kind: Kind, content: impl Into) -> AuditEventBuilder { AuditEventBuilder::new(kind, content, self.config.clone()) } - + /// Query events, optionally filtered to this audit run pub async fn query(&self, mut filter: Filter) -> Result> { use nostr_sdk::prelude::{Alphabet, SingleLetterTag}; - + if self.config.mode == AuditMode::CI { // In CI mode, only see our own audit events // Filter by "t" tags (hashtags) @@ -167,14 +167,15 @@ impl AuditClient { .custom_tag(t_tag, format!("audit-{}", self.config.run_id)); } // In Production mode, see all events (no filter modification) - - let events = self.client + + let events = self + .client .fetch_events(filter, Duration::from_secs(5)) .await?; - + Ok(events.into_iter().collect()) } - + /// Subscribe to events with a callback pub async fn subscribe( &self, @@ -183,27 +184,25 @@ impl AuditClient { ) -> Result> { let timeout = timeout.unwrap_or(Duration::from_secs(5)); let mut all_events = Vec::new(); - + for filter in filters { - let events = self.client - .fetch_events(filter, timeout) - .await?; + let events = self.client.fetch_events(filter, timeout).await?; all_events.extend(events.into_iter()); } - + Ok(all_events) } - + /// Get the underlying nostr client (for advanced usage) pub fn client(&self) -> &Client { &self.client } - + /// Get the keys (for signing custom events) pub fn keys(&self) -> &Keys { &self.keys } - + /// Create a NIP-34 repository announcement event /// /// This helper creates a properly formatted NIP-34 announcement that will be @@ -216,37 +215,58 @@ impl AuditClient { /// A built and signed Event ready to be sent to the relay pub async fn create_repo_announcement(&self, test_name: &str) -> Result { // Get relay URL from client - let relay_url = self.client.relays().await + let relay_url = self + .client + .relays() + .await .keys() .next() .ok_or_else(|| anyhow!("No relay connected"))? .to_string(); - + // Convert WebSocket URL to HTTP URL for clone tag let http_url = relay_url .replace("ws://", "http://") .replace("wss://", "https://"); - + // Create unique repository identifier using UUID for consistency let repo_id = format!("{}-{}", test_name, &uuid::Uuid::new_v4().to_string()[..8]); - + // Get npub for clone URL - let npub = self.public_key().to_bech32() + let npub = self + .public_key() + .to_bech32() .map_err(|e| anyhow!("Failed to convert public key to bech32 npub format: {}", e))?; - + // Build kind 30617 repository announcement - let event = self.event_builder(Kind::GitRepoAnnouncement, format!("Test repository for {}", test_name)) + let event = self + .event_builder( + Kind::GitRepoAnnouncement, + format!("Test repository for {}", test_name), + ) .tag(Tag::identifier(&repo_id)) - .tag(Tag::custom(TagKind::custom("name"), vec![format!("{} Test Repository", test_name)])) - .tag(Tag::custom(TagKind::custom("description"), vec![format!("Repository for {} testing", test_name)])) - .tag(Tag::custom(TagKind::custom("clone"), vec![format!("{}/{}/{}.git", http_url, npub, repo_id)])) - .tag(Tag::custom(TagKind::custom("relays"), vec![relay_url.clone()])) + .tag(Tag::custom( + TagKind::custom("name"), + vec![format!("{} Test Repository", test_name)], + )) + .tag(Tag::custom( + TagKind::custom("description"), + vec![format!("Repository for {} testing", test_name)], + )) + .tag(Tag::custom( + TagKind::custom("clone"), + vec![format!("{}/{}/{}.git", http_url, npub, repo_id)], + )) + .tag(Tag::custom( + TagKind::custom("relays"), + vec![relay_url.clone()], + )) .build(self.keys()) .map_err(|e| anyhow!("Failed to build repository announcement event: {}", e))?; - + Ok(event) } - + /// Create an issue (kind 1621) that references a repository /// /// # Arguments @@ -265,29 +285,31 @@ impl AuditClient { additional_tags: Vec, ) -> Result { // Extract repo_id from the d tag - let repo_id = repo_event.tags.iter() + let repo_id = repo_event + .tags + .iter() .find(|t| t.kind() == TagKind::d()) .and_then(|t| t.content()) .ok_or_else(|| anyhow!("Repository event must have a 'd' tag"))? .to_string(); - + let repo_pubkey = repo_event.pubkey; let a_tag_value = format!("30617:{}:{}", repo_pubkey, repo_id); - + let mut tags = vec![ Tag::custom(TagKind::custom("a"), vec![a_tag_value]), Tag::custom(TagKind::custom("subject"), vec![issue_title]), ]; - + // Add any additional tags tags.extend(additional_tags); - + self.event_builder(Kind::Custom(1621), content) .tags(tags) .build(self.keys()) .map_err(|e| anyhow!("Failed to build issue event: {}", e)) } - + /// Create a NIP-22 comment (kind 1111) for an event /// /// # Arguments @@ -306,17 +328,20 @@ impl AuditClient { let event_kind = event.kind; let event_pubkey = event.pubkey; let event_id = event.id; - + let mut tags = vec![ - Tag::custom(TagKind::custom("E"), vec![event_id.to_hex(), "".to_string(), "root".to_string()]), + Tag::custom( + TagKind::custom("E"), + vec![event_id.to_hex(), "".to_string(), "root".to_string()], + ), Tag::event(event_id), Tag::custom(TagKind::custom("K"), vec![event_kind.as_u16().to_string()]), Tag::public_key(event_pubkey), ]; - + // Add any additional tags tags.extend(additional_tags); - + self.event_builder(Kind::Custom(1111), content) .tags(tags) .build(self.keys()) @@ -327,22 +352,22 @@ impl AuditClient { #[cfg(test)] mod tests { use super::*; - + #[tokio::test] async fn test_client_creation() { let config = AuditConfig::ci(); - + // This will fail if no relay is running, which is expected in tests // In real usage, there should be a relay at the URL let result = AuditClient::new("ws://localhost:7000", config).await; - + // We can't test connection without a running relay // But we can test that the client is created if let Ok(client) = result { assert_eq!(client.config.mode, AuditMode::CI); } } - + #[test] fn test_event_builder() { let config = AuditConfig::ci(); @@ -352,13 +377,13 @@ mod tests { config: config.clone(), keys: keys.clone(), }; - + let _builder = client.event_builder(Kind::TextNote, "test content"); - + // Builder should be created successfully // (We can't test the internal config field as it's private, which is correct) } - + #[test] fn test_audit_tags_automatically_added() { let config = AuditConfig::ci(); @@ -368,21 +393,28 @@ mod tests { config: config.clone(), keys: keys.clone(), }; - + // Create an event with a custom tag - let event = client.event_builder(Kind::TextNote, "test content") + let event = client + .event_builder(Kind::TextNote, "test content") .tag(Tag::custom(TagKind::custom("custom"), vec!["value"])) .build(&keys) .unwrap(); - + // Should have custom tag (1) + 3 audit tags = at least 4 tags - assert!(event.tags.len() >= 4, "Expected at least 4 tags, got {}", event.tags.len()); - + assert!( + event.tags.len() >= 4, + "Expected at least 4 tags, got {}", + event.tags.len() + ); + // Verify audit tags are present by checking tag content - let tag_contents: Vec = event.tags.iter() + let tag_contents: Vec = event + .tags + .iter() .filter_map(|t| t.content().map(|s| s.to_string())) .collect(); - + // Check for the three required audit tags assert!( tag_contents.contains(&"grasp-audit-test-event".to_string()), @@ -393,10 +425,12 @@ mod tests { "Missing 'audit-ci-*' tag" ); assert!( - tag_contents.iter().any(|t| t.starts_with("audit-cleanup-after-")), + tag_contents + .iter() + .any(|t| t.starts_with("audit-cleanup-after-")), "Missing 'audit-cleanup-after-*' tag" ); - + // Verify the custom tag is also present assert!( tag_contents.contains(&"value".to_string()), -- cgit v1.2.3