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 '{}' not found",
207 id
208 )))
209 }
210 }
211 }
212
213 async fn list_all(&self) -> Result<Vec<NotificationRepoModel>, RepositoryError> {
214 let mut conn = self.client.as_ref().clone();
215 let notification_list_key = self.notification_list_key();
216
217 debug!("fetching all notification IDs");
218
219 let notification_ids: Vec<String> = conn
220 .smembers(¬ification_list_key)
221 .await
222 .map_err(|e| self.map_redis_error(e, "list_all_notification_ids"))?;
223
224 debug!(count = %notification_ids.len(), "found notification IDs");
225
226 let notifications = self.get_notifications_by_ids(¬ification_ids).await?;
227 Ok(notifications.results)
228 }
229
230 async fn list_paginated(
231 &self,
232 query: PaginationQuery,
233 ) -> Result<PaginatedResult<NotificationRepoModel>, RepositoryError> {
234 if query.per_page == 0 {
235 return Err(RepositoryError::InvalidData(
236 "per_page must be greater than 0".to_string(),
237 ));
238 }
239
240 let mut conn = self.client.as_ref().clone();
241 let notification_list_key = self.notification_list_key();
242
243 debug!(page = %query.page, per_page = %query.per_page, "fetching paginated notifications");
244
245 let all_notification_ids: Vec<String> = conn
246 .smembers(¬ification_list_key)
247 .await
248 .map_err(|e| self.map_redis_error(e, "list_paginated_notification_ids"))?;
249
250 let total = all_notification_ids.len() as u64;
251 let start = ((query.page - 1) * query.per_page) as usize;
252 let end = (start + query.per_page as usize).min(all_notification_ids.len());
253
254 if start >= all_notification_ids.len() {
255 debug!(page = %query.page, total = %total, "page is beyond available data");
256 return Ok(PaginatedResult {
257 items: vec![],
258 total,
259 page: query.page,
260 per_page: query.per_page,
261 });
262 }
263
264 let page_ids = &all_notification_ids[start..end];
265 let items = self.get_notifications_by_ids(page_ids).await?;
266
267 debug!(count = %items.results.len(), page = %query.page, "successfully fetched notifications for page");
268
269 Ok(PaginatedResult {
270 items: items.results.clone(),
271 total,
272 page: query.page,
273 per_page: query.per_page,
274 })
275 }
276
277 async fn update(
278 &self,
279 id: String,
280 entity: NotificationRepoModel,
281 ) -> Result<NotificationRepoModel, RepositoryError> {
282 if id.is_empty() {
283 return Err(RepositoryError::InvalidData(
284 "Notification ID cannot be empty".to_string(),
285 ));
286 }
287
288 if id != entity.id {
289 return Err(RepositoryError::InvalidData(
290 "Notification ID in URL does not match entity ID".to_string(),
291 ));
292 }
293
294 let key = self.notification_key(&id);
295 let mut conn = self.client.as_ref().clone();
296
297 debug!("updating notification");
298
299 let existing: Option<String> = conn
301 .get(&key)
302 .await
303 .map_err(|e| self.map_redis_error(e, "update_notification_check"))?;
304
305 if existing.is_none() {
306 return Err(RepositoryError::NotFound(format!(
307 "Notification with ID '{}' not found",
308 id
309 )));
310 }
311
312 let value = self.serialize_entity(&entity, |n| &n.id, "notification")?;
313
314 let _: () = conn
316 .set(&key, value)
317 .await
318 .map_err(|e| self.map_redis_error(e, "update_notification"))?;
319
320 debug!("successfully updated notification");
321 Ok(entity)
322 }
323
324 async fn delete_by_id(&self, id: String) -> Result<(), RepositoryError> {
325 if id.is_empty() {
326 return Err(RepositoryError::InvalidData(
327 "Notification ID cannot be empty".to_string(),
328 ));
329 }
330
331 let key = self.notification_key(&id);
332 let notification_list_key = self.notification_list_key();
333 let mut conn = self.client.as_ref().clone();
334
335 debug!("deleting notification");
336
337 let existing: Option<String> = conn
339 .get(&key)
340 .await
341 .map_err(|e| self.map_redis_error(e, "delete_notification_check"))?;
342
343 if existing.is_none() {
344 return Err(RepositoryError::NotFound(format!(
345 "Notification with ID '{}' not found",
346 id
347 )));
348 }
349
350 let mut pipe = redis::pipe();
352 pipe.atomic();
353 pipe.del(&key);
354 pipe.srem(¬ification_list_key, &id);
355
356 pipe.exec_async(&mut conn)
357 .await
358 .map_err(|e| self.map_redis_error(e, "delete_notification"))?;
359
360 debug!("successfully deleted notification");
361 Ok(())
362 }
363
364 async fn count(&self) -> Result<usize, RepositoryError> {
365 let mut conn = self.client.as_ref().clone();
366 let notification_list_key = self.notification_list_key();
367
368 debug!("counting notifications");
369
370 let count: u64 = conn
371 .scard(¬ification_list_key)
372 .await
373 .map_err(|e| self.map_redis_error(e, "count_notifications"))?;
374
375 debug!(count = %count, "notification count");
376 Ok(count as usize)
377 }
378
379 async fn has_entries(&self) -> Result<bool, RepositoryError> {
380 let mut conn = self.client.as_ref().clone();
381 let notification_list_key = self.notification_list_key();
382
383 debug!("checking if notification entries exist");
384
385 let exists: bool = conn
386 .exists(¬ification_list_key)
387 .await
388 .map_err(|e| self.map_redis_error(e, "has_entries_check"))?;
389
390 debug!(exists = %exists, "notification entries exist");
391 Ok(exists)
392 }
393
394 async fn drop_all_entries(&self) -> Result<(), RepositoryError> {
395 let mut conn = self.client.as_ref().clone();
396 let notification_list_key = self.notification_list_key();
397
398 debug!("dropping all notification entries");
399
400 let notification_ids: Vec<String> = conn
402 .smembers(¬ification_list_key)
403 .await
404 .map_err(|e| self.map_redis_error(e, "drop_all_entries_get_ids"))?;
405
406 if notification_ids.is_empty() {
407 debug!("no notification entries to drop");
408 return Ok(());
409 }
410
411 let mut pipe = redis::pipe();
413 pipe.atomic();
414
415 for notification_id in ¬ification_ids {
417 let notification_key = self.notification_key(notification_id);
418 pipe.del(¬ification_key);
419 }
420
421 pipe.del(¬ification_list_key);
423
424 pipe.exec_async(&mut conn)
425 .await
426 .map_err(|e| self.map_redis_error(e, "drop_all_entries_pipeline"))?;
427
428 debug!(count = %notification_ids.len(), "dropped notification entries");
429 Ok(())
430 }
431}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436 use crate::models::NotificationType;
437 use redis::Client;
438 use tokio;
439 use uuid::Uuid;
440
441 fn create_test_notification(id: &str) -> NotificationRepoModel {
443 NotificationRepoModel {
444 id: id.to_string(),
445 notification_type: NotificationType::Webhook,
446 url: "http://localhost:8080/webhook".to_string(),
447 signing_key: None,
448 }
449 }
450
451 fn create_test_notification_with_url(id: &str, url: &str) -> NotificationRepoModel {
452 NotificationRepoModel {
453 id: id.to_string(),
454 notification_type: NotificationType::Webhook,
455 url: url.to_string(),
456 signing_key: None,
457 }
458 }
459
460 async fn setup_test_repo() -> RedisNotificationRepository {
461 let redis_url = std::env::var("REDIS_TEST_URL")
463 .unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());
464
465 let client = Client::open(redis_url).expect("Failed to create Redis client");
466 let connection_manager = ConnectionManager::new(client)
467 .await
468 .expect("Failed to create connection manager");
469
470 RedisNotificationRepository::new(Arc::new(connection_manager), "test_prefix".to_string())
471 .expect("Failed to create RedisNotificationRepository")
472 }
473
474 #[tokio::test]
475 #[ignore = "Requires active Redis instance"]
476 async fn test_new_repository_creation() {
477 let repo = setup_test_repo().await;
478 assert_eq!(repo.key_prefix, "test_prefix");
479 }
480
481 #[tokio::test]
482 #[ignore = "Requires active Redis instance"]
483 async fn test_new_repository_empty_prefix_fails() {
484 let redis_url = std::env::var("REDIS_TEST_URL")
485 .unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());
486 let client = Client::open(redis_url).expect("Failed to create Redis client");
487 let connection_manager = ConnectionManager::new(client)
488 .await
489 .expect("Failed to create connection manager");
490
491 let result = RedisNotificationRepository::new(Arc::new(connection_manager), "".to_string());
492 assert!(matches!(result, Err(RepositoryError::InvalidData(_))));
493 }
494
495 #[tokio::test]
496 #[ignore = "Requires active Redis instance"]
497 async fn test_key_generation() {
498 let repo = setup_test_repo().await;
499
500 assert_eq!(
501 repo.notification_key("test-id"),
502 "test_prefix:notification:test-id"
503 );
504 assert_eq!(
505 repo.notification_list_key(),
506 "test_prefix:notification_list"
507 );
508 }
509
510 #[tokio::test]
511 #[ignore = "Requires active Redis instance"]
512
513 async fn test_serialize_deserialize_notification() {
514 let repo = setup_test_repo().await;
515 let random_id = Uuid::new_v4().to_string();
516 let notification = create_test_notification(&random_id);
517
518 let serialized = repo
519 .serialize_entity(¬ification, |n| &n.id, "notification")
520 .expect("Serialization should succeed");
521 let deserialized: NotificationRepoModel = repo
522 .deserialize_entity(&serialized, &random_id, "notification")
523 .expect("Deserialization should succeed");
524
525 assert_eq!(notification.id, deserialized.id);
526 assert_eq!(
527 notification.notification_type,
528 deserialized.notification_type
529 );
530 assert_eq!(notification.url, deserialized.url);
531 }
532
533 #[tokio::test]
534 #[ignore = "Requires active Redis instance"]
535 async fn test_create_notification() {
536 let repo = setup_test_repo().await;
537 let random_id = Uuid::new_v4().to_string();
538 let notification = create_test_notification(&random_id);
539
540 let result = repo.create(notification.clone()).await.unwrap();
541 assert_eq!(result.id, notification.id);
542 assert_eq!(result.url, notification.url);
543 }
544
545 #[tokio::test]
546 #[ignore = "Requires active Redis instance"]
547 async fn test_get_notification() {
548 let repo = setup_test_repo().await;
549 let random_id = Uuid::new_v4().to_string();
550 let notification = create_test_notification(&random_id);
551
552 repo.create(notification.clone()).await.unwrap();
553 let stored = repo.get_by_id(random_id.to_string()).await.unwrap();
554 assert_eq!(stored.id, notification.id);
555 assert_eq!(stored.url, notification.url);
556 }
557
558 #[tokio::test]
559 #[ignore = "Requires active Redis instance"]
560 async fn test_list_all_notifications() {
561 let repo = setup_test_repo().await;
562 let random_id = Uuid::new_v4().to_string();
563 let random_id2 = Uuid::new_v4().to_string();
564
565 let notification1 = create_test_notification(&random_id);
566 let notification2 = create_test_notification(&random_id2);
567
568 repo.create(notification1).await.unwrap();
569 repo.create(notification2).await.unwrap();
570
571 let notifications = repo.list_all().await.unwrap();
572 assert!(notifications.len() >= 2);
573 }
574
575 #[tokio::test]
576 #[ignore = "Requires active Redis instance"]
577 async fn test_count_notifications() {
578 let repo = setup_test_repo().await;
579 let random_id = Uuid::new_v4().to_string();
580 let notification = create_test_notification(&random_id);
581
582 let count = repo.count().await.unwrap();
583 repo.create(notification).await.unwrap();
584 assert!(repo.count().await.unwrap() > count);
585 }
586
587 #[tokio::test]
588 #[ignore = "Requires active Redis instance"]
589 async fn test_get_nonexistent_notification() {
590 let repo = setup_test_repo().await;
591 let result = repo.get_by_id("nonexistent".to_string()).await;
592 assert!(matches!(result, Err(RepositoryError::NotFound(_))));
593 }
594
595 #[tokio::test]
596 #[ignore = "Requires active Redis instance"]
597 async fn test_duplicate_notification_creation() {
598 let repo = setup_test_repo().await;
599 let random_id = Uuid::new_v4().to_string();
600
601 let notification = create_test_notification(&random_id);
602
603 repo.create(notification.clone()).await.unwrap();
604 let result = repo.create(notification).await;
605
606 assert!(matches!(
607 result,
608 Err(RepositoryError::ConstraintViolation(_))
609 ));
610 }
611
612 #[tokio::test]
613 #[ignore = "Requires active Redis instance"]
614 async fn test_update_notification() {
615 let repo = setup_test_repo().await;
616 let random_id = Uuid::new_v4().to_string();
617 let mut notification = create_test_notification(&random_id);
618
619 repo.create(notification.clone()).await.unwrap();
621
622 notification.url = "http://updated.example.com/webhook".to_string();
624 let result = repo
625 .update(random_id.to_string(), notification.clone())
626 .await
627 .unwrap();
628 assert_eq!(result.url, "http://updated.example.com/webhook");
629
630 let stored = repo.get_by_id(random_id.to_string()).await.unwrap();
632 assert_eq!(stored.url, "http://updated.example.com/webhook");
633 }
634
635 #[tokio::test]
636 #[ignore = "Requires active Redis instance"]
637 async fn test_delete_notification() {
638 let repo = setup_test_repo().await;
639 let random_id = Uuid::new_v4().to_string();
640 let notification = create_test_notification(&random_id);
641
642 repo.create(notification).await.unwrap();
644
645 let stored = repo.get_by_id(random_id.to_string()).await.unwrap();
647 assert_eq!(stored.id, random_id);
648
649 repo.delete_by_id(random_id.to_string()).await.unwrap();
651
652 let result = repo.get_by_id(random_id.to_string()).await;
654 assert!(matches!(result, Err(RepositoryError::NotFound(_))));
655 }
656
657 #[tokio::test]
658 #[ignore = "Requires active Redis instance"]
659 async fn test_list_paginated() {
660 let repo = setup_test_repo().await;
661
662 for i in 1..=10 {
664 let random_id = Uuid::new_v4().to_string();
665 let notification =
666 create_test_notification_with_url(&random_id, &format!("http://test{}.com", i));
667 repo.create(notification).await.unwrap();
668 }
669
670 let query = PaginationQuery {
672 page: 1,
673 per_page: 3,
674 };
675 let result = repo.list_paginated(query).await.unwrap();
676 assert_eq!(result.items.len(), 3);
677 assert!(result.total >= 10);
678 assert_eq!(result.page, 1);
679 assert_eq!(result.per_page, 3);
680
681 let query = PaginationQuery {
683 page: 1000,
684 per_page: 3,
685 };
686 let result = repo.list_paginated(query).await.unwrap();
687 assert_eq!(result.items.len(), 0);
688 }
689
690 #[tokio::test]
691 #[ignore = "Requires active Redis instance"]
692 async fn test_debug_implementation() {
693 let repo = setup_test_repo().await;
694 let debug_str = format!("{:?}", repo);
695 assert!(debug_str.contains("RedisNotificationRepository"));
696 assert!(debug_str.contains("test_prefix"));
697 }
698
699 #[tokio::test]
700 #[ignore = "Requires active Redis instance"]
701 async fn test_error_handling_empty_id() {
702 let repo = setup_test_repo().await;
703
704 let result = repo.get_by_id("".to_string()).await;
705 assert!(matches!(result, Err(RepositoryError::InvalidData(_))));
706 }
707
708 #[tokio::test]
709 #[ignore = "Requires active Redis instance"]
710 async fn test_pagination_validation() {
711 let repo = setup_test_repo().await;
712
713 let query = PaginationQuery {
714 page: 1,
715 per_page: 0,
716 };
717 let result = repo.list_paginated(query).await;
718 assert!(matches!(result, Err(RepositoryError::InvalidData(_))));
719 }
720
721 #[tokio::test]
722 #[ignore = "Requires active Redis instance"]
723 async fn test_update_nonexistent_notification() {
724 let repo = setup_test_repo().await;
725 let random_id = Uuid::new_v4().to_string();
726 let notification = create_test_notification(&random_id);
727
728 let result = repo.update(random_id.to_string(), notification).await;
729 assert!(matches!(result, Err(RepositoryError::NotFound(_))));
730 }
731
732 #[tokio::test]
733 #[ignore = "Requires active Redis instance"]
734 async fn test_delete_nonexistent_notification() {
735 let repo = setup_test_repo().await;
736 let random_id = Uuid::new_v4().to_string();
737
738 let result = repo.delete_by_id(random_id.to_string()).await;
739 assert!(matches!(result, Err(RepositoryError::NotFound(_))));
740 }
741
742 #[tokio::test]
743 #[ignore = "Requires active Redis instance"]
744 async fn test_update_with_empty_id() {
745 let repo = setup_test_repo().await;
746 let notification = create_test_notification("test-id");
747
748 let result = repo.update("".to_string(), notification).await;
749 assert!(matches!(result, Err(RepositoryError::InvalidData(_))));
750 }
751
752 #[tokio::test]
753 #[ignore = "Requires active Redis instance"]
754 async fn test_delete_with_empty_id() {
755 let repo = setup_test_repo().await;
756
757 let result = repo.delete_by_id("".to_string()).await;
758 assert!(matches!(result, Err(RepositoryError::InvalidData(_))));
759 }
760
761 #[tokio::test]
762 #[ignore = "Requires active Redis instance"]
763 async fn test_update_with_mismatched_id() {
764 let repo = setup_test_repo().await;
765 let random_id = Uuid::new_v4().to_string();
766 let notification = create_test_notification(&random_id);
767
768 repo.create(notification.clone()).await.unwrap();
770
771 let result = repo.update("different-id".to_string(), notification).await;
773 assert!(matches!(result, Err(RepositoryError::InvalidData(_))));
774 }
775
776 #[tokio::test]
777 #[ignore = "Requires active Redis instance"]
778 async fn test_delete_maintains_list_consistency() {
779 let repo = setup_test_repo().await;
780 let random_id = Uuid::new_v4().to_string();
781 let notification = create_test_notification(&random_id);
782
783 repo.create(notification).await.unwrap();
785
786 let all_notifications = repo.list_all().await.unwrap();
788 assert!(all_notifications.iter().any(|n| n.id == random_id));
789
790 repo.delete_by_id(random_id.to_string()).await.unwrap();
792
793 let all_notifications = repo.list_all().await.unwrap();
795 assert!(!all_notifications.iter().any(|n| n.id == random_id));
796 }
797
798 #[tokio::test]
800 #[ignore = "Requires active Redis instance"]
801 async fn test_has_entries() {
802 let repo = setup_test_repo().await;
803 assert!(!repo.has_entries().await.unwrap());
804
805 let notification = create_test_notification("test");
806 repo.create(notification.clone()).await.unwrap();
807 assert!(repo.has_entries().await.unwrap());
808 }
809
810 #[tokio::test]
811 #[ignore = "Requires active Redis instance"]
812 async fn test_drop_all_entries() {
813 let repo = setup_test_repo().await;
814 let notification = create_test_notification("test");
815
816 repo.create(notification.clone()).await.unwrap();
817 assert!(repo.has_entries().await.unwrap());
818
819 repo.drop_all_entries().await.unwrap();
820 assert!(!repo.has_entries().await.unwrap());
821 }
822}