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