/ ferris-proof-core / tests / cache_integration.rs
cache_integration.rs
  1  use ferris_proof_core::cache::{
  2      CacheKey, ConfigHash, ContentHash, ToolVersions, VerificationCache,
  3  };
  4  use ferris_proof_core::types::*;
  5  use std::time::Duration;
  6  use tempfile::TempDir;
  7  
  8  #[test]
  9  fn test_cache_basic_operations() {
 10      let temp_dir = TempDir::new().unwrap();
 11      let cache_dir = temp_dir.path().join("cache");
 12  
 13      let mut cache = VerificationCache::with_cache_dir(cache_dir);
 14  
 15      // Create test cache key
 16      let cache_key = CacheKey {
 17          content_hash: ContentHash("test_hash".to_string()),
 18          config_hash: ConfigHash("config_hash".to_string()),
 19          tool_versions: ToolVersions {
 20              ferris_proof: "0.1.0".to_string(),
 21              external_tools: vec![],
 22          },
 23          layer: Layer::PropertyBased,
 24      };
 25  
 26      // Create test cache entry
 27      let cache_entry = ferris_proof_core::cache::CacheEntry {
 28          result: LayerResult {
 29              layer: Layer::PropertyBased,
 30              status: Status::Success,
 31              violations: vec![],
 32              execution_time: Duration::from_millis(100),
 33              tool_outputs: vec![],
 34          },
 35          timestamp: chrono::Utc::now(),
 36          ttl: Duration::from_secs(3600),
 37          metadata: ferris_proof_core::cache::CacheMetadata {
 38              file_size: 1024,
 39              execution_time: Duration::from_millis(100),
 40              memory_usage: 512 * 1024 * 1024, // 512MB
 41              cache_hit_count: 0,
 42          },
 43      };
 44  
 45      // Test store operation
 46      cache.store(cache_key.clone(), cache_entry.clone());
 47  
 48      // Test get operation
 49      let retrieved_entry = cache.get(&cache_key);
 50      assert!(retrieved_entry.is_some());
 51  
 52      let retrieved = retrieved_entry.unwrap();
 53      assert_eq!(retrieved.result.status, Status::Success);
 54      assert_eq!(retrieved.result.layer, Layer::PropertyBased);
 55  }
 56  
 57  #[test]
 58  fn test_cache_expiration() {
 59      let temp_dir = TempDir::new().unwrap();
 60      let cache_dir = temp_dir.path().join("cache");
 61  
 62      let mut cache = VerificationCache::with_cache_dir(cache_dir);
 63  
 64      let cache_key = CacheKey {
 65          content_hash: ContentHash("test_hash".to_string()),
 66          config_hash: ConfigHash("config_hash".to_string()),
 67          tool_versions: ToolVersions {
 68              ferris_proof: "0.1.0".to_string(),
 69              external_tools: vec![],
 70          },
 71          layer: Layer::PropertyBased,
 72      };
 73  
 74      // Create cache entry with very short TTL
 75      let cache_entry = ferris_proof_core::cache::CacheEntry {
 76          result: LayerResult {
 77              layer: Layer::PropertyBased,
 78              status: Status::Success,
 79              violations: vec![],
 80              execution_time: Duration::from_millis(100),
 81              tool_outputs: vec![],
 82          },
 83          timestamp: chrono::Utc::now() - chrono::Duration::seconds(10), // 10 seconds ago
 84          ttl: Duration::from_secs(5),                                   // 5 second TTL (expired)
 85          metadata: ferris_proof_core::cache::CacheMetadata {
 86              file_size: 1024,
 87              execution_time: Duration::from_millis(100),
 88              memory_usage: 512 * 1024 * 1024,
 89              cache_hit_count: 0,
 90          },
 91      };
 92  
 93      cache.store(cache_key.clone(), cache_entry);
 94  
 95      // Should not retrieve expired entry
 96      let retrieved_entry = cache.get(&cache_key);
 97      assert!(retrieved_entry.is_none());
 98  }
 99  
