/ tests / e2e_integration_tests.rs
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  }