upleb.uk

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

summaryrefslogtreecommitdiff
path: root/grasp-audit/src
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-11-05 13:32:50 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-11-05 13:32:50 +0000
commit5f137994850773114d8a4f8ba70f34aaf2eb1992 (patch)
treeaec84ac0536412896826e47de563213c26276988 /grasp-audit/src
parent64a86de9fc5ded51a1b5405223fc5dce16839fef (diff)
tag test events with audit-grasp-test-event
Diffstat (limited to 'grasp-audit/src')
-rw-r--r--grasp-audit/src/audit.rs40
-rw-r--r--grasp-audit/src/client.rs78
2 files changed, 116 insertions, 2 deletions
diff --git a/grasp-audit/src/audit.rs b/grasp-audit/src/audit.rs
index fad4bf2..105fa00 100644
--- a/grasp-audit/src/audit.rs
+++ b/grasp-audit/src/audit.rs
@@ -61,7 +61,45 @@ impl AuditConfig {
61 } 61 }
62 } 62 }
63 63
64 /// Get audit tags for an event 64 /// Get audit tags that are automatically added to all events
65 ///
66 /// These tags are automatically added to all events created via [`AuditEventBuilder`].
67 /// They provide isolation, cleanup scheduling, and easy discovery of audit events.
68 ///
69 /// # Tag Format
70 ///
71 /// All tags use the `"t"` (hashtag) format for maximum relay compatibility:
72 ///
73 /// 1. `["t", "grasp-audit-test-event"]` - Identifies all audit-related events
74 /// 2. `["t", "audit-{run_id}"]` - Unique identifier for this audit run
75 /// - CI mode: `audit-ci-{uuid}`
76 /// - Production mode: `audit-prod-audit-{timestamp}`
77 /// 3. `["t", "audit-cleanup-after-{unix_timestamp}"]` - Cleanup timestamp
78 /// - CI mode: Current time + 3600 seconds (1 hour)
79 /// - Production mode: Current time + 300 seconds (5 minutes)
80 ///
81 /// # Purpose
82 ///
83 /// - **Isolation**: Each test run has a unique ID for event filtering in CI mode
84 /// - **Cleanup**: Events marked for cleanup after timestamp (enables direct DB cleanup)
85 /// - **Discovery**: Easy to query all audit events via hashtag
86 /// - **No deletion trails**: Avoids NIP-09 deletion events by using direct cleanup
87 ///
88 /// # Example
89 ///
90 /// ```rust
91 /// use grasp_audit::AuditConfig;
92 ///
93 /// let config = AuditConfig::ci();
94 /// let tags = config.audit_tags();
95 ///
96 /// // Tags will look like:
97 /// // [
98 /// // ["t", "grasp-audit-test-event"],
99 /// // ["t", "audit-ci-a1b2c3d4-e5f6-7890-abcd-ef1234567890"],
100 /// // ["t", "audit-cleanup-after-1730822334"]
101 /// // ]
102 /// ```
65 pub fn audit_tags(&self) -> Vec<Tag> { 103 pub fn audit_tags(&self) -> Vec<Tag> {
66 use nostr_sdk::prelude::{Alphabet, SingleLetterTag}; 104 use nostr_sdk::prelude::{Alphabet, SingleLetterTag};
67 105
diff --git a/grasp-audit/src/client.rs b/grasp-audit/src/client.rs
index 7706ee3..cbefeb9 100644
--- a/grasp-audit/src/client.rs
+++ b/grasp-audit/src/client.rs
@@ -104,7 +104,38 @@ impl AuditClient {
104 Ok(event_id) 104 Ok(event_id)
105 } 105 }
106 106
107 /// Create an event builder with audit tags 107 /// Create an event builder that automatically includes audit tags
108 ///
109 /// All events built through this method will automatically have audit tags appended
110 /// when you call `.build()`. These tags provide isolation, cleanup scheduling, and
111 /// easy discovery of audit events.
112 ///
113 /// # Automatic Tags Added
114 ///
115 /// When you call `.build()` on the returned builder, these tags will be automatically added:
116 /// - `["t", "grasp-audit-test-event"]` - Identifies all audit events
117 /// - `["t", "audit-{run_id}"]` - Unique ID for this audit run
118 /// - `["t", "audit-cleanup-after-{timestamp}"]` - Cleanup scheduling
119 ///
120 /// # Example
121 ///
122 /// ```no_run
123 /// # use grasp_audit::*;
124 /// # async fn example() -> anyhow::Result<()> {
125 /// let config = AuditConfig::ci();
126 /// let client = AuditClient::new("ws://localhost:7000", config).await?;
127 ///
128 /// // Create event with automatic audit tags
129 /// let event = client.event_builder(Kind::TextNote, "test content")
130 /// .tag(Tag::custom(TagKind::custom("custom"), vec!["value"]))
131 /// .build(client.keys())?;
132 ///
133 /// // Event now has both your custom tag AND the 3 audit tags
134 /// # Ok(())
135 /// # }
136 /// ```
137 ///
138 /// See [`AuditConfig::audit_tags()`] for details on the tag format.
108 pub fn event_builder(&self, kind: Kind, content: impl Into<String>) -> AuditEventBuilder { 139 pub fn event_builder(&self, kind: Kind, content: impl Into<String>) -> AuditEventBuilder {
109 AuditEventBuilder::new(kind, content, self.config.clone()) 140 AuditEventBuilder::new(kind, content, self.config.clone())
110 } 141 }
@@ -237,4 +268,49 @@ mod tests {
237 // Builder should be created successfully 268 // Builder should be created successfully
238 // (We can't test the internal config field as it's private, which is correct) 269 // (We can't test the internal config field as it's private, which is correct)
239 } 270 }
271
272 #[test]
273 fn test_audit_tags_automatically_added() {
274 let config = AuditConfig::ci();
275 let keys = Keys::generate();
276 let client = AuditClient {
277 client: Client::new(keys.clone()),
278 config: config.clone(),
279 keys: keys.clone(),
280 };
281
282 // Create an event with a custom tag
283 let event = client.event_builder(Kind::TextNote, "test content")
284 .tag(Tag::custom(TagKind::custom("custom"), vec!["value"]))
285 .build(&keys)
286 .unwrap();
287
288 // Should have custom tag (1) + 3 audit tags = at least 4 tags
289 assert!(event.tags.len() >= 4, "Expected at least 4 tags, got {}", event.tags.len());
290
291 // Verify audit tags are present by checking tag content
292 let tag_contents: Vec<String> = event.tags.iter()
293 .filter_map(|t| t.content().map(|s| s.to_string()))
294 .collect();
295
296 // Check for the three required audit tags
297 assert!(
298 tag_contents.contains(&"grasp-audit-test-event".to_string()),
299 "Missing 'grasp-audit-test-event' tag"
300 );
301 assert!(
302 tag_contents.iter().any(|t| t.starts_with("audit-ci-")),
303 "Missing 'audit-ci-*' tag"
304 );
305 assert!(
306 tag_contents.iter().any(|t| t.starts_with("audit-cleanup-after-")),
307 "Missing 'audit-cleanup-after-*' tag"
308 );
309
310 // Verify the custom tag is also present
311 assert!(
312 tag_contents.contains(&"value".to_string()),
313 "Missing custom tag value"
314 );
315 }
240} 316}