1use crate::models::{NotificationRepoModel, PaginationQuery, RepositoryError};
4use crate::repositories::redis_base::RedisRepository;
5use crate::repositories::{BatchRetrievalResult, PaginatedResult, Repository};
6use async_trait::async_trait;
7use redis::aio::ConnectionManager;
8use redis::AsyncCommands;
9use std::fmt;
10use std::sync::Arc;
11use tracing::{debug, error, warn};
12
13const NOTIFICATION_PREFIX: &str = "notification";
14const NOTIFICATION_LIST_KEY: &str = "notification_list";
15
16#[derive(Clone)]
17pub struct RedisNotificationRepository {
18 pub client: Arc<ConnectionManager>,
19 pub key_prefix: String,
20}
21
22impl RedisRepository for RedisNotificationRepository {}
23
24impl RedisNotificationRepository {
25 pub fn new(
26 connection_manager: Arc<ConnectionManager>,
27 key_prefix: String,
28 ) -> Result<Self, RepositoryError> {
29 if key_prefix.is_empty() {
30 return Err(RepositoryError::InvalidData(
31 "Redis key prefix cannot be empty".to_string(),
32 ));
33 }
34
35 Ok(Self {
36 client: connection_manager,
37 key_prefix,
38 })
39 }
40
41 fn notification_key(&self, notification_id: &str) -> String {
43 format!(
44 "{}:{}:{}",
45 self.key_prefix, NOTIFICATION_PREFIX, notification_id
46 )
47 }
48
49 fn notification_list_key(&self) -> String {
51 format!("{}:{}", self.key_prefix, NOTIFICATION_LIST_KEY)
52 }
53
54 async fn get_notifications_by_ids(
56 &self,
57 ids: &[String],
58 ) -> Result<BatchRetrievalResult<NotificationRepoModel>, RepositoryError> {
59 if ids.is_empty() {
60 debug!("no notification IDs provided for batch fetch");
61 return Ok(BatchRetrievalResult {
62 results: vec![],
63 failed_ids: vec![],
64 });
65 }
66
67 let mut conn = self.client.as_ref().clone();
68 let keys: Vec<String> = ids.iter().map(|id| self.notification_key(id)).collect();
69
70 debug!(count = %keys.len(), "batch fetching notification data");
71
72 let values: Vec<Option<String>> = conn
73 .mget(&keys)
74 .await
75 .map_err(|e| self.map_redis_error(e, "batch_fetch_notifications"))?;
76
77 let mut notifications = Vec::new();
78 let mut failed_count = 0;
79 let mut failed_ids = Vec::new();
80 for (i, value) in values.into_iter().enumerate() {
81 match value {
82 Some(json) => {
83 match self.deserialize_entity::<NotificationRepoModel>(
84 &json,
85 &ids[i],
86 "notification",
87 ) {
88 Ok(notification) => notifications.push(notification),
89 Err(e) => {
90 failed_count += 1;
91 error!(error = %e, "failed to deserialize notification");
92 failed_ids.push(ids[i].clone());
93 }
95 }
96 }
97 None => {
98 warn!("notification not found in batch fetch");
99 }
100 }
101 }
102
103 if failed_count > 0 {
104 warn!(failed_count = %failed_count, total_count = %ids.len(), "failed to deserialize notifications in batch");
105 }
106
107 warn!(failed_ids = ?failed_ids, "failed to deserialize notifications");
108
109 debug!(count = %notifications.len(), "successfully fetched notifications");
110 Ok(BatchRetrievalResult {
111 results: notifications,
112 failed_ids,
113 })
114 }
115}
116
117impl fmt::Debug for RedisNotificationRepository {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 f.debug_struct("RedisNotificationRepository")
120 .field("client", &"<ConnectionManager>")
121 .field("key_prefix", &self.key_prefix)
122 .finish()
123 }
124}
125
126#[async_trait]
127impl Repository<NotificationRepoModel, String> for RedisNotificationRepository {
128 async fn create(
129 &self,
130 entity: NotificationRepoModel,
131 ) -> Result<NotificationRepoModel, RepositoryError> {
132 if entity.id.is_empty() {
133 return Err(RepositoryError::InvalidData(
134 "Notification ID cannot be empty".to_string(),
135 ));
136 }
137
138 if entity.url.is_empty() {
139 return Err(RepositoryError::InvalidData(
140 "Notification URL cannot be empty".to_string(),
141 ));
142 }
143
144 let key = self.notification_key(&entity.id);
145 let notification_list_key = self.notification_list_key();
146 let mut conn = self.client.as_ref().clone();
147
148 debug!("creating notification");
149
150 let value = self.serialize_entity(&entity, |n| &n.id, "notification")?;
151
152 let existing: Option<String> = conn
154 .get(&key)
155 .await
156 .map_err(|e| self.map_redis_error(e, "create_notification_check"))?;
157
158 if existing.is_some() {
159 return Err(RepositoryError::ConstraintViolation(format!(
160 "Notification with ID '{}' already exists",
161 entity.id
162 )));
163 }
164
165 let mut pipe = redis::pipe();
167 pipe.atomic();
168 pipe.set(&key, &value);
169 pipe.sadd(¬ification_list_key, &entity.id);
170
171 pipe.exec_async(&mut conn)
172 .await
173 .map_err(|e| self.map_redis_error(e, "create_notification"))?;
174
175 debug!("successfully created notification");
176 Ok(entity)
177 }
178
179 async fn get_by_id(&self, id: String) -> Result<NotificationRepoModel, RepositoryError> {
180 if id.is_empty() {
181 return Err(RepositoryError::InvalidData(
182 "Notification ID cannot be empty".to_string(),
183 ));
184 }
185
186 let mut conn = self.client.as_ref().clone();
187 let key = self.notification_key(&id);
188
189 debug!("fetching notification");
190
191 let value: Option<String> = conn
192 .get(&key)
193 .await
194 .map_err(|e| self.map_redis_error(e, "get_notification_by_id"))?;
195
196 match value {
197 Some(json) => {
198 let notification =
199 self.deserialize_entity::<NotificationRepoModel>(&json, &id, "notification")?;
200 debug!("successfully fetched notification");
201 Ok(notification)
202 }
203 None => {
204 debug!("notification not found");
205 Err(RepositoryError::NotFound(format!(
206 "Notification with ID '{id}' not found"
207 )))
208 }
209 }
210 }
211
212 async fn list_all(&self) -> Result<Vec<NotificationRepoModel>, RepositoryError> {
213 let mut conn = self.client.as_ref().clone();
214 let notification_list_key = self.notification_list_key();
215
216 debug!("fetching all notification IDs");
217
218 let notification_ids: Vec<String> = conn
219 .smembers(¬ification_list_key)
220 .await
221 .map_err(|e| self.map_redis_error(e, "list_all_notification_ids"))?;
222
223 debug!(count = %notification_ids.len(), "found notification IDs");
224
225 let notifications = self.get_notifications_by_ids(¬ification_ids).await?;
226 Ok(notifications.results)
227 }
228
229 async fn list_paginated(
230 &self,
231 query: PaginationQuery,
232 ) -> Result<PaginatedResult<NotificationRepoModel>, RepositoryError> {
233 if query.per_page == 0 {
234 return Err(RepositoryError::InvalidData(
235 "per_page must be greater than 0".to_string(),
236 ));
237 }
238
239 let mut conn = self.client.as_ref().clone();
240 let notification_list_key = self.notification_list_key();
241
242 debug!(page = %query.page, per_page = %query.per_page, "fetching paginated notifications");
243
244 let all_notification_ids: Vec<String> = conn
245 .smembers(¬ification_list_key)
246 .await
247 .map_err(|e| self.map_redis_error(e, "list_paginated_notification_ids"))?;
248
249 let total = all_notification_ids.len() as u64;
250 let start = ((query.page - 1) * query.per_page) as usize;
251 let end = (start + query.per_page as usize).min(all_notification_ids.len());
252
253 if start >= all_notification_ids.len() {
254 debug!(page = %query.page, total = %total, "page is beyond available data");
255 return Ok(PaginatedResult {
256 items: vec![],
257 total,
258 page: query.page,
259 per_page: query.per_page,
260 });
261 }
262
263 let page_ids = &all_notification_ids[start..end];
264 let items = self.get_notifications_by_ids(page_ids).await?;
265
266 debug!(count = %items.results.len(), page = %query.page, "successfully fetched notifications for page");
267
268 Ok(PaginatedResult {
269 items: items.results.clone(),
270 total,
271 page: query.page,
272 per_page: query.per_page,
273 })
274 }
275
276 async fn update(
277 &self,
278 id: String,
279 entity: NotificationRepoModel,
280 ) -> Result<NotificationRepoModel, RepositoryError> {
281 if id.is_empty() {
282 return Err(RepositoryError::InvalidData(
283 "Notification ID cannot be empty".to_string(),
284 ));
285 }
286
287 if id != entity.id {
288 return Err(RepositoryError::InvalidData(
289 "Notification ID in URL does not match entity ID".to_string(),
290 ));
291 }
292
293 let key = self.notification_key(&id);
294 let mut conn = self.client.as_ref().clone();
295
296 debug!("updating notification");
297
298 let existing: Option<String> = conn
300 .get(&key)
301 .await
302 .map_err(|e| self.map_redis_error(e, "update_notification_check"))?;
303
304 if existing.is_none() {
305 return Err(RepositoryError::NotFound(format!(
306 "Notification with ID '{id}' not found"
307 )));
308 }
309
310 let value = self.serialize_entity(&entity, |n| &n.id, "notification")?;
311
312 let _: () = conn
314 .set(&key, value)
315 .await
316 .map_err(|e| self.map_redis_error(e, "update_notification"))?;
317
318 debug!("successfully updated notification");
319 Ok(entity)
320 }
321
322 async fn delete_by_id(&self, id: String) -> Result<(), RepositoryError> {
323 if id.is_empty() {
324 return Err(RepositoryError::InvalidData(
325 "Notification ID cannot be empty".to_string(),
326 ));
327 }
328
329 let key = self.notification_key(&id);
330 let notification_list_key = self.notification_list_key();
331 let mut conn = self.client.as_ref().clone();
332
333 debug!("deleting notification");
334
335 let existing: Option<String> = conn
337 .get(&key)
338 .await
339 .map_err(|e| self.map_redis_error(e, "delete_notification_check"))?;
340
341 if existing.is_none() {
342 return Err(RepositoryError::NotFound(format!(
343 "Notification with ID '{id}' not found"
344 )));
345 }
346
347 let mut pipe = redis::pipe();
349 pipe.atomic();
350 pipe.del(&key);
351 pipe.srem(¬ification_list_key, &id);
352
353 pipe.exec_async(&mut conn)
354 .await
355 .map_err(|e| self.map_redis_error(e, "delete_notification"))?;
356
357 debug!("successfully deleted notification");
358 Ok(())
359 }
360
361 async fn count(&self) -> Result<usize, RepositoryError> {
362 let mut conn = self.client.as_ref().clone();
363 let notification_list_key = self.notification_list_key();
364
365 debug!("counting notifications");
366
367 let count: u64 = conn
368 .scard(¬ification_list_key)
369 .await
370 .map_err(|e| self.map_redis_error(e, "count_notifications"))?;
371
372 debug!(count = %count, "notification count");
373 Ok(count as usize)
374 }
375
376 async fn has_entries(&self) -> Result<bool, RepositoryError> {
377 let mut conn = self.client.as_ref().clone();
378 let notification_list_key = self.notification_list_key();
379
380 debug!("checking if notification entries exist");
381
382 let exists: bool = conn
383 .exists(¬ification_list_key)
384 .await
385 .map_err(|e| self.map_redis_error(e, "has_entries_check"))?;
386
387 debug!(exists = %exists, "notification entries exist");
388 Ok(exists)
389 }
390
391 async fn drop_all_entries(&self) -> Result<(), RepositoryError> {
392 let mut conn = self.client.as_ref().clone();
393 let notification_list_key = self.notification_list_key();
394
395 debug!("dropping all notification entries");
396
397 let notification_ids: Vec<String> = conn
399 .smembers(¬ification_list_key)
400 .await
401 .map_err(|e| self.map_redis_error(e, "drop_all_entries_get_ids"))?;
402
403 if notification_ids.is_empty() {
404 debug!("no notification entries to drop");
405 return Ok(());
406 }
407
408 let mut pipe = redis::pipe();
410 pipe.atomic();
411
412 for notification_id in ¬ification_ids {
414 let notification_key = self.notification_key(notification_id);
415 pipe.del(¬ification_key);
416 }
417
418 pipe.del(¬ification_list_key);
420
421 pipe.exec_async(&mut conn)
422 .await
423 .map_err(|e| self.map_redis_error(e, "drop_all_entries_pipeline"))?;
424
425 debug!(count = %notification_ids.len(), "dropped notification entries");
426 Ok(())
427 }
428}
429
430#[cfg(test)]
431mod tests {
432 use super::*;
433 use crate::models::NotificationType;
434 use redis::Client;
435 use tokio;
436 use uuid::Uuid;
437
438 fn create_test_notification(id: &str) -> NotificationRepoModel {
440 NotificationRepoModel {
441 id: id.to_string(),
442 notification_type: NotificationType::Webhook,
443 url: "http://localhost:8080/webhook".to_string(),
444 signing_key: None,
445 }
446 }
447
448 fn create_test_notification_with_url(id: &str, url: &str) -> NotificationRepoModel {
449 NotificationRepoModel {
450 id: id.to_string(),
451 notification_type: NotificationType::Webhook,
452 url: url.to_string(),
453 signing_key: None,
454 }
455 }
456
457 async fn setup_test_repo() -> RedisNotificationRepository {
458 let redis_url = std::env::var("REDIS_TEST_URL")
460 .unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());
461
462 let client = Client::open(redis_url).expect("Failed to create Redis client");
463 let connection_manager = ConnectionManager::new(client)
464 .await
465 .expect("Failed to create connection manager");
466
467 RedisNotificationRepository::new(Arc::new(connection_manager), "test_prefix".to_string())
468 .expect("Failed to create RedisNotificationRepository")
469 }
470
471 #[tokio::test]
472 #[ignore = "Requires active Redis instance"]
473 async fn test_new_repository_creation() {
474 let repo = setup_test_repo().await;
475 assert_eq!(repo.key_prefix, "test_prefix");
476 }
477
478 #[tokio::test]
479 #[ignore = "Requires active Redis instance"]
480 async fn test_new_repository_empty_prefix_fails() {
481 let redis_url = std::env::var("REDIS_TEST_URL")
482 .unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());
483 let client = Client::open(redis_url).expect("Failed to create Redis client");
484 let connection_manager = ConnectionManager::new(client)
485 .await
486 .expect("Failed to create connection manager");
487
488 let result = RedisNotificationRepository::new(Arc::new(connection_manager), "".to_string());
489 assert!(matches!(result, Err(RepositoryError::InvalidData(_))));
490 }
491
492 #[tokio::test]
493 #[ignore = "Requires active Redis instance"]
494 async fn test_key_generation() {
495 let repo = setup_test_repo().await;
496
497 assert_eq!(
498 repo.notification_key("test-id"),
499 "test_prefix:notification:test-id"
500 );
501 assert_eq!(
502 repo.notification_list_key(),
503 "test_prefix:notification_list"
504 );
505 }
506
507 #[tokio::test]
508 #[ignore = "Requires active Redis instance"]
509
510 async fn test_serialize_deserialize_notification() {
511 let repo = setup_test_repo().await;
512 let random_id = Uuid::new_v4().to_string();
513 let notification = create_test_notification(&random_id);
514
515 let serialized = repo
516 .serialize_entity(¬ification, |n| &n.id, "notification")
517 .expect("Serialization should succeed");
518 let deserialized: NotificationRepoModel = repo
519 .deserialize_entity(&serialized, &random_id, "notification")
520 .expect("Deserialization should succeed");
521
522 assert_eq!(notification.id, deserialized.id);
523 assert_eq!(
524 notification.notification_type,
525 deserialized.notification_type
526 );
527 assert_eq!(notification.url, deserialized.url);
528 }
529
530 #[tokio::test]
531 #[ignore = "Requires active Redis instance"]
532 async fn test_create_notification() {
533 let repo = setup_test_repo().await;
534 let random_id = Uuid::new_v4().to_string();
535 let notification = create_test_notification(&random_id);
536
537 let result = repo.create(notification.clone()).await.unwrap();
538 assert_eq!(result.id, notification.id);
539 assert_eq!(result.url, notification.url);
540 }
541
542 #[tokio::test]
543 #[ignore = "Requires active Redis instance"]
544 async fn test_get_notification() {
545 let repo = setup_test_repo().await;
546 let random_id = Uuid::new_v4().to_string();
547 let notification = create_test_notification(&random_id);
548
549 repo.create(notification.clone()).await.unwrap();
550 let stored = repo.get_by_id(random_id.to_string()).await.unwrap();
551 assert_eq!(stored.id, notification.id);
552 assert_eq!(stored.url, notification.url);
553 }
554
555 #[tokio::test]
556 #[ignore = "Requires active Redis instance"]
557 async fn test_list_all_notifications() {
558 let repo = setup_test_repo().await;
559 let random_id = Uuid::new_v4().to_string();
560 let random_id2 = Uuid::new_v4().to_string();
561
562 let notification1 = create_test_notification(&random_id);
563 let notification2 = create_test_notification(&random_id2);
564
565 repo.create(notification1).await.unwrap();
566 repo.create(notification2).await.unwrap();
567
568 let notifications = repo.list_all().await.unwrap();
569 assert!(notifications.len() >= 2);
570 }
571
572 #[tokio::test]
573 #[ignore = "Requires active Redis instance"]
574 async fn test_count_notifications() {
575 let repo = setup_test_repo().await;
576 let random_id = Uuid::new_v4().to_string();
577 let notification = create_test_notification(&random_id);
578
579 let count = repo.count().await.unwrap();
580 repo.create(notification).await.unwrap();
581 assert!(repo.count().await.unwrap() > count);
582 }
583
584 #[tokio::test]
585 #[ignore = "Requires active Redis instance"]
586 async fn test_get_nonexistent_notification() {
587 let repo = setup_test_repo().await;
588 let result = repo.get_by_id("nonexistent".to_string()).await;
589 assert!(matches!(result, Err(RepositoryError::NotFound(_))));
590 }
591
592 #[tokio::test]
593 #[ignore = "Requires active Redis instance"]
594 async fn test_duplicate_notification_creation() {
595 let repo = setup_test_repo().await;
596 let random_id = Uuid::new_v4().to_string();
597
598 let notification = create_test_notification(&random_id);
599
600 repo.create(notification.clone()).await.unwrap();
601 let result = repo.create(notification).await;
602
603 assert!(matches!(
604 result,
605 Err(RepositoryError::ConstraintViolation(_))
606 ));
607 }
608
609 #[tokio::test]
610 #[ignore = "Requires active Redis instance"]
611 async fn test_update_notification() {
612 let repo = setup_test_repo().await;
613 let random_id = Uuid::new_v4().to_string();
614 let mut notification = create_test_notification(&random_id);
615
616 repo.create(notification.clone()).await.unwrap();
618
619 notification.url = "http://updated.example.com/webhook".to_string();
621 let result = repo
622 .update(random_id.to_string(), notification.clone())
623 .await
624 .unwrap();
625 assert_eq!(result.url, "http://updated.example.com/webhook");
626
627 let stored = repo.get_by_id(random_id.to_string()).await.unwrap();
629 assert_eq!(stored.url, "http://updated.example.com/webhook");
630 }
631
632 #[tokio::test]
633 #[ignore = "Requires active Redis instance"]
634 async fn test_delete_notification() {
635 let repo = setup_test_repo().await;
636 let random_id = Uuid::new_v4().to_string();
637 let notification = create_test_notification(&random_id);
638
639 repo.create(notification).await.unwrap();
641
642 let stored = repo.get_by_id(random_id.to_string()).await.unwrap();
644 assert_eq!(stored.id, random_id);
645
646 repo.delete_by_id(random_id.to_string()).await.unwrap();
648
649 let result = repo.get_by_id(random_id.to_string()).await;
651 assert!(matches!(result, Err(RepositoryError::NotFound(_))));
652 }
653
654 #[tokio::test]
655 #[ignore = "Requires active Redis instance"]
656 async fn test_list_paginated() {
657 let repo = setup_test_repo().await;
658
659 for i in 1..=10 {
661 let random_id = Uuid::new_v4().to_string();
662 let notification =
663 create_test_notification_with_url(&random_id, &format!("http://test{}.com", i));
664 repo.create(notification).await.unwrap();
665 }
666
667 let query = PaginationQuery {
669 page: 1,
670 per_page: 3,
671 };
672 let result = repo.list_paginated(query).await.unwrap();
673 assert_eq!(result.items.len(), 3);
674 assert!(result.total >= 10);
675 assert_eq!(result.page, 1);
676 assert_eq!(result.per_page, 3);
677
678 let query = PaginationQuery {
680 page: 1000,
681 per_page: 3,
682 };
683 let result = repo.list_paginated(query).await.unwrap();
684 assert_eq!(result.items.len(), 0);
685 }
686
687 #[tokio::test]
688 #[ignore = "Requires active Redis instance"]
689 async fn test_debug_implementation() {
690 let repo = setup_test_repo().await;
691 let debug_str = format!("{:?}", repo);
692 assert!(debug_str.contains("RedisNotificationRepository"));
693 assert!(debug_str.contains("test_prefix"));
694 }
695
696 #[tokio::test]
697 #[ignore = "Requires active Redis instance"]
698 async fn test_error_handling_empty_id() {
699 let repo = setup_test_repo().await;
700
701 let result = repo.get_by_id("".to_string()).await;
702 assert!(matches!(result, Err(RepositoryError::InvalidData(_))));
703 }
704
705 #[tokio::test]
706 #[ignore = "Requires active Redis instance"]
707 async fn test_pagination_validation() {
708 let repo = setup_test_repo().await;
709
710 let query = PaginationQuery {
711 page: 1,
712 per_page: 0,
713 };
714 let result = repo.list_paginated(query).await;
715 assert!(matches!(result, Err(RepositoryError::InvalidData(_))));
716 }
717
718 #[tokio::test]
719 #[ignore = "Requires active Redis instance"]
720 async fn test_update_nonexistent_notification() {
721 let repo = setup_test_repo().await;
722 let random_id = Uuid::new_v4().to_string();
723 let notification = create_test_notification(&random_id);
724
725 let result = repo.update(random_id.to_string(), notification).await;
726 assert!(matches!(result, Err(RepositoryError::NotFound(_))));
727 }
728
729 #[tokio::test]
730 #[ignore = "Requires active Redis instance"]
731 async fn test_delete_nonexistent_notification() {
732 let repo = setup_test_repo().await;
733 let random_id = Uuid::new_v4().to_string();
734
735 let result = repo.delete_by_id(random_id.to_string()).await;
736 assert!(matches!(result, Err(RepositoryError::NotFound(_))));
737 }
738
739 #[tokio::test]
740 #[ignore = "Requires active Redis instance"]
741 async fn test_update_with_empty_id() {
742 let repo = setup_test_repo().await;
743 let notification = create_test_notification("test-id");
744
745 let result = repo.update("".to_string(), notification).await;
746 assert!(matches!(result, Err(RepositoryError::InvalidData(_))));
747 }
748
749 #[tokio::test]
750 #[ignore = "Requires active Redis instance"]
751 async fn test_delete_with_empty_id() {
752 let repo = setup_test_repo().await;
753
754 let result = repo.delete_by_id("".to_string()).await;
755 assert!(matches!(result, Err(RepositoryError::InvalidData(_))));
756 }
757
758 #[tokio::test]
759 #[ignore = "Requires active Redis instance"]
760 async fn test_update_with_mismatched_id() {
761 let repo = setup_test_repo().await;
762 let random_id = Uuid::new_v4().to_string();
763 let notification = create_test_notification(&random_id);
764
765 repo.create(notification.clone()).await.unwrap();
767
768 let result = repo.update("different-id".to_string(), notification).await;
770 assert!(matches!(result, Err(RepositoryError::InvalidData(_))));
771 }
772
773 #[tokio::test]
774 #[ignore = "Requires active Redis instance"]
775 async fn test_delete_maintains_list_consistency() {
776 let repo = setup_test_repo().await;
777 let random_id = Uuid::new_v4().to_string();
778 let notification = create_test_notification(&random_id);
779
780 repo.create(notification).await.unwrap();
782
783 let all_notifications = repo.list_all().await.unwrap();
785 assert!(all_notifications.iter().any(|n| n.id == random_id));
786
787 repo.delete_by_id(random_id.to_string()).await.unwrap();
789
790 let all_notifications = repo.list_all().await.unwrap();
792 assert!(!all_notifications.iter().any(|n| n.id == random_id));
793 }
794
795 #[tokio::test]
797 #[ignore = "Requires active Redis instance"]
798 async fn test_has_entries() {
799 let repo = setup_test_repo().await;
800 assert!(!repo.has_entries().await.unwrap());
801
802 let notification = create_test_notification("test");
803 repo.create(notification.clone()).await.unwrap();
804 assert!(repo.has_entries().await.unwrap());
805 }
806
807 #[tokio::test]
808 #[ignore = "Requires active Redis instance"]
809 async fn test_drop_all_entries() {
810 let repo = setup_test_repo().await;
811 let notification = create_test_notification("test");
812
813 repo.create(notification.clone()).await.unwrap();
814 assert!(repo.has_entries().await.unwrap());
815
816 repo.drop_all_entries().await.unwrap();
817 assert!(!repo.has_entries().await.unwrap());
818 }
819}