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 }