100  #[test]
101  fn test_cache_persistence() {
102      let temp_dir = TempDir::new().unwrap();
103      let cache_dir = temp_dir.path().join("cache");
104  
105      {
106          let mut cache1 = VerificationCache::with_cache_dir(cache_dir.clone());
107  
108          let cache_key = CacheKey {
109              content_hash: ContentHash("persistent_test".to_string()),
110              config_hash: ConfigHash("config_hash".to_string()),
111              tool_versions: ToolVersions {
112                  ferris_proof: "0.1.0".to_string(),
113                  external_tools: vec![],
114              },
115              layer: Layer::PropertyBased,
116          };
117  
118          let cache_entry = ferris_proof_core::cache::CacheEntry {
119              result: LayerResult {
120                  layer: Layer::PropertyBased,
121                  status: Status::Success,
122                  violations: vec![],
123                  execution_time: Duration::from_millis(100),
124                  tool_outputs: vec![],
125              },
126              timestamp: chrono::Utc::now(),
127              ttl: Duration::from_secs(3600),
128              metadata: ferris_proof_core::cache::CacheMetadata {
129                  file_size: 2048,
130                  execution_time: Duration::from_millis(200),
131                  memory_usage: 1024 * 1024 * 1024, // 1GB
132                  cache_hit_count: 0,
133              },
134          };
135  
136          cache1.store(cache_key.clone(), cache_entry);
137  
138          // Save to disk
139          cache1.save_to_disk().unwrap();
140      }
141  
142      // Create new cache instance and load from disk
143      {
144          let mut cache2 = VerificationCache::with_cache_dir(cache_dir);
145          cache2.load_from_disk().unwrap();
146  
147          let cache_key = CacheKey {
148              content_hash: ContentHash("persistent_test".to_string()),
149              config_hash: ConfigHash("config_hash".to_string()),
150              tool_versions: ToolVersions {
151                  ferris_proof: "0.1.0".to_string(),
152                  external_tools: vec![],
153              },
154              layer: Layer::PropertyBased,
155          };
156  
157          let retrieved_entry = cache2.get(&cache_key);
158          assert!(retrieved_entry.is_some());
159  
160          let retrieved = retrieved_entry.unwrap();
161          assert_eq!(retrieved.result.status, Status::Success);
162          assert_eq!(retrieved.metadata.file_size, 2048);
163      }
164  }
165  
166  #[test]
167  fn test_cache_cleanup() {
168      let temp_dir = TempDir::new().unwrap();
169      let cache_dir = temp_dir.path().join("cache");
170  
171      let mut cache = VerificationCache::with_cache_dir(cache_dir);
172  
173      // Add multiple cache entries
174      for i in 0..5 {
175          let cache_key = CacheKey {
176              content_hash: ContentHash(format!("test_hash_{}", i)),
177              config_hash: ConfigHash("config_hash".to_string()),
178              tool_versions: ToolVersions {
179                  ferris_proof: "0.1.0".to_string(),
180                  external_tools: vec![],
181              },
182              layer: Layer::PropertyBased,
183          };
184  
185          let cache_entry = ferris_proof_core::cache::CacheEntry {
186              result: LayerResult {
187                  layer: Layer::PropertyBased,
188                  status: Status::Success,
189                  violations: vec![],
190                  execution_time: Duration::from_millis(100),
191                  tool_outputs: vec![],
192              },
193              timestamp: chrono::Utc::now() - chrono::Duration::seconds(i * 10), // Varying ages
194              ttl: Duration::from_secs((5 + i * 5) as u64), // Some expired, some not
195              metadata: ferris_proof_core::cache::CacheMetadata {
196                  file_size: 1024,
197                  execution_time: Duration::from_millis(100),
198                  memory_usage: 512 * 1024 * 1024,
199                  cache_hit_count: 0,
200              },
201          };
202  
203          cache.store(cache_key, cache_entry);
204      }
205  
206      // Cleanup expired entries
207      let expired_count = cache.cleanup_expired().unwrap();
208  
209      // Should have cleaned up some entries
210      assert!(expired_count > 0);
211  }
212  
213  #[test]
214  fn test_cache_statistics() {
215      let temp_dir = TempDir::new().unwrap();
216      let cache_dir = temp_dir.path().join("cache");
217  
218      let mut cache = VerificationCache::with_cache_dir(cache_dir);
219  
220      // Add some test entries
221      for i in 0..3 {
222          let cache_key = CacheKey {
223              content_hash: ContentHash(format!("stats_test_{}", i)),
224              config_hash: ConfigHash("config_hash".to_string()),
225              tool_versions: ToolVersions {
226                  ferris_proof: "0.1.0".to_string(),
227                  external_tools: vec![],
228              },
229              layer: Layer::PropertyBased,
230          };
231  
232          let cache_entry = ferris_proof_core::cache::CacheEntry {
233              result: LayerResult {
234                  layer: Layer::PropertyBased,
235                  status: Status::Success,
236                  violations: vec![],
237                  execution_time: Duration::from_millis(100),
238                  tool_outputs: vec![],
239              },
240              timestamp: chrono::Utc::now(),
241              ttl: Duration::from_secs(3600),
242              metadata: ferris_proof_core::cache::CacheMetadata {
243                  file_size: 1024 * (i + 1),
244                  execution_time: Duration::from_millis(100 * (i + 1)),
245                  memory_usage: 512 * 1024 * 1024 * (i + 1),
246                  cache_hit_count: 0,
247              },
248          };
249  
250          cache.store(cache_key, cache_entry);
251      }
252  
253      // Get statistics
254      let stats = cache.statistics();
255  
256      assert_eq!(stats.total_entries, 3);
257      assert_eq!(stats.expired_entries, 0); // All should be valid
258      assert_eq!(stats.valid_entries, 3);
259      assert_eq!(stats.total_size_bytes, 1024 + 2048 + 3072); // Sum of file sizes
260  }
261  
262  #[test]
263  fn test_cache_hit_rate() {
264      let cache = VerificationCache::new();
265  
266      // Test hit rate calculation
267      assert_eq!(cache.hit_rate(80, 20), 0.8);
268      assert_eq!(cache.hit_rate(0, 100), 0.0);
269      assert_eq!(cache.hit_rate(100, 0), 1.0);
270      assert_eq!(cache.hit_rate(0, 0), 0.0);
271  }
272  
273  #[test]
274  fn test_cache_key_hash() {
275      // Test that cache keys with different content produce different hashes
276      let key1 = CacheKey {
277          content_hash: ContentHash("hash1".to_string()),
278          config_hash: ConfigHash("config_hash".to_string()),
279          tool_versions: ToolVersions {
280              ferris_proof: "0.1.0".to_string(),
281              external_tools: vec![],
282          },
283          layer: Layer::PropertyBased,
284      };
285  
286      let key2 = CacheKey {
287          content_hash: ContentHash("hash2".to_string()),
288          config_hash: ConfigHash("config_hash".to_string()),
289          tool_versions: ToolVersions {
290              ferris_proof: "0.1.0".to_string(),
291              external_tools: vec![],
292          },
293          layer: Layer::PropertyBased,
294      };
295  
296      // Different content hashes should produce different keys
297      assert_ne!(key1, key2);
298  
299      use std::hash::{Hash, Hasher};
300      let mut hasher1 = std::collections::hash_map::DefaultHasher::new();
301      let mut hasher2 = std::collections::hash_map::DefaultHasher::new();
302      key1.hash(&mut hasher1);
303      key2.hash(&mut hasher2);
304      assert_ne!(hasher1.finish(), hasher2.finish());
305  }
306  
307  #[test]
308  fn test_cache_invalidation() {
309      let temp_dir = TempDir::new().unwrap();
310      let cache_dir = temp_dir.path().join("cache");
311  
312      let mut cache = VerificationCache::with_cache_dir(cache_dir);
313  
314      let cache_key = CacheKey {
315          content_hash: ContentHash("invalidate_test".to_string()),
316          config_hash: ConfigHash("config_hash".to_string()),
317          tool_versions: ToolVersions {
318              ferris_proof: "0.1.0".to_string(),
319              external_tools: vec![],
320          },
321          layer: Layer::PropertyBased,
322      };
323  
324      let cache_entry = ferris_proof_core::cache::CacheEntry {
325          result: LayerResult {
326              layer: Layer::PropertyBased,
327              status: Status::Success,
328              violations: vec![],
329              execution_time: Duration::from_millis(100),
330              tool_outputs: vec![],
331          },
332          timestamp: chrono::Utc::now(),
333          ttl: Duration::from_secs(3600),
334          metadata: ferris_proof_core::cache::CacheMetadata {
335              file_size: 1024,
336              execution_time: Duration::from_millis(100),
337              memory_usage: 512 * 1024 * 1024,
338              cache_hit_count: 0,
339          },
340      };
341  
342      // Store and verify
343      cache.store(cache_key.clone(), cache_entry);
344      assert!(cache.get(&cache_key).is_some());
345  
346      // Invalidate and verify removal
347      cache.invalidate(&cache_key);
348      assert!(cache.get(&cache_key).is_none());
349  }
350  
351  #[test]
352  fn test_cache_clear() {
353      let temp_dir = TempDir::new().unwrap();
354      let cache_dir = temp_dir.path().join("cache");
355  
356      let mut cache = VerificationCache::with_cache_dir(cache_dir);
357  
358      // Add some entries
359      for i in 0..5 {
360          let cache_key = CacheKey {
361              content_hash: ContentHash(format!("clear_test_{}", i)),
362              config_hash: ConfigHash("config_hash".to_string()),
363              tool_versions: ToolVersions {
364                  ferris_proof: "0.1.0".to_string(),
365                  external_tools: vec![],
366              },
367              layer: Layer::PropertyBased,
368          };
369  
370          let cache_entry = ferris_proof_core::cache::CacheEntry {
371              result: LayerResult {
372                  layer: Layer::PropertyBased,
373                  status: Status::Success,
374                  violations: vec![],
375                  execution_time: Duration::from_millis(100),
376                  tool_outputs: vec![],
377              },
378              timestamp: chrono::Utc::now(),
379              ttl: Duration::from_secs(3600),
380              metadata: ferris_proof_core::cache::CacheMetadata {
381                  file_size: 1024,
382                  execution_time: Duration::from_millis(100),
383                  memory_usage: 512 * 1024 * 1024,
384                  cache_hit_count: 0,
385              },
386          };
387  
388          cache.store(cache_key, cache_entry);
389      }
390  
391      // Verify entries exist
392      let stats_before = cache.statistics();
393      assert_eq!(stats_before.total_entries, 5);
394  
395      // Clear all
396      cache.clear();
397  
398      // Verify all entries are gone
399      let stats_after = cache.statistics();
400      assert_eq!(stats_after.total_entries, 0);
401  }