e2e_integration_tests.rs
1 use abundantis::{ 2 config::AbundantisConfig, 3 events::{AbundantisEvent, EventSubscriber}, 4 source::{EnvSource, MemorySource, SourceId}, 5 CacheKey, ResolutionCache, 6 }; 7 use compact_str::CompactString; 8 use std::path::PathBuf; 9 use std::sync::atomic::{AtomicU32, Ordering}; 10 use std::sync::Arc; 11 12 #[test] 13 fn test_basic_memory_source() { 14 let source = MemorySource::new(); 15 source.set("TEST_VAR", "test_value"); 16 17 let snapshot = source.load().unwrap(); 18 assert_eq!(snapshot.variables.len(), 1); 19 assert_eq!(snapshot.variables[0].key.as_str(), "TEST_VAR"); 20 assert_eq!(snapshot.variables[0].raw_value.as_str(), "test_value"); 21 } 22 23 #[test] 24 fn test_memory_source_with_description() { 25 let source = MemorySource::new(); 26 source.set_with_description("API_KEY", "secret123", "Production API key"); 27 28 let snapshot = source.load().unwrap(); 29 assert_eq!( 30 snapshot.variables[0].description.as_deref(), 31 Some("Production API key") 32 ); 33 } 34 35 #[test] 36 fn test_memory_source_crud() { 37 let source = MemorySource::new(); 38 39 source.set("KEY1", "value1"); 40 source.set("KEY2", "value2"); 41 assert_eq!(source.len(), 2); 42 43 let removed = source.remove("KEY1"); 44 assert!(removed.is_some()); 45 assert_eq!(source.len(), 1); 46 47 source.clear(); 48 assert_eq!(source.len(), 0); 49 assert!(source.is_empty()); 50 } 51 52 #[test] 53 fn test_resolution_cache() { 54 let config = abundantis::config::CacheConfig { 55 enabled: true, 56 hot_cache_size: 10, 57 ttl: std::time::Duration::from_secs(60), 58 }; 59 60 let cache = ResolutionCache::new(&config); 61 assert!(cache.is_empty()); 62 63 let key = CacheKey::new("TEST", 123); 64 65 let var = Arc::new(abundantis::ResolvedVariable { 66 key: CompactString::new("TEST"), 67 raw_value: CompactString::new("value"), 68 resolved_value: CompactString::new("value"), 69 source: abundantis::source::VariableSource::Memory, 70 description: None, 71 has_warnings: false, 72 interpolation_depth: 0, 73 }); 74 75 cache.insert(key.clone(), var.clone()); 76 assert_eq!(cache.len(), 2); 77 78 let retrieved = cache.get(&key).unwrap(); 79 assert_eq!(retrieved.key.as_str(), "TEST"); 80 81 cache.invalidate(&key); 82 assert!(cache.get(&key).is_none()); 83 } 84 85 struct TestEventCounter { 86 count: Arc<AtomicU32>, 87 } 88 89 impl TestEventCounter { 90 fn new() -> Self { 91 Self { 92 count: Arc::new(AtomicU32::new(0)), 93 } 94 } 95 96 #[allow(dead_code)] 97 fn get_count(&self) -> u32 { 98 self.count.load(Ordering::SeqCst) 99 } 100 } 101 102 impl EventSubscriber for TestEventCounter { 103 fn on_event(&self, _event: &AbundantisEvent) { 104 self.count.fetch_add(1, Ordering::SeqCst); 105 } 106 } 107 108 #[test] 109 fn test_event_bus() { 110 use abundantis::events::EventBus; 111 112 let bus = EventBus::new(100); 113 let counter = TestEventCounter::new(); 114 115 bus.subscribe(Arc::new(counter)); 116 117 bus.publish(AbundantisEvent::CacheInvalidated { scope: None }); 118 } 119 120 #[test] 121 fn test_config_defaults() { 122 let config = AbundantisConfig::default(); 123 124 assert!(config.workspace.provider.is_none()); 125 assert!(!config.workspace.cascading); 126 assert!(config.resolution.type_check); 127 assert!(config.interpolation.enabled); 128 assert_eq!(config.interpolation.max_depth, 64); 129 assert!(config.cache.enabled); 130 } 131 132 #[test] 133 fn test_source_id() { 134 let id = SourceId::new("test-source"); 135 assert_eq!(id.as_str(), "test-source"); 136 137 let id2: SourceId = "another-source".into(); 138 assert_eq!(id2.as_str(), "another-source"); 139 140 let id3: SourceId = String::from("string-source").into(); 141 assert_eq!(id3.as_str(), "string-source"); 142 } 143 144 #[test] 145 fn test_priority_constants() { 146 use abundantis::source::Priority; 147 148 assert!(Priority::SHELL > Priority::FILE); 149 assert!(Priority::FILE > Priority::MEMORY); 150 assert_eq!(Priority::REMOTE.0, 75); 151 } 152 153 #[test] 154 fn test_source_capabilities() { 155 use abundantis::source::SourceCapabilities; 156 157 let capabilities = 158 SourceCapabilities::READ | SourceCapabilities::WRITE | SourceCapabilities::CACHEABLE; 159 160 assert!(capabilities.contains(SourceCapabilities::READ)); 161 assert!(capabilities.contains(SourceCapabilities::WRITE)); 162 assert!(capabilities.contains(SourceCapabilities::CACHEABLE)); 163 assert!(!capabilities.contains(SourceCapabilities::WATCH)); 164 } 165 166 #[test] 167 fn test_variable_source() { 168 use abundantis::source::VariableSource; 169 170 let file_source = VariableSource::File { 171 path: PathBuf::from("/path/to/.env"), 172 offset: 42, 173 }; 174 175 assert_eq!( 176 file_source.file_path(), 177 Some(&PathBuf::from("/path/to/.env")) 178 ); 179 } 180 181 #[test] 182 fn test_workspace_context() { 183 let context = abundantis::workspace::WorkspaceContext { 184 workspace_root: PathBuf::from("/workspace"), 185 package_root: PathBuf::from("/workspace/package"), 186 package_name: Some(CompactString::new("my-package")), 187 env_files: vec![PathBuf::from("/workspace/package/.env")], 188 }; 189 190 assert_eq!(context.workspace_root, PathBuf::from("/workspace")); 191 assert_eq!(context.package_name.as_deref(), Some("my-package")); 192 } 193 194 #[test] 195 fn test_package_info() { 196 let info = abundantis::workspace::PackageInfo { 197 root: PathBuf::from("/workspace/package"), 198 name: Some(CompactString::new("my-package")), 199 relative_path: CompactString::new("package"), 200 }; 201 202 assert_eq!(info.name.as_deref(), Some("my-package")); 203 assert_eq!(info.relative_path.as_str(), "package"); 204 } 205 206 #[test] 207 fn test_error_types() { 208 use abundantis::error::{AbundantisError, SourceError}; 209 210 let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "test"); 211 let abundantis_error: AbundantisError = io_error.into(); 212 assert!(matches!(abundantis_error, AbundantisError::Io(_))); 213 214 let source_error = SourceError::SourceRead { 215 source_name: "test".into(), 216 reason: "failed".into(), 217 }; 218 let abundantis_error: AbundantisError = source_error.into(); 219 assert!(matches!(abundantis_error, AbundantisError::Source(_))); 220 } 221 222 #[test] 223 fn test_diagnostic() { 224 use abundantis::error::{Diagnostic, DiagnosticCode, DiagnosticSeverity}; 225 226 let diagnostic = Diagnostic { 227 severity: DiagnosticSeverity::Error, 228 code: DiagnosticCode::EDF001, 229 message: "Test error".to_string(), 230 path: PathBuf::from("/test.env"), 231 line: 10, 232 column: 5, 233 }; 234 235 assert_eq!(diagnostic.severity, DiagnosticSeverity::Error); 236 assert_eq!(diagnostic.line, 10); 237 } 238 239 #[test] 240 fn test_resolution_engine_basic() { 241 let resolution_config = abundantis::config::ResolutionConfig::default(); 242 let interpolation_config = abundantis::config::InterpolationConfig::default(); 243 let cache_config = abundantis::config::CacheConfig::default(); 244 245 let engine = 246 abundantis::ResolutionEngine::new(&resolution_config, &interpolation_config, &cache_config); 247 248 assert!(engine.cache().is_empty()); 249 } 250 251 #[test] 252 fn test_interpolation_config() { 253 let config = abundantis::config::InterpolationConfig::default(); 254 255 assert!(config.enabled); 256 assert_eq!(config.max_depth, 64); 257 assert!(config.features.defaults); 258 assert!(config.features.alternates); 259 assert!(config.features.recursion); 260 assert!(!config.features.commands); 261 } 262 263 #[test] 264 fn test_cache_config() { 265 let config = abundantis::config::CacheConfig::default(); 266 267 assert!(config.enabled); 268 assert_eq!(config.hot_cache_size, 1000); 269 assert_eq!(config.ttl, std::time::Duration::from_secs(300)); 270 } 271 272 #[test] 273 fn test_file_resolution_config() { 274 let config = abundantis::config::FileResolutionConfig::default(); 275 276 assert_eq!(config.mode, abundantis::config::FileMergeMode::Merge); 277 assert_eq!(config.order.len(), 2); 278 assert_eq!(config.order[0].as_str(), ".env"); 279 assert_eq!(config.order[1].as_str(), ".env.local"); 280 } 281 282 #[test] 283 fn test_abundantis_stats() { 284 let stats = abundantis::AbundantisStats { 285 cached_variables: 100, 286 source_count: 5, 287 }; 288 289 assert_eq!(stats.cached_variables, 100); 290 assert_eq!(stats.source_count, 5); 291 } 292 293 #[test] 294 fn test_compact_string_optimization() { 295 let short = CompactString::new("short"); 296 let long = 297 CompactString::new("this is a much longer string that won't fit in the inline buffer"); 298 299 assert_eq!(short.as_str(), "short"); 300 assert_eq!( 301 long.as_str(), 302 "this is a much longer string that won't fit in the inline buffer" 303 ); 304 }