1#![allow(clippy::unwrap_used, clippy::disallowed_methods)]
2use std::collections::HashMap;
12use std::hash::Hash;
13use std::sync::Arc;
14use std::time::Duration;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub struct CacheKey(u64);
19
20impl CacheKey {
21 pub fn from_str(s: &str) -> Self {
22 let mut hash: u64 = 0;
24 for byte in s.bytes() {
25 hash = hash.wrapping_mul(31).wrapping_add(u64::from(byte));
26 }
27 Self(hash)
28 }
29
30 pub fn as_u64(self) -> u64 {
31 self.0
32 }
33}
34
35impl From<u64> for CacheKey {
36 fn from(v: u64) -> Self {
37 Self(v)
38 }
39}
40
41impl From<&str> for CacheKey {
42 fn from(s: &str) -> Self {
43 Self::from_str(s)
44 }
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub enum CacheState {
50 Fresh,
52 Stale,
54 Expired,
56}
57
58#[derive(Debug, Clone)]
60pub struct CacheMetadata {
61 pub created_at: u64,
63 pub last_accessed: u64,
65 pub ttl_ms: u64,
67 pub stale_ms: u64,
69 pub access_count: u64,
71 pub size_bytes: usize,
73 pub tags: Vec<String>,
75}
76
77impl CacheMetadata {
78 pub fn state(&self, now: u64) -> CacheState {
80 let age = now.saturating_sub(self.created_at);
81 if age <= self.ttl_ms {
82 CacheState::Fresh
83 } else if age <= self.ttl_ms + self.stale_ms {
84 CacheState::Stale
85 } else {
86 CacheState::Expired
87 }
88 }
89
90 pub fn is_expired(&self, now: u64) -> bool {
92 self.state(now) == CacheState::Expired
93 }
94}
95
96#[derive(Debug, Clone)]
98pub struct CacheConfig {
99 pub max_entries: usize,
101 pub max_memory: usize,
103 pub default_ttl_ms: u64,
105 pub default_stale_ms: u64,
107 pub enable_lru: bool,
109 pub cleanup_interval_ms: u64,
111}
112
113impl Default for CacheConfig {
114 fn default() -> Self {
115 Self {
116 max_entries: 1000,
117 max_memory: 50 * 1024 * 1024, default_ttl_ms: 5 * 60 * 1000, default_stale_ms: 60 * 1000, enable_lru: true,
121 cleanup_interval_ms: 60 * 1000, }
123 }
124}
125
126#[derive(Debug, Clone, Default)]
128pub struct CacheOptions {
129 pub ttl: Option<Duration>,
131 pub stale: Option<Duration>,
133 pub tags: Vec<String>,
135 pub priority: u8,
137}
138
139impl CacheOptions {
140 pub fn new() -> Self {
141 Self::default()
142 }
143
144 pub fn with_ttl(mut self, ttl: Duration) -> Self {
145 self.ttl = Some(ttl);
146 self
147 }
148
149 pub fn with_stale(mut self, stale: Duration) -> Self {
150 self.stale = Some(stale);
151 self
152 }
153
154 pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
155 self.tags.push(tag.into());
156 self
157 }
158
159 pub fn with_priority(mut self, priority: u8) -> Self {
160 self.priority = priority;
161 self
162 }
163}
164
165#[derive(Debug, Clone)]
167pub enum CacheEvent {
168 Added(CacheKey),
170 Hit(CacheKey),
172 Miss(CacheKey),
174 Evicted(CacheKey),
176 Invalidated(CacheKey),
178 TagInvalidated(String, usize),
180 Cleared,
182}
183
184pub type CacheCallback = Arc<dyn Fn(CacheEvent) + Send + Sync>;
186
187struct CacheEntry<V> {
189 value: V,
190 metadata: CacheMetadata,
191 #[allow(dead_code)]
192 priority: u8,
193}
194
195pub struct DataCache<K, V>
197where
198 K: Eq + Hash + Clone,
199{
200 config: CacheConfig,
201 entries: HashMap<K, CacheEntry<V>>,
202 access_order: Vec<K>,
204 current_memory: usize,
206 timestamp: u64,
208 last_cleanup: u64,
210 listeners: Vec<CacheCallback>,
212 stats: CacheStats,
214}
215
216#[derive(Debug, Clone, Default)]
218pub struct CacheStats {
219 pub hits: u64,
220 pub misses: u64,
221 pub evictions: u64,
222 pub invalidations: u64,
223 pub current_entries: usize,
224 pub current_memory: usize,
225}
226
227impl CacheStats {
228 pub fn hit_rate(&self) -> f64 {
229 let total = self.hits + self.misses;
230 if total == 0 {
231 0.0
232 } else {
233 self.hits as f64 / total as f64
234 }
235 }
236}
237
238impl<K, V> Default for DataCache<K, V>
239where
240 K: Eq + Hash + Clone,
241{
242 fn default() -> Self {
243 Self::new(CacheConfig::default())
244 }
245}
246
247impl<K, V> DataCache<K, V>
248where
249 K: Eq + Hash + Clone,
250{
251 pub fn new(config: CacheConfig) -> Self {
252 Self {
253 config,
254 entries: HashMap::new(),
255 access_order: Vec::new(),
256 current_memory: 0,
257 timestamp: 0,
258 last_cleanup: 0,
259 listeners: Vec::new(),
260 stats: CacheStats::default(),
261 }
262 }
263
264 pub fn get(&mut self, key: &K) -> Option<&V> {
266 self.maybe_cleanup();
268
269 if let Some(entry) = self.entries.get_mut(key) {
270 if entry.metadata.is_expired(self.timestamp) {
272 return None;
273 }
274
275 entry.metadata.last_accessed = self.timestamp;
277 entry.metadata.access_count += 1;
278
279 if self.config.enable_lru {
281 self.access_order.retain(|k| k != key);
282 self.access_order.push(key.clone());
283 }
284
285 self.stats.hits += 1;
286 Some(&entry.value)
287 } else {
288 self.stats.misses += 1;
289 None
290 }
291 }
292
293 pub fn get_with_state(&mut self, key: &K) -> Option<(&V, CacheState)> {
295 self.maybe_cleanup();
296
297 if let Some(entry) = self.entries.get_mut(key) {
298 let state = entry.metadata.state(self.timestamp);
299
300 if state == CacheState::Expired {
302 return None;
303 }
304
305 entry.metadata.last_accessed = self.timestamp;
306 entry.metadata.access_count += 1;
307
308 if self.config.enable_lru {
309 self.access_order.retain(|k| k != key);
310 self.access_order.push(key.clone());
311 }
312
313 self.stats.hits += 1;
314 Some((&entry.value, state))
315 } else {
316 self.stats.misses += 1;
317 None
318 }
319 }
320
321 pub fn contains(&self, key: &K) -> bool {
323 self.entries
324 .get(key)
325 .is_some_and(|e| !e.metadata.is_expired(self.timestamp))
326 }
327
328 pub fn insert(&mut self, key: K, value: V, options: CacheOptions)
330 where
331 V: CacheSize,
332 {
333 let size = value.cache_size();
334 let ttl = options.ttl.map(|d| d.as_millis() as u64);
335 let stale = options.stale.map(|d| d.as_millis() as u64);
336
337 let metadata = CacheMetadata {
338 created_at: self.timestamp,
339 last_accessed: self.timestamp,
340 ttl_ms: ttl.unwrap_or(self.config.default_ttl_ms),
341 stale_ms: stale.unwrap_or(self.config.default_stale_ms),
342 access_count: 0,
343 size_bytes: size,
344 tags: options.tags,
345 };
346
347 if let Some(old) = self.entries.remove(&key) {
349 self.current_memory = self.current_memory.saturating_sub(old.metadata.size_bytes);
350 self.access_order.retain(|k| k != &key);
351 }
352
353 while self.entries.len() >= self.config.max_entries
355 || self.current_memory + size > self.config.max_memory
356 {
357 if !self.evict_one() {
358 break;
359 }
360 }
361
362 self.current_memory += size;
363
364 let entry = CacheEntry {
365 value,
366 metadata,
367 priority: options.priority,
368 };
369
370 self.entries.insert(key.clone(), entry);
371 self.access_order.push(key);
372
373 self.stats.current_entries = self.entries.len();
374 self.stats.current_memory = self.current_memory;
375 }
376
377 pub fn insert_default(&mut self, key: K, value: V)
379 where
380 V: CacheSize,
381 {
382 self.insert(key, value, CacheOptions::default());
383 }
384
385 pub fn remove(&mut self, key: &K) -> Option<V> {
387 if let Some(entry) = self.entries.remove(key) {
388 self.current_memory = self
389 .current_memory
390 .saturating_sub(entry.metadata.size_bytes);
391 self.access_order.retain(|k| k != key);
392 self.stats.current_entries = self.entries.len();
393 self.stats.current_memory = self.current_memory;
394 self.stats.invalidations += 1;
395 Some(entry.value)
396 } else {
397 None
398 }
399 }
400
401 pub fn invalidate_tag(&mut self, tag: &str) -> usize {
403 let keys_to_remove: Vec<K> = self
404 .entries
405 .iter()
406 .filter(|(_, e)| e.metadata.tags.iter().any(|t| t == tag))
407 .map(|(k, _)| k.clone())
408 .collect();
409
410 let count = keys_to_remove.len();
411 for key in keys_to_remove {
412 self.remove(&key);
413 }
414
415 self.emit(CacheEvent::TagInvalidated(tag.to_string(), count));
416 count
417 }
418
419 pub fn clear(&mut self) {
421 self.entries.clear();
422 self.access_order.clear();
423 self.current_memory = 0;
424 self.stats.current_entries = 0;
425 self.stats.current_memory = 0;
426 self.emit(CacheEvent::Cleared);
427 }
428
429 pub fn stats(&self) -> &CacheStats {
431 &self.stats
432 }
433
434 pub fn len(&self) -> usize {
436 self.entries.len()
437 }
438
439 pub fn is_empty(&self) -> bool {
441 self.entries.is_empty()
442 }
443
444 pub fn memory_usage(&self) -> usize {
446 self.current_memory
447 }
448
449 pub fn tick(&mut self, delta_ms: u64) {
451 self.timestamp += delta_ms;
452 self.maybe_cleanup();
453 }
454
455 pub fn set_timestamp(&mut self, timestamp: u64) {
457 self.timestamp = timestamp;
458 }
459
460 pub fn timestamp(&self) -> u64 {
462 self.timestamp
463 }
464
465 pub fn on_event(&mut self, callback: CacheCallback) {
467 self.listeners.push(callback);
468 }
469
470 fn emit(&self, event: CacheEvent) {
471 for listener in &self.listeners {
472 listener(event.clone());
473 }
474 }
475
476 fn maybe_cleanup(&mut self) {
477 if self.timestamp - self.last_cleanup >= self.config.cleanup_interval_ms {
478 self.cleanup_expired();
479 self.last_cleanup = self.timestamp;
480 }
481 }
482
483 fn cleanup_expired(&mut self) {
484 let now = self.timestamp;
485 let keys_to_remove: Vec<K> = self
486 .entries
487 .iter()
488 .filter(|(_, e)| e.metadata.is_expired(now))
489 .map(|(k, _)| k.clone())
490 .collect();
491
492 for key in keys_to_remove {
493 if let Some(entry) = self.entries.remove(&key) {
494 self.current_memory = self
495 .current_memory
496 .saturating_sub(entry.metadata.size_bytes);
497 self.access_order.retain(|k| k != &key);
498 self.stats.evictions += 1;
499 }
500 }
501
502 self.stats.current_entries = self.entries.len();
503 self.stats.current_memory = self.current_memory;
504 }
505
506 fn evict_one(&mut self) -> bool {
507 if self.config.enable_lru && !self.access_order.is_empty() {
508 let key = self.access_order.remove(0);
510 if let Some(entry) = self.entries.remove(&key) {
511 self.current_memory = self
512 .current_memory
513 .saturating_sub(entry.metadata.size_bytes);
514 self.stats.evictions += 1;
515 return true;
516 }
517 } else if !self.entries.is_empty() {
518 if let Some(key) = self.entries.keys().next().cloned() {
520 if let Some(entry) = self.entries.remove(&key) {
521 self.current_memory = self
522 .current_memory
523 .saturating_sub(entry.metadata.size_bytes);
524 self.access_order.retain(|k| k != &key);
525 self.stats.evictions += 1;
526 return true;
527 }
528 }
529 }
530 false
531 }
532}
533
534pub trait CacheSize {
536 fn cache_size(&self) -> usize;
537}
538
539impl CacheSize for String {
541 fn cache_size(&self) -> usize {
542 self.len()
543 }
544}
545
546impl<T> CacheSize for Vec<T> {
547 fn cache_size(&self) -> usize {
548 self.len() * std::mem::size_of::<T>()
549 }
550}
551
552impl<T> CacheSize for Box<T> {
553 fn cache_size(&self) -> usize {
554 std::mem::size_of::<T>()
555 }
556}
557
558impl CacheSize for () {
559 fn cache_size(&self) -> usize {
560 0
561 }
562}
563
564impl CacheSize for i32 {
565 fn cache_size(&self) -> usize {
566 std::mem::size_of::<Self>()
567 }
568}
569
570impl CacheSize for i64 {
571 fn cache_size(&self) -> usize {
572 std::mem::size_of::<Self>()
573 }
574}
575
576impl CacheSize for f32 {
577 fn cache_size(&self) -> usize {
578 std::mem::size_of::<Self>()
579 }
580}
581
582impl CacheSize for f64 {
583 fn cache_size(&self) -> usize {
584 std::mem::size_of::<Self>()
585 }
586}
587
588pub type StringCache<V> = DataCache<String, V>;
590
591pub struct CacheBuilder<V> {
593 value: V,
594 options: CacheOptions,
595}
596
597impl<V> CacheBuilder<V> {
598 pub fn new(value: V) -> Self {
599 Self {
600 value,
601 options: CacheOptions::default(),
602 }
603 }
604
605 pub fn ttl(mut self, ttl: Duration) -> Self {
606 self.options.ttl = Some(ttl);
607 self
608 }
609
610 pub fn stale(mut self, stale: Duration) -> Self {
611 self.options.stale = Some(stale);
612 self
613 }
614
615 pub fn tag(mut self, tag: impl Into<String>) -> Self {
616 self.options.tags.push(tag.into());
617 self
618 }
619
620 pub fn priority(mut self, priority: u8) -> Self {
621 self.options.priority = priority;
622 self
623 }
624
625 pub fn build(self) -> (V, CacheOptions) {
626 (self.value, self.options)
627 }
628}
629
630#[cfg(test)]
631#[allow(clippy::unwrap_used)]
632mod tests {
633 use super::*;
634
635 #[test]
636 fn test_cache_key_from_str() {
637 let key1 = CacheKey::from_str("test");
638 let key2 = CacheKey::from_str("test");
639 let key3 = CacheKey::from_str("different");
640
641 assert_eq!(key1, key2);
642 assert_ne!(key1, key3);
643 }
644
645 #[test]
646 fn test_cache_key_from_u64() {
647 let key: CacheKey = 42u64.into();
648 assert_eq!(key.as_u64(), 42);
649 }
650
651 #[test]
652 fn test_cache_default() {
653 let cache: DataCache<String, String> = DataCache::default();
654 assert!(cache.is_empty());
655 assert_eq!(cache.len(), 0);
656 }
657
658 #[test]
659 fn test_cache_insert_get() {
660 let mut cache: DataCache<String, String> = DataCache::default();
661
662 cache.insert_default("key".to_string(), "value".to_string());
663
664 let result = cache.get(&"key".to_string());
665 assert_eq!(result, Some(&"value".to_string()));
666 }
667
668 #[test]
669 fn test_cache_miss() {
670 let mut cache: DataCache<String, String> = DataCache::default();
671
672 let result = cache.get(&"missing".to_string());
673 assert_eq!(result, None);
674 }
675
676 #[test]
677 fn test_cache_contains() {
678 let mut cache: DataCache<String, String> = DataCache::default();
679
680 cache.insert_default("key".to_string(), "value".to_string());
681
682 assert!(cache.contains(&"key".to_string()));
683 assert!(!cache.contains(&"missing".to_string()));
684 }
685
686 #[test]
687 fn test_cache_remove() {
688 let mut cache: DataCache<String, String> = DataCache::default();
689
690 cache.insert_default("key".to_string(), "value".to_string());
691 let removed = cache.remove(&"key".to_string());
692
693 assert_eq!(removed, Some("value".to_string()));
694 assert!(!cache.contains(&"key".to_string()));
695 }
696
697 #[test]
698 fn test_cache_clear() {
699 let mut cache: DataCache<String, String> = DataCache::default();
700
701 cache.insert_default("key1".to_string(), "value1".to_string());
702 cache.insert_default("key2".to_string(), "value2".to_string());
703
704 cache.clear();
705
706 assert!(cache.is_empty());
707 assert_eq!(cache.len(), 0);
708 }
709
710 #[test]
711 fn test_cache_expiration() {
712 let config = CacheConfig {
713 default_ttl_ms: 100,
714 default_stale_ms: 0,
715 ..Default::default()
716 };
717 let mut cache: DataCache<String, String> = DataCache::new(config);
718
719 cache.insert_default("key".to_string(), "value".to_string());
720
721 assert!(cache.contains(&"key".to_string()));
723
724 cache.set_timestamp(200);
726
727 assert!(!cache.contains(&"key".to_string()));
729 assert!(cache.get(&"key".to_string()).is_none());
730 }
731
732 #[test]
733 fn test_cache_stale_while_revalidate() {
734 let config = CacheConfig {
735 default_ttl_ms: 100,
736 default_stale_ms: 50,
737 ..Default::default()
738 };
739 let mut cache: DataCache<String, String> = DataCache::new(config);
740
741 cache.insert_default("key".to_string(), "value".to_string());
742
743 let (_, state) = cache.get_with_state(&"key".to_string()).unwrap();
745 assert_eq!(state, CacheState::Fresh);
746
747 cache.set_timestamp(120);
749 let (_, state) = cache.get_with_state(&"key".to_string()).unwrap();
750 assert_eq!(state, CacheState::Stale);
751
752 cache.set_timestamp(200);
754 assert!(cache.get_with_state(&"key".to_string()).is_none());
755 }
756
757 #[test]
758 fn test_cache_custom_ttl() {
759 let config = CacheConfig {
760 default_ttl_ms: 1000,
761 default_stale_ms: 0, ..Default::default()
763 };
764 let mut cache: DataCache<String, String> = DataCache::new(config);
765
766 let options = CacheOptions::new().with_ttl(Duration::from_millis(50));
767 cache.insert("key".to_string(), "value".to_string(), options);
768
769 cache.set_timestamp(100);
770 assert!(cache.get(&"key".to_string()).is_none());
771 }
772
773 #[test]
774 fn test_cache_tags() {
775 let mut cache: DataCache<String, String> = DataCache::default();
776
777 let options1 = CacheOptions::new().with_tag("user").with_tag("profile");
778 let options2 = CacheOptions::new().with_tag("user");
779 let options3 = CacheOptions::new().with_tag("settings");
780
781 cache.insert("key1".to_string(), "value1".to_string(), options1);
782 cache.insert("key2".to_string(), "value2".to_string(), options2);
783 cache.insert("key3".to_string(), "value3".to_string(), options3);
784
785 let count = cache.invalidate_tag("user");
787 assert_eq!(count, 2);
788
789 assert!(!cache.contains(&"key1".to_string()));
790 assert!(!cache.contains(&"key2".to_string()));
791 assert!(cache.contains(&"key3".to_string()));
792 }
793
794 #[test]
795 fn test_cache_lru_eviction() {
796 let config = CacheConfig {
797 max_entries: 3,
798 enable_lru: true,
799 ..Default::default()
800 };
801 let mut cache: DataCache<String, i32> = DataCache::new(config);
802
803 cache.insert_default("key1".to_string(), 1);
804 cache.insert_default("key2".to_string(), 2);
805 cache.insert_default("key3".to_string(), 3);
806
807 cache.get(&"key1".to_string());
809
810 cache.insert_default("key4".to_string(), 4);
812
813 assert!(cache.contains(&"key1".to_string()));
814 assert!(!cache.contains(&"key2".to_string())); assert!(cache.contains(&"key3".to_string()));
816 assert!(cache.contains(&"key4".to_string()));
817 }
818
819 #[test]
820 fn test_cache_memory_limit() {
821 let config = CacheConfig {
822 max_memory: 20, ..Default::default()
824 };
825 let mut cache: DataCache<String, String> = DataCache::new(config);
826
827 cache.insert_default("key1".to_string(), "12345".to_string()); cache.insert_default("key2".to_string(), "12345".to_string()); cache.insert_default("key3".to_string(), "12345".to_string()); cache.insert_default("key4".to_string(), "12345".to_string()); assert!(cache.memory_usage() <= 20);
834 }
835
836 #[test]
837 fn test_cache_stats() {
838 let mut cache: DataCache<String, String> = DataCache::default();
839
840 cache.insert_default("key".to_string(), "value".to_string());
841
842 cache.get(&"key".to_string()); cache.get(&"key".to_string()); cache.get(&"missing".to_string()); let stats = cache.stats();
847 assert_eq!(stats.hits, 2);
848 assert_eq!(stats.misses, 1);
849 assert!((stats.hit_rate() - 0.666).abs() < 0.01);
850 }
851
852 #[test]
853 fn test_cache_tick() {
854 let config = CacheConfig {
855 cleanup_interval_ms: 100,
856 default_ttl_ms: 50,
857 default_stale_ms: 0, ..Default::default()
859 };
860 let mut cache: DataCache<String, String> = DataCache::new(config);
861
862 cache.insert_default("key".to_string(), "value".to_string());
863
864 cache.tick(150);
866
867 assert!(!cache.contains(&"key".to_string()));
869 }
870
871 #[test]
872 fn test_cache_metadata_state() {
873 let meta = CacheMetadata {
874 created_at: 0,
875 last_accessed: 0,
876 ttl_ms: 100,
877 stale_ms: 50,
878 access_count: 0,
879 size_bytes: 0,
880 tags: vec![],
881 };
882
883 assert_eq!(meta.state(50), CacheState::Fresh);
884 assert_eq!(meta.state(120), CacheState::Stale);
885 assert_eq!(meta.state(200), CacheState::Expired);
886
887 assert!(!meta.is_expired(100));
888 assert!(meta.is_expired(200));
889 }
890
891 #[test]
892 fn test_cache_options_builder() {
893 let options = CacheOptions::new()
894 .with_ttl(Duration::from_secs(60))
895 .with_stale(Duration::from_secs(30))
896 .with_tag("test")
897 .with_priority(5);
898
899 assert_eq!(options.ttl, Some(Duration::from_secs(60)));
900 assert_eq!(options.stale, Some(Duration::from_secs(30)));
901 assert_eq!(options.tags, vec!["test"]);
902 assert_eq!(options.priority, 5);
903 }
904
905 #[test]
906 fn test_cache_builder() {
907 let (value, options) = CacheBuilder::new("test".to_string())
908 .ttl(Duration::from_secs(60))
909 .stale(Duration::from_secs(30))
910 .tag("user")
911 .priority(3)
912 .build();
913
914 assert_eq!(value, "test");
915 assert_eq!(options.ttl, Some(Duration::from_secs(60)));
916 assert_eq!(options.tags, vec!["user"]);
917 }
918
919 #[test]
920 fn test_cache_config_default() {
921 let config = CacheConfig::default();
922 assert_eq!(config.max_entries, 1000);
923 assert_eq!(config.max_memory, 50 * 1024 * 1024);
924 assert_eq!(config.default_ttl_ms, 5 * 60 * 1000);
925 assert_eq!(config.default_stale_ms, 60 * 1000);
926 assert!(config.enable_lru);
927 }
928
929 #[test]
930 fn test_cache_size_string() {
931 let s = "hello".to_string();
932 assert_eq!(s.cache_size(), 5);
933 }
934
935 #[test]
936 fn test_cache_size_vec() {
937 let v: Vec<u8> = vec![1, 2, 3, 4, 5];
938 assert_eq!(v.cache_size(), 5);
939 }
940
941 #[test]
942 fn test_cache_size_i32() {
943 let n: i32 = 42;
944 assert_eq!(n.cache_size(), 4);
945 }
946
947 #[test]
948 fn test_cache_update_entry() {
949 let mut cache: DataCache<String, String> = DataCache::default();
950
951 cache.insert_default("key".to_string(), "value1".to_string());
952 cache.insert_default("key".to_string(), "value2".to_string());
953
954 let result = cache.get(&"key".to_string());
955 assert_eq!(result, Some(&"value2".to_string()));
956 assert_eq!(cache.len(), 1);
957 }
958
959 #[test]
960 fn test_cache_event_callback() {
961 use std::sync::atomic::{AtomicUsize, Ordering};
962
963 let mut cache: DataCache<String, String> = DataCache::default();
964 let event_count = Arc::new(AtomicUsize::new(0));
965
966 let ec = event_count.clone();
967 cache.on_event(Arc::new(move |_event| {
968 ec.fetch_add(1, Ordering::SeqCst);
969 }));
970
971 cache.invalidate_tag("test");
972 cache.clear();
973
974 assert!(event_count.load(Ordering::SeqCst) >= 1);
975 }
976
977 #[test]
978 fn test_cache_state_variants() {
979 assert_eq!(CacheState::Fresh, CacheState::Fresh);
980 assert_ne!(CacheState::Fresh, CacheState::Stale);
981 assert_ne!(CacheState::Stale, CacheState::Expired);
982 }
983
984 #[test]
985 fn test_stats_hit_rate_empty() {
986 let stats = CacheStats::default();
987 assert_eq!(stats.hit_rate(), 0.0);
988 }
989
990 #[test]
991 fn test_cache_timestamp() {
992 let mut cache: DataCache<String, String> = DataCache::default();
993 assert_eq!(cache.timestamp(), 0);
994
995 cache.set_timestamp(1000);
996 assert_eq!(cache.timestamp(), 1000);
997
998 cache.tick(500);
999 assert_eq!(cache.timestamp(), 1500);
1000 }
1001
1002 #[test]
1005 fn test_cache_key_empty_string() {
1006 let key = CacheKey::from_str("");
1007 assert_eq!(key.as_u64(), 0);
1008 }
1009
1010 #[test]
1011 fn test_cache_key_unicode() {
1012 let key1 = CacheKey::from_str("日本語");
1013 let key2 = CacheKey::from_str("日本語");
1014 let key3 = CacheKey::from_str("䏿–‡");
1015 assert_eq!(key1, key2);
1016 assert_ne!(key1, key3);
1017 }
1018
1019 #[test]
1020 fn test_cache_key_long_string() {
1021 let long_str: String = "a".repeat(10000);
1022 let key = CacheKey::from_str(&long_str);
1023 assert!(key.as_u64() > 0);
1024 }
1025
1026 #[test]
1027 fn test_cache_key_from_str_trait() {
1028 let key: CacheKey = "test".into();
1029 assert_eq!(key, CacheKey::from_str("test"));
1030 }
1031
1032 #[test]
1033 fn test_cache_key_hash_distribution() {
1034 let keys: Vec<CacheKey> = (0..100)
1036 .map(|i| CacheKey::from_str(&format!("key{i}")))
1037 .collect();
1038 let unique: std::collections::HashSet<_> = keys.iter().map(|k| k.as_u64()).collect();
1039 assert_eq!(unique.len(), 100);
1040 }
1041
1042 #[test]
1043 fn test_cache_key_special_chars() {
1044 let key = CacheKey::from_str("!@#$%^&*()_+-=[]{}|;':\",./<>?");
1045 assert!(key.as_u64() > 0);
1046 }
1047
1048 #[test]
1049 fn test_cache_key_whitespace() {
1050 let key1 = CacheKey::from_str(" ");
1051 let key2 = CacheKey::from_str("\t\n");
1052 assert_ne!(key1, key2);
1053 }
1054
1055 #[test]
1056 fn test_cache_key_debug() {
1057 let key = CacheKey::from_str("test");
1058 let debug = format!("{key:?}");
1059 assert!(debug.contains("CacheKey"));
1060 }
1061
1062 #[test]
1063 fn test_cache_key_clone() {
1064 let key1 = CacheKey::from_str("test");
1065 let key2 = key1;
1066 assert_eq!(key1, key2);
1067 }
1068
1069 #[test]
1072 fn test_cache_metadata_boundary_fresh() {
1073 let meta = CacheMetadata {
1074 created_at: 0,
1075 last_accessed: 0,
1076 ttl_ms: 100,
1077 stale_ms: 50,
1078 access_count: 0,
1079 size_bytes: 0,
1080 tags: vec![],
1081 };
1082 assert_eq!(meta.state(100), CacheState::Fresh);
1084 }
1085
1086 #[test]
1087 fn test_cache_metadata_boundary_stale() {
1088 let meta = CacheMetadata {
1089 created_at: 0,
1090 last_accessed: 0,
1091 ttl_ms: 100,
1092 stale_ms: 50,
1093 access_count: 0,
1094 size_bytes: 0,
1095 tags: vec![],
1096 };
1097 assert_eq!(meta.state(101), CacheState::Stale);
1099 assert_eq!(meta.state(150), CacheState::Stale);
1101 }
1102
1103 #[test]
1104 fn test_cache_metadata_zero_ttl() {
1105 let meta = CacheMetadata {
1106 created_at: 0,
1107 last_accessed: 0,
1108 ttl_ms: 0,
1109 stale_ms: 0,
1110 access_count: 0,
1111 size_bytes: 0,
1112 tags: vec![],
1113 };
1114 assert_eq!(meta.state(0), CacheState::Fresh);
1115 assert_eq!(meta.state(1), CacheState::Expired);
1116 }
1117
1118 #[test]
1119 fn test_cache_metadata_zero_stale() {
1120 let meta = CacheMetadata {
1121 created_at: 0,
1122 last_accessed: 0,
1123 ttl_ms: 100,
1124 stale_ms: 0,
1125 access_count: 0,
1126 size_bytes: 0,
1127 tags: vec![],
1128 };
1129 assert_eq!(meta.state(100), CacheState::Fresh);
1130 assert_eq!(meta.state(101), CacheState::Expired);
1131 }
1132
1133 #[test]
1134 fn test_cache_metadata_large_ttl() {
1135 let meta = CacheMetadata {
1136 created_at: 0,
1137 last_accessed: 0,
1138 ttl_ms: u64::MAX / 2,
1139 stale_ms: 1000,
1140 access_count: 0,
1141 size_bytes: 0,
1142 tags: vec![],
1143 };
1144 assert_eq!(meta.state(1_000_000), CacheState::Fresh);
1145 }
1146
1147 #[test]
1148 fn test_cache_metadata_created_in_future() {
1149 let meta = CacheMetadata {
1150 created_at: 1000,
1151 last_accessed: 1000,
1152 ttl_ms: 100,
1153 stale_ms: 50,
1154 access_count: 0,
1155 size_bytes: 0,
1156 tags: vec![],
1157 };
1158 assert_eq!(meta.state(500), CacheState::Fresh);
1160 }
1161
1162 #[test]
1163 fn test_cache_metadata_with_tags() {
1164 let meta = CacheMetadata {
1165 created_at: 0,
1166 last_accessed: 0,
1167 ttl_ms: 100,
1168 stale_ms: 50,
1169 access_count: 5,
1170 size_bytes: 1024,
1171 tags: vec!["user".to_string(), "profile".to_string()],
1172 };
1173 assert_eq!(meta.tags.len(), 2);
1174 assert_eq!(meta.access_count, 5);
1175 assert_eq!(meta.size_bytes, 1024);
1176 }
1177
1178 #[test]
1179 fn test_cache_metadata_clone() {
1180 let meta = CacheMetadata {
1181 created_at: 100,
1182 last_accessed: 200,
1183 ttl_ms: 1000,
1184 stale_ms: 500,
1185 access_count: 10,
1186 size_bytes: 256,
1187 tags: vec!["test".to_string()],
1188 };
1189 let cloned = meta.clone();
1190 assert_eq!(cloned.created_at, 100);
1191 assert_eq!(cloned.tags, vec!["test"]);
1192 }
1193
1194 #[test]
1197 fn test_cache_state_debug() {
1198 assert_eq!(format!("{:?}", CacheState::Fresh), "Fresh");
1199 assert_eq!(format!("{:?}", CacheState::Stale), "Stale");
1200 assert_eq!(format!("{:?}", CacheState::Expired), "Expired");
1201 }
1202
1203 #[test]
1204 fn test_cache_state_clone() {
1205 let state = CacheState::Fresh;
1206 let cloned = state;
1207 assert_eq!(state, cloned);
1208 }
1209
1210 #[test]
1213 fn test_cache_config_custom() {
1214 let config = CacheConfig {
1215 max_entries: 500,
1216 max_memory: 10 * 1024 * 1024,
1217 default_ttl_ms: 60_000,
1218 default_stale_ms: 10_000,
1219 enable_lru: false,
1220 cleanup_interval_ms: 30_000,
1221 };
1222 assert_eq!(config.max_entries, 500);
1223 assert!(!config.enable_lru);
1224 }
1225
1226 #[test]
1227 fn test_cache_config_clone() {
1228 let config = CacheConfig::default();
1229 let cloned = config.clone();
1230 assert_eq!(cloned.max_entries, 1000);
1231 }
1232
1233 #[test]
1236 fn test_cache_options_default() {
1237 let options = CacheOptions::default();
1238 assert!(options.ttl.is_none());
1239 assert!(options.stale.is_none());
1240 assert!(options.tags.is_empty());
1241 assert_eq!(options.priority, 0);
1242 }
1243
1244 #[test]
1245 fn test_cache_options_multiple_tags() {
1246 let options = CacheOptions::new()
1247 .with_tag("user")
1248 .with_tag("profile")
1249 .with_tag("admin");
1250 assert_eq!(options.tags.len(), 3);
1251 }
1252
1253 #[test]
1254 fn test_cache_options_zero_duration() {
1255 let options = CacheOptions::new()
1256 .with_ttl(Duration::ZERO)
1257 .with_stale(Duration::ZERO);
1258 assert_eq!(options.ttl, Some(Duration::ZERO));
1259 assert_eq!(options.stale, Some(Duration::ZERO));
1260 }
1261
1262 #[test]
1263 fn test_cache_options_max_priority() {
1264 let options = CacheOptions::new().with_priority(255);
1265 assert_eq!(options.priority, 255);
1266 }
1267
1268 #[test]
1269 fn test_cache_options_clone() {
1270 let options = CacheOptions::new()
1271 .with_ttl(Duration::from_secs(60))
1272 .with_tag("test");
1273 let cloned = options.clone();
1274 assert_eq!(cloned.ttl, Some(Duration::from_secs(60)));
1275 assert_eq!(cloned.tags, vec!["test"]);
1276 }
1277
1278 #[test]
1281 fn test_cache_event_all_variants() {
1282 let key = CacheKey::from_str("test");
1283 let events = vec![
1284 CacheEvent::Added(key),
1285 CacheEvent::Hit(key),
1286 CacheEvent::Miss(key),
1287 CacheEvent::Evicted(key),
1288 CacheEvent::Invalidated(key),
1289 CacheEvent::TagInvalidated("user".to_string(), 5),
1290 CacheEvent::Cleared,
1291 ];
1292 for event in events {
1293 let _ = format!("{event:?}");
1294 }
1295 }
1296
1297 #[test]
1298 fn test_cache_event_clone() {
1299 let event = CacheEvent::TagInvalidated("test".to_string(), 10);
1300 let cloned = event.clone();
1301 if let CacheEvent::TagInvalidated(tag, count) = cloned {
1302 assert_eq!(tag, "test");
1303 assert_eq!(count, 10);
1304 } else {
1305 panic!("Clone failed");
1306 }
1307 }
1308
1309 #[test]
1312 fn test_cache_lru_disabled() {
1313 let config = CacheConfig {
1314 max_entries: 3,
1315 enable_lru: false,
1316 ..Default::default()
1317 };
1318 let mut cache: DataCache<String, i32> = DataCache::new(config);
1319
1320 cache.insert_default("key1".to_string(), 1);
1321 cache.insert_default("key2".to_string(), 2);
1322 cache.insert_default("key3".to_string(), 3);
1323 cache.insert_default("key4".to_string(), 4);
1324
1325 assert_eq!(cache.len(), 3);
1327 }
1328
1329 #[test]
1330 fn test_cache_get_with_state_fresh() {
1331 let mut cache: DataCache<String, String> = DataCache::default();
1332 cache.insert_default("key".to_string(), "value".to_string());
1333
1334 let result = cache.get_with_state(&"key".to_string());
1335 assert!(result.is_some());
1336 let (value, state) = result.unwrap();
1337 assert_eq!(value, "value");
1338 assert_eq!(state, CacheState::Fresh);
1339 }
1340
1341 #[test]
1342 fn test_cache_get_with_state_miss() {
1343 let mut cache: DataCache<String, String> = DataCache::default();
1344 let result = cache.get_with_state(&"missing".to_string());
1345 assert!(result.is_none());
1346 assert_eq!(cache.stats().misses, 1);
1347 }
1348
1349 #[test]
1350 fn test_cache_multiple_removes() {
1351 let mut cache: DataCache<String, String> = DataCache::default();
1352 cache.insert_default("key".to_string(), "value".to_string());
1353
1354 let removed1 = cache.remove(&"key".to_string());
1355 let removed2 = cache.remove(&"key".to_string());
1356
1357 assert_eq!(removed1, Some("value".to_string()));
1358 assert_eq!(removed2, None);
1359 }
1360
1361 #[test]
1362 fn test_cache_invalidate_nonexistent_tag() {
1363 let mut cache: DataCache<String, String> = DataCache::default();
1364 cache.insert_default("key".to_string(), "value".to_string());
1365
1366 let count = cache.invalidate_tag("nonexistent");
1367 assert_eq!(count, 0);
1368 assert!(cache.contains(&"key".to_string()));
1369 }
1370
1371 #[test]
1372 fn test_cache_clear_empty() {
1373 let mut cache: DataCache<String, String> = DataCache::default();
1374 cache.clear();
1375 assert!(cache.is_empty());
1376 }
1377
1378 #[test]
1379 fn test_cache_memory_accounting() {
1380 let mut cache: DataCache<String, String> = DataCache::default();
1381
1382 cache.insert_default("key1".to_string(), "12345".to_string()); assert_eq!(cache.memory_usage(), 5);
1384
1385 cache.insert_default("key2".to_string(), "12345678".to_string()); assert_eq!(cache.memory_usage(), 13);
1387
1388 cache.remove(&"key1".to_string());
1389 assert_eq!(cache.memory_usage(), 8);
1390
1391 cache.clear();
1392 assert_eq!(cache.memory_usage(), 0);
1393 }
1394
1395 #[test]
1396 fn test_cache_replace_updates_memory() {
1397 let mut cache: DataCache<String, String> = DataCache::default();
1398
1399 cache.insert_default("key".to_string(), "12345".to_string()); assert_eq!(cache.memory_usage(), 5);
1401
1402 cache.insert_default("key".to_string(), "12345678901234567890".to_string()); assert_eq!(cache.memory_usage(), 20);
1404 }
1405
1406 #[test]
1407 fn test_cache_lru_order_updates_on_get() {
1408 let config = CacheConfig {
1409 max_entries: 2,
1410 enable_lru: true,
1411 ..Default::default()
1412 };
1413 let mut cache: DataCache<String, i32> = DataCache::new(config);
1414
1415 cache.insert_default("key1".to_string(), 1);
1416 cache.insert_default("key2".to_string(), 2);
1417
1418 cache.get(&"key1".to_string());
1420
1421 cache.insert_default("key3".to_string(), 3);
1423
1424 assert!(cache.contains(&"key1".to_string()));
1425 assert!(!cache.contains(&"key2".to_string()));
1426 assert!(cache.contains(&"key3".to_string()));
1427 }
1428
1429 #[test]
1430 fn test_cache_access_count_increments() {
1431 let config = CacheConfig {
1432 default_ttl_ms: 10000,
1433 ..Default::default()
1434 };
1435 let mut cache: DataCache<String, String> = DataCache::new(config);
1436 cache.insert_default("key".to_string(), "value".to_string());
1437
1438 for _ in 0..10 {
1439 cache.get(&"key".to_string());
1440 }
1441
1442 assert_eq!(cache.stats().hits, 10);
1443 }
1444
1445 #[test]
1446 fn test_cache_cleanup_triggered_by_tick() {
1447 let config = CacheConfig {
1448 cleanup_interval_ms: 50,
1449 default_ttl_ms: 25,
1450 default_stale_ms: 0,
1451 ..Default::default()
1452 };
1453 let mut cache: DataCache<String, String> = DataCache::new(config);
1454
1455 cache.insert_default("key".to_string(), "value".to_string());
1456 assert_eq!(cache.len(), 1);
1457
1458 cache.tick(30);
1460 assert!(cache.get(&"key".to_string()).is_none());
1462 assert_eq!(cache.entries.len(), 1);
1464
1465 cache.tick(30);
1467 assert_eq!(cache.entries.len(), 0);
1469 }
1470
1471 #[test]
1472 fn test_cache_multiple_listeners() {
1473 use std::sync::atomic::{AtomicUsize, Ordering};
1474
1475 let mut cache: DataCache<String, String> = DataCache::default();
1476 let count1 = Arc::new(AtomicUsize::new(0));
1477 let count2 = Arc::new(AtomicUsize::new(0));
1478
1479 let c1 = count1.clone();
1480 cache.on_event(Arc::new(move |_| {
1481 c1.fetch_add(1, Ordering::SeqCst);
1482 }));
1483
1484 let c2 = count2.clone();
1485 cache.on_event(Arc::new(move |_| {
1486 c2.fetch_add(1, Ordering::SeqCst);
1487 }));
1488
1489 cache.clear();
1490
1491 assert_eq!(count1.load(Ordering::SeqCst), 1);
1492 assert_eq!(count2.load(Ordering::SeqCst), 1);
1493 }
1494
1495 #[test]
1496 fn test_cache_eviction_updates_stats() {
1497 let config = CacheConfig {
1498 max_entries: 2,
1499 ..Default::default()
1500 };
1501 let mut cache: DataCache<String, i32> = DataCache::new(config);
1502
1503 cache.insert_default("key1".to_string(), 1);
1504 cache.insert_default("key2".to_string(), 2);
1505 cache.insert_default("key3".to_string(), 3);
1506
1507 assert_eq!(cache.stats().evictions, 1);
1508 }
1509
1510 #[test]
1511 fn test_cache_contains_expired_entry() {
1512 let config = CacheConfig {
1513 default_ttl_ms: 50,
1514 default_stale_ms: 0,
1515 ..Default::default()
1516 };
1517 let mut cache: DataCache<String, String> = DataCache::new(config);
1518
1519 cache.insert_default("key".to_string(), "value".to_string());
1520 assert!(cache.contains(&"key".to_string()));
1521
1522 cache.set_timestamp(100);
1523 assert!(!cache.contains(&"key".to_string()));
1524 }
1525
1526 #[test]
1527 fn test_cache_stats_current_entries() {
1528 let mut cache: DataCache<String, i32> = DataCache::default();
1529
1530 cache.insert_default("k1".to_string(), 1);
1531 assert_eq!(cache.stats().current_entries, 1);
1532
1533 cache.insert_default("k2".to_string(), 2);
1534 assert_eq!(cache.stats().current_entries, 2);
1535
1536 cache.remove(&"k1".to_string());
1537 assert_eq!(cache.stats().current_entries, 1);
1538 }
1539
1540 #[test]
1541 fn test_cache_integer_keys() {
1542 let mut cache: DataCache<u64, String> = DataCache::default();
1543
1544 cache.insert_default(1, "one".to_string());
1545 cache.insert_default(2, "two".to_string());
1546
1547 assert_eq!(cache.get(&1), Some(&"one".to_string()));
1548 assert_eq!(cache.get(&2), Some(&"two".to_string()));
1549 }
1550
1551 #[test]
1552 fn test_cache_with_custom_stale() {
1553 let config = CacheConfig {
1554 default_ttl_ms: 1000,
1555 default_stale_ms: 500,
1556 ..Default::default()
1557 };
1558 let mut cache: DataCache<String, String> = DataCache::new(config);
1559
1560 let options = CacheOptions::new().with_stale(Duration::from_millis(100));
1561 cache.insert("key".to_string(), "value".to_string(), options);
1562
1563 cache.set_timestamp(1050);
1565 let result = cache.get_with_state(&"key".to_string());
1566 assert!(result.is_some());
1567 let (_, state) = result.unwrap();
1568 assert_eq!(state, CacheState::Stale);
1569
1570 cache.set_timestamp(1150);
1572 assert!(cache.get_with_state(&"key".to_string()).is_none());
1573 }
1574
1575 #[test]
1578 fn test_cache_stats_hit_rate_all_hits() {
1579 let mut stats = CacheStats::default();
1580 stats.hits = 100;
1581 stats.misses = 0;
1582 assert_eq!(stats.hit_rate(), 1.0);
1583 }
1584
1585 #[test]
1586 fn test_cache_stats_hit_rate_all_misses() {
1587 let mut stats = CacheStats::default();
1588 stats.hits = 0;
1589 stats.misses = 100;
1590 assert_eq!(stats.hit_rate(), 0.0);
1591 }
1592
1593 #[test]
1594 fn test_cache_stats_hit_rate_half() {
1595 let mut stats = CacheStats::default();
1596 stats.hits = 50;
1597 stats.misses = 50;
1598 assert!((stats.hit_rate() - 0.5).abs() < 0.001);
1599 }
1600
1601 #[test]
1602 fn test_cache_stats_debug() {
1603 let stats = CacheStats::default();
1604 let debug = format!("{stats:?}");
1605 assert!(debug.contains("hits"));
1606 assert!(debug.contains("misses"));
1607 }
1608
1609 #[test]
1610 fn test_cache_stats_clone() {
1611 let mut stats = CacheStats::default();
1612 stats.hits = 42;
1613 stats.evictions = 5;
1614 let cloned = stats.clone();
1615 assert_eq!(cloned.hits, 42);
1616 assert_eq!(cloned.evictions, 5);
1617 }
1618
1619 #[test]
1622 fn test_cache_size_unit() {
1623 let unit = ();
1624 assert_eq!(unit.cache_size(), 0);
1625 }
1626
1627 #[test]
1628 fn test_cache_size_i64() {
1629 let n: i64 = 42;
1630 assert_eq!(n.cache_size(), 8);
1631 }
1632
1633 #[test]
1634 fn test_cache_size_f32() {
1635 let n: f32 = 3.14;
1636 assert_eq!(n.cache_size(), 4);
1637 }
1638
1639 #[test]
1640 fn test_cache_size_f64() {
1641 let n: f64 = 3.14159;
1642 assert_eq!(n.cache_size(), 8);
1643 }
1644
1645 #[test]
1646 fn test_cache_size_box() {
1647 let b: Box<i32> = Box::new(42);
1648 assert_eq!(b.cache_size(), 4);
1649 }
1650
1651 #[test]
1652 fn test_cache_size_empty_string() {
1653 let s = String::new();
1654 assert_eq!(s.cache_size(), 0);
1655 }
1656
1657 #[test]
1658 fn test_cache_size_empty_vec() {
1659 let v: Vec<u8> = Vec::new();
1660 assert_eq!(v.cache_size(), 0);
1661 }
1662
1663 #[test]
1664 fn test_cache_size_vec_of_structs() {
1665 #[derive(Clone)]
1666 struct Data {
1667 _a: i32,
1668 _b: i32,
1669 }
1670 let v: Vec<Data> = vec![Data { _a: 1, _b: 2 }, Data { _a: 3, _b: 4 }];
1671 assert_eq!(v.cache_size(), 16);
1673 }
1674
1675 #[test]
1678 fn test_cache_builder_default_options() {
1679 let (value, options) = CacheBuilder::new(42i32).build();
1680 assert_eq!(value, 42);
1681 assert!(options.ttl.is_none());
1682 assert!(options.tags.is_empty());
1683 }
1684
1685 #[test]
1686 fn test_cache_builder_multiple_tags() {
1687 let (_, options) = CacheBuilder::new("test".to_string())
1688 .tag("a")
1689 .tag("b")
1690 .tag("c")
1691 .build();
1692 assert_eq!(options.tags, vec!["a", "b", "c"]);
1693 }
1694
1695 #[test]
1696 fn test_cache_builder_chaining() {
1697 let (value, options) = CacheBuilder::new(vec![1, 2, 3])
1698 .ttl(Duration::from_secs(120))
1699 .stale(Duration::from_secs(60))
1700 .tag("numbers")
1701 .priority(10)
1702 .build();
1703
1704 assert_eq!(value, vec![1, 2, 3]);
1705 assert_eq!(options.ttl, Some(Duration::from_secs(120)));
1706 assert_eq!(options.stale, Some(Duration::from_secs(60)));
1707 assert_eq!(options.tags, vec!["numbers"]);
1708 assert_eq!(options.priority, 10);
1709 }
1710
1711 #[test]
1712 fn test_cache_builder_with_cache() {
1713 let mut cache: DataCache<String, String> = DataCache::default();
1714 let (value, options) = CacheBuilder::new("cached_value".to_string())
1715 .ttl(Duration::from_secs(300))
1716 .tag("test")
1717 .build();
1718
1719 cache.insert("key".to_string(), value, options);
1720 assert!(cache.contains(&"key".to_string()));
1721 }
1722
1723 #[test]
1726 fn test_cache_rapid_insert_remove() {
1727 let mut cache: DataCache<i32, i32> = DataCache::default();
1728
1729 for i in 0..1000 {
1730 cache.insert_default(i, i);
1731 if i % 2 == 0 {
1732 cache.remove(&i);
1733 }
1734 }
1735
1736 assert_eq!(cache.len(), 500);
1737 }
1738
1739 #[test]
1740 fn test_cache_same_key_multiple_times() {
1741 let mut cache: DataCache<String, i32> = DataCache::default();
1742
1743 for i in 0..100 {
1744 cache.insert_default("key".to_string(), i);
1745 }
1746
1747 assert_eq!(cache.len(), 1);
1748 assert_eq!(cache.get(&"key".to_string()), Some(&99));
1749 }
1750
1751 #[test]
1752 fn test_cache_evict_all() {
1753 let config = CacheConfig {
1754 max_entries: 5,
1755 ..Default::default()
1756 };
1757 let mut cache: DataCache<i32, i32> = DataCache::new(config);
1758
1759 for i in 0..10 {
1761 cache.insert_default(i, i);
1762 }
1763
1764 assert_eq!(cache.len(), 5);
1765 assert!(cache.stats().evictions >= 5);
1766 }
1767
1768 #[test]
1769 fn test_cache_memory_eviction_large_item() {
1770 let config = CacheConfig {
1771 max_memory: 100,
1772 ..Default::default()
1773 };
1774 let mut cache: DataCache<String, String> = DataCache::new(config);
1775
1776 cache.insert_default("key".to_string(), "a".repeat(200));
1778
1779 assert!(cache.memory_usage() <= 200);
1781 }
1782
1783 #[test]
1784 fn test_cache_get_updates_lru_order() {
1785 let config = CacheConfig {
1786 max_entries: 3,
1787 enable_lru: true,
1788 ..Default::default()
1789 };
1790 let mut cache: DataCache<String, i32> = DataCache::new(config);
1791
1792 cache.insert_default("a".to_string(), 1);
1793 cache.insert_default("b".to_string(), 2);
1794 cache.insert_default("c".to_string(), 3);
1795
1796 cache.get(&"a".to_string());
1798 cache.get(&"b".to_string());
1799
1800 cache.insert_default("d".to_string(), 4);
1802
1803 assert!(cache.contains(&"a".to_string()));
1804 assert!(cache.contains(&"b".to_string()));
1805 assert!(!cache.contains(&"c".to_string()));
1806 assert!(cache.contains(&"d".to_string()));
1807 }
1808
1809 #[test]
1810 fn test_cache_invalidate_multiple_tags_same_entry() {
1811 let mut cache: DataCache<String, String> = DataCache::default();
1812
1813 let options = CacheOptions::new().with_tag("tag1").with_tag("tag2");
1814 cache.insert("key".to_string(), "value".to_string(), options);
1815
1816 let count1 = cache.invalidate_tag("tag1");
1818 assert_eq!(count1, 1);
1819
1820 let count2 = cache.invalidate_tag("tag2");
1822 assert_eq!(count2, 0);
1823 }
1824
1825 #[test]
1826 fn test_cache_tick_zero() {
1827 let mut cache: DataCache<String, String> = DataCache::default();
1828 cache.tick(0);
1829 assert_eq!(cache.timestamp(), 0);
1830 }
1831
1832 #[test]
1833 fn test_cache_tick_large_values() {
1834 let mut cache: DataCache<String, String> = DataCache::default();
1835 cache.set_timestamp(u64::MAX / 2);
1836 cache.tick(1000);
1837 assert_eq!(cache.timestamp(), u64::MAX / 2 + 1000);
1838 }
1839
1840 #[test]
1841 fn test_cache_remove_nonexistent() {
1842 let mut cache: DataCache<String, String> = DataCache::default();
1843 let result = cache.remove(&"nonexistent".to_string());
1844 assert!(result.is_none());
1845 }
1846
1847 #[test]
1848 fn test_string_cache_type_alias() {
1849 let mut cache: StringCache<i32> = StringCache::default();
1850 cache.insert_default("key".to_string(), 42);
1851 assert_eq!(cache.get(&"key".to_string()), Some(&42));
1852 }
1853}