diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-12-02 21:20:17 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-12-02 21:20:17 +0000 |
| commit | 72683beea066d066637e747c40dc859fb709babf (patch) | |
| tree | 2db3462ebbcb7501e56491148cc3ffa7aa294071 /grasp-audit/src/audit.rs | |
| parent | 5c10ca008413744b09136618eaa85275c997704c (diff) | |
refactor: rename AuditMode variants and change CLI default to shared
Breaking change: Renamed AuditMode enum variants for clarity:
- AuditMode::CI -> AuditMode::Isolated (fresh fixtures per test)
- AuditMode::Production -> AuditMode::Shared (reuse fixtures across tests)
Config constructors renamed (with deprecated aliases):
- AuditConfig::ci() -> AuditConfig::isolated()
- AuditConfig::production() -> AuditConfig::shared()
CLI default changed from 'ci' to 'shared' mode, which enables
fixture caching across tests. This fixes the issue where fixtures
were being re-created for every test in CLI mode.
Fixture caching behavior:
- Shared mode (CLI default): Uses client's cache, fixtures reused
- Isolated mode (for cargo test): Local cache per TestContext
Diffstat (limited to 'grasp-audit/src/audit.rs')
| -rw-r--r-- | grasp-audit/src/audit.rs | 100 |
1 files changed, 64 insertions, 36 deletions
diff --git a/grasp-audit/src/audit.rs b/grasp-audit/src/audit.rs index b97ddb6..713c948 100644 --- a/grasp-audit/src/audit.rs +++ b/grasp-audit/src/audit.rs | |||
| @@ -18,46 +18,75 @@ pub struct AuditConfig { | |||
| 18 | pub read_only: bool, | 18 | pub read_only: bool, |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | /// Audit mode | 21 | /// Audit mode for fixture management |
| 22 | /// | ||
| 23 | /// Controls how test fixtures are cached and shared between tests. | ||
| 22 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 24 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 23 | pub enum AuditMode { | 25 | pub enum AuditMode { |
| 24 | /// Isolated CI/CD tests - only see own events | 26 | /// Isolated mode - each test creates fresh fixtures |
| 25 | CI, | 27 | /// |
| 28 | /// Use this mode when running tests in parallel (e.g., `cargo test`) | ||
| 29 | /// where each test needs complete isolation from other tests. | ||
| 30 | /// Each TestContext gets its own local cache. | ||
| 31 | Isolated, | ||
| 26 | 32 | ||
| 27 | /// Production audit - see all events, minimal writes | 33 | /// Shared mode - fixtures are cached and reused across tests |
| 28 | Production, | 34 | /// |
| 35 | /// Use this mode when running the CLI audit tool where tests run | ||
| 36 | /// sequentially and build on each other's fixtures. This is more | ||
| 37 | /// efficient as it avoids re-creating the same prerequisite events. | ||
| 38 | /// All TestContexts share the client's cache. | ||
| 39 | Shared, | ||
| 29 | } | 40 | } |
| 30 | 41 | ||
| 31 | impl AuditConfig { | 42 | impl AuditConfig { |
| 32 | /// Create config for CI/CD testing | 43 | /// Create config for isolated testing (e.g., cargo test) |
| 33 | pub fn ci() -> Self { | 44 | /// |
| 34 | let run_id = format!("ci-{}", &uuid::Uuid::new_v4().to_string()[..8]); | 45 | /// Each test creates fresh fixtures for complete test isolation. |
| 46 | /// Use this when running tests in parallel. | ||
| 47 | pub fn isolated() -> Self { | ||
| 48 | let run_id = format!("isolated-{}", &uuid::Uuid::new_v4().to_string()[..8]); | ||
| 35 | Self { | 49 | Self { |
| 36 | run_id, | 50 | run_id, |
| 37 | mode: AuditMode::CI, | 51 | mode: AuditMode::Isolated, |
| 38 | cleanup_after: Timestamp::now() + 3600, // 1 hour from now | 52 | cleanup_after: Timestamp::now() + 3600, // 1 hour from now |
| 39 | read_only: false, | 53 | read_only: false, |
| 40 | } | 54 | } |
| 41 | } | 55 | } |
| 42 | 56 | ||
| 43 | /// Create config for production audit | 57 | /// Create config for shared fixture mode (default for CLI) |
| 44 | pub fn production() -> Self { | 58 | /// |
| 45 | let run_id = format!("prod-audit-{}", Timestamp::now().as_u64()); | 59 | /// Fixtures are cached and reused across tests. Use this when |
| 60 | /// running the CLI audit tool where tests run sequentially. | ||
| 61 | pub fn shared() -> Self { | ||
| 62 | let run_id = format!("audit-{}", &uuid::Uuid::new_v4().to_string()[..8]); | ||
| 46 | Self { | 63 | Self { |
| 47 | run_id, | 64 | run_id, |
| 48 | mode: AuditMode::Production, | 65 | mode: AuditMode::Shared, |
| 49 | cleanup_after: Timestamp::now() + 300, // 5 minutes from now | 66 | cleanup_after: Timestamp::now() + 3600, // 1 hour from now |
| 50 | read_only: true, // Default to read-only for production | 67 | read_only: false, |
| 51 | } | 68 | } |
| 52 | } | 69 | } |
| 53 | 70 | ||
| 71 | /// Alias for isolated() - for backwards compatibility | ||
| 72 | #[deprecated(since = "0.2.0", note = "Use isolated() instead")] | ||
| 73 | pub fn ci() -> Self { | ||
| 74 | Self::isolated() | ||
| 75 | } | ||
| 76 | |||
| 77 | /// Alias for shared() - for backwards compatibility | ||
| 78 | #[deprecated(since = "0.2.0", note = "Use shared() instead")] | ||
| 79 | pub fn production() -> Self { | ||
| 80 | Self::shared() | ||
| 81 | } | ||
| 82 | |||
| 54 | /// Create config with custom run ID | 83 | /// Create config with custom run ID |
| 55 | pub fn with_run_id(run_id: String, mode: AuditMode) -> Self { | 84 | pub fn with_run_id(run_id: String, mode: AuditMode) -> Self { |
| 56 | Self { | 85 | Self { |
| 57 | run_id, | 86 | run_id, |
| 58 | mode, | 87 | mode, |
| 59 | cleanup_after: Timestamp::now() + 3600, | 88 | cleanup_after: Timestamp::now() + 3600, |
| 60 | read_only: mode == AuditMode::Production, | 89 | read_only: false, |
| 61 | } | 90 | } |
| 62 | } | 91 | } |
| 63 | 92 | ||
| @@ -72,11 +101,10 @@ impl AuditConfig { | |||
| 72 | /// | 101 | /// |
| 73 | /// 1. `["t", "grasp-audit-test-event"]` - Identifies all audit-related events | 102 | /// 1. `["t", "grasp-audit-test-event"]` - Identifies all audit-related events |
| 74 | /// 2. `["t", "audit-{run_id}"]` - Unique identifier for this audit run | 103 | /// 2. `["t", "audit-{run_id}"]` - Unique identifier for this audit run |
| 75 | /// - CI mode: `audit-ci-{uuid}` | 104 | /// - Isolated mode: `audit-isolated-{uuid}` |
| 76 | /// - Production mode: `audit-prod-audit-{timestamp}` | 105 | /// - Shared mode: `audit-audit-{uuid}` |
| 77 | /// 3. `["t", "audit-cleanup-after-{unix_timestamp}"]` - Cleanup timestamp | 106 | /// 3. `["t", "audit-cleanup-after-{unix_timestamp}"]` - Cleanup timestamp |
| 78 | /// - CI mode: Current time + 3600 seconds (1 hour) | 107 | /// - Default: Current time + 3600 seconds (1 hour) |
| 79 | /// - Production mode: Current time + 300 seconds (5 minutes) | ||
| 80 | /// | 108 | /// |
| 81 | /// # Purpose | 109 | /// # Purpose |
| 82 | /// | 110 | /// |
| @@ -90,7 +118,7 @@ impl AuditConfig { | |||
| 90 | /// ```rust | 118 | /// ```rust |
| 91 | /// use grasp_audit::AuditConfig; | 119 | /// use grasp_audit::AuditConfig; |
| 92 | /// | 120 | /// |
| 93 | /// let config = AuditConfig::ci(); | 121 | /// let config = AuditConfig::isolated(); |
| 94 | /// let tags = config.audit_tags(); | 122 | /// let tags = config.audit_tags(); |
| 95 | /// | 123 | /// |
| 96 | /// // Tags will look like: | 124 | /// // Tags will look like: |
| @@ -168,7 +196,7 @@ impl AuditEventBuilder { | |||
| 168 | /// use nostr_sdk::prelude::*; | 196 | /// use nostr_sdk::prelude::*; |
| 169 | /// use grasp_audit::{AuditConfig, AuditEventBuilder}; | 197 | /// use grasp_audit::{AuditConfig, AuditEventBuilder}; |
| 170 | /// | 198 | /// |
| 171 | /// let config = AuditConfig::ci(); | 199 | /// let config = AuditConfig::isolated(); |
| 172 | /// let keys = Keys::generate(); | 200 | /// let keys = Keys::generate(); |
| 173 | /// | 201 | /// |
| 174 | /// // Create an event with a past timestamp | 202 | /// // Create an event with a past timestamp |
| @@ -209,26 +237,26 @@ mod tests { | |||
| 209 | use super::*; | 237 | use super::*; |
| 210 | 238 | ||
| 211 | #[test] | 239 | #[test] |
| 212 | fn test_ci_config() { | 240 | fn test_isolated_config() { |
| 213 | let config = AuditConfig::ci(); | 241 | let config = AuditConfig::isolated(); |
| 214 | assert_eq!(config.mode, AuditMode::CI); | 242 | assert_eq!(config.mode, AuditMode::Isolated); |
| 215 | assert!(!config.read_only); | 243 | assert!(!config.read_only); |
| 216 | assert!(config.run_id.starts_with("ci-")); | 244 | assert!(config.run_id.starts_with("isolated-")); |
| 217 | } | 245 | } |
| 218 | 246 | ||
| 219 | #[test] | 247 | #[test] |
| 220 | fn test_production_config() { | 248 | fn test_shared_config() { |
| 221 | let config = AuditConfig::production(); | 249 | let config = AuditConfig::shared(); |
| 222 | assert_eq!(config.mode, AuditMode::Production); | 250 | assert_eq!(config.mode, AuditMode::Shared); |
| 223 | assert!(config.read_only); | 251 | assert!(!config.read_only); |
| 224 | assert!(config.run_id.starts_with("prod-audit-")); | 252 | assert!(config.run_id.starts_with("audit-")); |
| 225 | } | 253 | } |
| 226 | 254 | ||
| 227 | #[test] | 255 | #[test] |
| 228 | fn test_audit_tags() { | 256 | fn test_audit_tags() { |
| 229 | use nostr_sdk::prelude::{Alphabet, SingleLetterTag}; | 257 | use nostr_sdk::prelude::{Alphabet, SingleLetterTag}; |
| 230 | 258 | ||
| 231 | let config = AuditConfig::ci(); | 259 | let config = AuditConfig::isolated(); |
| 232 | let tags = config.audit_tags(); | 260 | let tags = config.audit_tags(); |
| 233 | 261 | ||
| 234 | assert_eq!(tags.len(), 3); | 262 | assert_eq!(tags.len(), 3); |
| @@ -252,7 +280,7 @@ mod tests { | |||
| 252 | // Check for "t" tag with "audit-{run_id}" | 280 | // Check for "t" tag with "audit-{run_id}" |
| 253 | assert!(tags.iter().any(|t| { | 281 | assert!(tags.iter().any(|t| { |
| 254 | t.content() | 282 | t.content() |
| 255 | .map(|c| c.starts_with("audit-ci-")) | 283 | .map(|c| c.starts_with("audit-isolated-")) |
| 256 | .unwrap_or(false) | 284 | .unwrap_or(false) |
| 257 | })); | 285 | })); |
| 258 | 286 | ||
| @@ -266,7 +294,7 @@ mod tests { | |||
| 266 | 294 | ||
| 267 | #[test] | 295 | #[test] |
| 268 | fn test_audit_event_builder() { | 296 | fn test_audit_event_builder() { |
| 269 | let config = AuditConfig::ci(); | 297 | let config = AuditConfig::isolated(); |
| 270 | let keys = Keys::generate(); | 298 | let keys = Keys::generate(); |
| 271 | 299 | ||
| 272 | let event = AuditEventBuilder::new(Kind::TextNote, "test", config.clone()) | 300 | let event = AuditEventBuilder::new(Kind::TextNote, "test", config.clone()) |
| @@ -283,7 +311,7 @@ mod tests { | |||
| 283 | 311 | ||
| 284 | #[test] | 312 | #[test] |
| 285 | fn test_custom_timestamp_applied() { | 313 | fn test_custom_timestamp_applied() { |
| 286 | let config = AuditConfig::ci(); | 314 | let config = AuditConfig::isolated(); |
| 287 | let keys = Keys::generate(); | 315 | let keys = Keys::generate(); |
| 288 | let custom_ts = Timestamp::from(1700000000); | 316 | let custom_ts = Timestamp::from(1700000000); |
| 289 | 317 | ||
| @@ -302,7 +330,7 @@ mod tests { | |||
| 302 | 330 | ||
| 303 | #[test] | 331 | #[test] |
| 304 | fn test_default_timestamp_uses_current_time() { | 332 | fn test_default_timestamp_uses_current_time() { |
| 305 | let config = AuditConfig::ci(); | 333 | let config = AuditConfig::isolated(); |
| 306 | let keys = Keys::generate(); | 334 | let keys = Keys::generate(); |
| 307 | 335 | ||
| 308 | let before = Timestamp::now(); | 336 | let before = Timestamp::now(); |