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