1use std::collections::HashMap;
7use std::sync::{Arc, Mutex};
8use std::time::{Duration, Instant};
9
10use crate::Cache;
11
12macro_rules! check_error_mode {
15 ($error_mode:expr) => {{
16 let guard = $error_mode.lock().unwrap();
17 if let Some(ref msg) = *guard {
18 return Err(anyhow::anyhow!("{}", msg));
19 }
20 }};
21}
22
23#[derive(Clone, Debug)]
25struct MockEntry {
26 value: Vec<u8>,
27 expires_at: Option<Instant>,
28}
29
30impl MockEntry {
31 fn is_expired(&self) -> bool {
32 self.expires_at
33 .map(|t| Instant::now() >= t)
34 .unwrap_or(false)
35 }
36}
37
38#[derive(Clone, Debug)]
61pub struct MockCache {
62 data: Arc<Mutex<HashMap<Vec<u8>, MockEntry>>>,
63 error_mode: Arc<Mutex<Option<String>>>,
65 operation_count: Arc<Mutex<OperationCounts>>,
67}
68
69#[derive(Clone, Debug, Default)]
71pub struct OperationCounts {
72 pub gets: usize,
73 pub sets: usize,
74 pub set_nx_px: usize,
75 pub deletes: usize,
76}
77
78impl MockCache {
79 pub fn new() -> Self {
81 Self {
82 data: Arc::new(Mutex::new(HashMap::new())),
83 error_mode: Arc::new(Mutex::new(None)),
84 operation_count: Arc::new(Mutex::new(OperationCounts::default())),
85 }
86 }
87
88 pub fn with_data<I, K, V>(entries: I) -> Self
94 where
95 I: IntoIterator<Item = (K, V)>,
96 K: AsRef<[u8]>,
97 V: AsRef<[u8]>,
98 {
99 let cache = Self::new();
100 {
101 let mut data = cache.data.lock().unwrap();
102 for (key, value) in entries {
103 data.insert(
104 key.as_ref().to_vec(),
105 MockEntry {
106 value: value.as_ref().to_vec(),
107 expires_at: None,
108 },
109 );
110 }
111 }
112 cache
113 }
114
115 pub fn enable_error_mode(&self, message: &str) {
119 let mut error_mode = self.error_mode.lock().unwrap();
120 *error_mode = Some(message.to_owned());
121 }
122
123 pub fn disable_error_mode(&self) {
125 let mut error_mode = self.error_mode.lock().unwrap();
126 *error_mode = None;
127 }
128
129 pub fn operation_counts(&self) -> OperationCounts {
131 self.operation_count.lock().unwrap().clone()
132 }
133
134 pub fn reset_counts(&self) {
136 let mut counts = self.operation_count.lock().unwrap();
137 *counts = OperationCounts::default();
138 }
139
140 pub fn len(&self) -> usize {
142 let data = self.data.lock().unwrap();
143 data.values().filter(|e| !e.is_expired()).count()
144 }
145
146 pub fn is_empty(&self) -> bool {
148 self.len() == 0
149 }
150
151 pub fn clear(&self) {
153 let mut data = self.data.lock().unwrap();
154 data.clear();
155 }
156
157 pub fn force_expire(&self, key: &[u8]) {
159 let mut data = self.data.lock().unwrap();
160 if let Some(entry) = data.get_mut(&key.to_vec()) {
161 entry.expires_at = Some(Instant::now() - Duration::from_secs(1));
162 }
163 }
164}
165
166impl Default for MockCache {
167 fn default() -> Self {
168 Self::new()
169 }
170}
171
172impl Cache for MockCache {
173 fn set_nx_px(
174 &self,
175 key: &[u8],
176 value: &[u8],
177 ttl: Duration,
178 ) -> impl Future<Output = anyhow::Result<bool>> + Send {
179 let key_vec = key.to_vec();
180 let value_vec = value.to_vec();
181 let data = Arc::clone(&self.data);
182 let error_mode = Arc::clone(&self.error_mode);
183 let operation_count = Arc::clone(&self.operation_count);
184
185 async move {
186 check_error_mode!(error_mode);
187 operation_count.lock().unwrap().set_nx_px += 1;
188
189 let mut data = data.lock().unwrap();
190
191 if let Some(entry) = data.get(&key_vec)
193 && !entry.is_expired()
194 {
195 return Ok(false);
196 }
197
198 let expires_at = if ttl.is_zero() {
200 Some(Instant::now())
201 } else {
202 Some(Instant::now() + ttl)
203 };
204
205 data.insert(
206 key_vec,
207 MockEntry {
208 value: value_vec,
209 expires_at,
210 },
211 );
212
213 Ok(true)
214 }
215 }
216
217 fn set(
218 &self,
219 key: &[u8],
220 value: &[u8],
221 ttl: Duration,
222 ) -> impl Future<Output = anyhow::Result<()>> + Send {
223 let key_vec = key.to_vec();
224 let value_vec = value.to_vec();
225 let data = Arc::clone(&self.data);
226 let error_mode = Arc::clone(&self.error_mode);
227 let operation_count = Arc::clone(&self.operation_count);
228
229 async move {
230 check_error_mode!(error_mode);
231 operation_count.lock().unwrap().sets += 1;
232
233 let expires_at = if ttl.is_zero() {
234 Some(Instant::now())
235 } else {
236 Some(Instant::now() + ttl)
237 };
238
239 let mut data = data.lock().unwrap();
240 data.insert(
241 key_vec,
242 MockEntry {
243 value: value_vec,
244 expires_at,
245 },
246 );
247
248 Ok(())
249 }
250 }
251
252 fn get(&self, key: &[u8]) -> impl Future<Output = anyhow::Result<Option<Vec<u8>>>> + Send {
253 let key_vec = key.to_vec();
254 let data = Arc::clone(&self.data);
255 let error_mode = Arc::clone(&self.error_mode);
256 let operation_count = Arc::clone(&self.operation_count);
257
258 async move {
259 check_error_mode!(error_mode);
260 operation_count.lock().unwrap().gets += 1;
261
262 let data = data.lock().unwrap();
263
264 match data.get(&key_vec) {
265 Some(entry) if !entry.is_expired() => Ok(Some(entry.value.clone())),
266 _ => Ok(None),
267 }
268 }
269 }
270
271 fn del(&self, key: &[u8]) -> impl Future<Output = anyhow::Result<()>> + Send {
272 let key_vec = key.to_vec();
273 let data = Arc::clone(&self.data);
274 let error_mode = Arc::clone(&self.error_mode);
275 let operation_count = Arc::clone(&self.operation_count);
276
277 async move {
278 check_error_mode!(error_mode);
279 operation_count.lock().unwrap().deletes += 1;
280
281 let mut data = data.lock().unwrap();
282 data.remove(&key_vec);
283
284 Ok(())
285 }
286 }
287}
288
289#[derive(Clone, Debug)]
291pub struct MockSet {
292 data: Arc<Mutex<std::collections::HashSet<String>>>,
293 error_mode: Arc<Mutex<Option<String>>>,
294}
295
296impl MockSet {
297 pub fn new() -> Self {
299 Self {
300 data: Arc::new(Mutex::new(std::collections::HashSet::new())),
301 error_mode: Arc::new(Mutex::new(None)),
302 }
303 }
304
305 pub fn enable_error_mode(&self, message: &str) {
307 let mut error_mode = self.error_mode.lock().unwrap();
308 *error_mode = Some(message.to_owned());
309 }
310
311 pub fn disable_error_mode(&self) {
313 let mut error_mode = self.error_mode.lock().unwrap();
314 *error_mode = None;
315 }
316
317 pub async fn add_items(&self, items: &[String]) -> anyhow::Result<()> {
319 check_error_mode!(self.error_mode);
320
321 if items.is_empty() {
322 return Ok(());
323 }
324
325 let mut data = self.data.lock().unwrap();
326 for item in items {
327 data.insert(item.clone());
328 }
329 Ok(())
330 }
331
332 pub async fn remove_items(&self, items: &[String]) -> anyhow::Result<()> {
334 check_error_mode!(self.error_mode);
335
336 if items.is_empty() {
337 return Ok(());
338 }
339
340 let mut data = self.data.lock().unwrap();
341 for item in items {
342 data.remove(item);
343 }
344 Ok(())
345 }
346
347 pub async fn add_item(&self, item: &str) -> anyhow::Result<()> {
349 self.add_items(&[item.to_owned()]).await
350 }
351
352 pub async fn remove_item(&self, item: &str) -> anyhow::Result<()> {
354 self.remove_items(&[item.to_owned()]).await
355 }
356
357 pub async fn load_items(&self) -> anyhow::Result<Vec<String>> {
359 check_error_mode!(self.error_mode);
360
361 let data = self.data.lock().unwrap();
362 Ok(data.iter().cloned().collect())
363 }
364
365 pub async fn trim_to(&self, max_entries: usize) -> anyhow::Result<()> {
367 check_error_mode!(self.error_mode);
368
369 if max_entries == 0 {
370 return Ok(());
371 }
372
373 let mut data = self.data.lock().unwrap();
374 while data.len() > max_entries {
375 if let Some(item) = data.iter().next().cloned() {
377 data.remove(&item);
378 }
379 }
380 Ok(())
381 }
382
383 pub fn len(&self) -> usize {
385 self.data.lock().unwrap().len()
386 }
387
388 pub fn is_empty(&self) -> bool {
390 self.data.lock().unwrap().is_empty()
391 }
392
393 pub fn clear(&self) {
395 self.data.lock().unwrap().clear();
396 }
397
398 pub fn contains(&self, item: &str) -> bool {
400 self.data.lock().unwrap().contains(item)
401 }
402}
403
404impl Default for MockSet {
405 fn default() -> Self {
406 Self::new()
407 }
408}
409
410#[cfg(test)]
411mod tests {
412 use super::*;
413
414 #[tokio::test]
415 async fn test_mock_cache_basic_operations() {
416 let cache = MockCache::new();
417
418 cache
420 .set(b"key1", b"value1", Duration::from_secs(60))
421 .await
422 .unwrap();
423 let result = cache.get(b"key1").await.unwrap();
424 assert_eq!(result, Some(b"value1".to_vec()));
425
426 let result = cache.get(b"nonexistent").await.unwrap();
428 assert_eq!(result, None);
429
430 cache.del(b"key1").await.unwrap();
432 let result = cache.get(b"key1").await.unwrap();
433 assert_eq!(result, None);
434 }
435
436 #[tokio::test]
437 async fn test_mock_cache_set_nx_px() {
438 let cache = MockCache::new();
439
440 let was_set = cache
442 .set_nx_px(b"key1", b"value1", Duration::from_secs(60))
443 .await
444 .unwrap();
445 assert!(was_set);
446
447 let was_set = cache
449 .set_nx_px(b"key1", b"value2", Duration::from_secs(60))
450 .await
451 .unwrap();
452 assert!(!was_set);
453
454 let result = cache.get(b"key1").await.unwrap();
456 assert_eq!(result, Some(b"value1".to_vec()));
457 }
458
459 #[tokio::test]
460 async fn test_mock_cache_error_mode() {
461 let cache = MockCache::new();
462
463 cache
465 .set(b"key1", b"value1", Duration::from_secs(60))
466 .await
467 .unwrap();
468
469 cache.enable_error_mode("Redis connection failed");
471
472 let result = cache.get(b"key1").await;
474 assert!(result.is_err());
475 assert!(
476 result
477 .unwrap_err()
478 .to_string()
479 .contains("connection failed")
480 );
481
482 let result = cache.set(b"key2", b"value2", Duration::from_secs(60)).await;
483 assert!(result.is_err());
484
485 let result = cache
486 .set_nx_px(b"key3", b"value3", Duration::from_secs(60))
487 .await;
488 assert!(result.is_err());
489
490 let result = cache.del(b"key1").await;
491 assert!(result.is_err());
492
493 cache.disable_error_mode();
495
496 let result = cache.get(b"key1").await.unwrap();
498 assert_eq!(result, Some(b"value1".to_vec()));
499 }
500
501 #[tokio::test]
502 async fn test_mock_cache_operation_counts() {
503 let cache = MockCache::new();
504
505 cache
506 .set(b"k1", b"v1", Duration::from_secs(60))
507 .await
508 .unwrap();
509 cache
510 .set(b"k2", b"v2", Duration::from_secs(60))
511 .await
512 .unwrap();
513 cache.get(b"k1").await.unwrap();
514 cache.get(b"k2").await.unwrap();
515 cache.get(b"k3").await.unwrap();
516 cache
517 .set_nx_px(b"k4", b"v4", Duration::from_secs(60))
518 .await
519 .unwrap();
520 cache.del(b"k1").await.unwrap();
521
522 let counts = cache.operation_counts();
523 assert_eq!(counts.sets, 2);
524 assert_eq!(counts.gets, 3);
525 assert_eq!(counts.set_nx_px, 1);
526 assert_eq!(counts.deletes, 1);
527
528 cache.reset_counts();
529 let counts = cache.operation_counts();
530 assert_eq!(counts.sets, 0);
531 assert_eq!(counts.gets, 0);
532 }
533
534 #[tokio::test]
535 async fn test_mock_cache_ttl_expiration() {
536 let cache = MockCache::new();
537
538 cache
539 .set(b"key1", b"value1", Duration::from_millis(50))
540 .await
541 .unwrap();
542
543 let result = cache.get(b"key1").await.unwrap();
545 assert_eq!(result, Some(b"value1".to_vec()));
546
547 tokio::time::sleep(Duration::from_millis(100)).await;
549
550 let result = cache.get(b"key1").await.unwrap();
552 assert_eq!(result, None);
553 }
554
555 #[tokio::test]
556 async fn test_mock_cache_force_expire() {
557 let cache = MockCache::new();
558
559 cache
560 .set(b"key1", b"value1", Duration::from_secs(60))
561 .await
562 .unwrap();
563
564 cache.force_expire(b"key1");
566
567 let result = cache.get(b"key1").await.unwrap();
569 assert_eq!(result, None);
570 }
571
572 #[tokio::test]
573 async fn test_mock_cache_with_data() {
574 let cache = MockCache::with_data([
575 (b"key1".as_slice(), b"value1".as_slice()),
576 (b"key2", b"value2"),
577 ]);
578
579 let result1 = cache.get(b"key1").await.unwrap();
580 assert_eq!(result1, Some(b"value1".to_vec()));
581
582 let result2 = cache.get(b"key2").await.unwrap();
583 assert_eq!(result2, Some(b"value2".to_vec()));
584 }
585
586 #[tokio::test]
587 async fn test_mock_cache_len_and_clear() {
588 let cache = MockCache::new();
589
590 assert!(cache.is_empty());
591 assert_eq!(cache.len(), 0);
592
593 cache
594 .set(b"k1", b"v1", Duration::from_secs(60))
595 .await
596 .unwrap();
597 cache
598 .set(b"k2", b"v2", Duration::from_secs(60))
599 .await
600 .unwrap();
601
602 assert!(!cache.is_empty());
603 assert_eq!(cache.len(), 2);
604
605 cache.clear();
606
607 assert!(cache.is_empty());
608 assert_eq!(cache.len(), 0);
609 }
610
611 #[tokio::test]
612 async fn test_mock_set_basic_operations() {
613 let set = MockSet::new();
614
615 assert!(set.is_empty());
616
617 set.add_item("item1").await.unwrap();
619 set.add_item("item2").await.unwrap();
620
621 assert_eq!(set.len(), 2);
622 assert!(set.contains("item1"));
623 assert!(set.contains("item2"));
624 assert!(!set.contains("item3"));
625
626 let items = set.load_items().await.unwrap();
628 assert_eq!(items.len(), 2);
629
630 set.remove_item("item1").await.unwrap();
632 assert!(!set.contains("item1"));
633 assert_eq!(set.len(), 1);
634
635 set.clear();
637 assert!(set.is_empty());
638 }
639
640 #[tokio::test]
641 async fn test_mock_set_batch_operations() {
642 let set = MockSet::new();
643
644 set.add_items(&["a".to_owned(), "b".to_owned(), "c".to_owned()])
646 .await
647 .unwrap();
648 assert_eq!(set.len(), 3);
649
650 set.remove_items(&["a".to_owned(), "c".to_owned()])
652 .await
653 .unwrap();
654 assert_eq!(set.len(), 1);
655 assert!(set.contains("b"));
656 }
657
658 #[tokio::test]
659 async fn test_mock_set_trim_to() {
660 let set = MockSet::new();
661
662 for i in 0..10 {
663 set.add_item(&format!("item{i}")).await.unwrap();
664 }
665 assert_eq!(set.len(), 10);
666
667 set.trim_to(5).await.unwrap();
668 assert_eq!(set.len(), 5);
669
670 set.trim_to(0).await.unwrap();
672 assert_eq!(set.len(), 5);
673 }
674
675 #[tokio::test]
676 async fn test_mock_set_error_mode() {
677 let set = MockSet::new();
678
679 set.add_item("item1").await.unwrap();
680
681 set.enable_error_mode("Redis error");
682
683 assert!(set.add_item("item2").await.is_err());
684 assert!(set.remove_item("item1").await.is_err());
685 assert!(set.load_items().await.is_err());
686 assert!(set.trim_to(1).await.is_err());
687
688 set.disable_error_mode();
689
690 assert!(set.load_items().await.is_ok());
691 }
692
693 #[tokio::test]
694 async fn test_mock_set_empty_operations() {
695 let set = MockSet::new();
696
697 set.add_items(&[]).await.unwrap();
699 set.remove_items(&[]).await.unwrap();
700
701 assert!(set.is_empty());
702 }
703}