1use crate::models::{RepositoryError, SignerRepoModel};
4use crate::repositories::redis_base::RedisRepository;
5use crate::repositories::*;
6use async_trait::async_trait;
7use log::{debug, error, warn};
8use redis::aio::ConnectionManager;
9use redis::{AsyncCommands, RedisError};
10use std::fmt;
11use std::sync::Arc;
12
13const SIGNER_PREFIX: &str = "signer";
14const SIGNER_LIST_KEY: &str = "signer_list";
15
16#[derive(Clone)]
17pub struct RedisSignerRepository {
18 pub client: Arc<ConnectionManager>,
19 pub key_prefix: String,
20}
21
22impl RedisRepository for RedisSignerRepository {}
23
24impl RedisSignerRepository {
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 signer_key(&self, id: &str) -> String {
42 format!("{}:{}:{}", self.key_prefix, SIGNER_PREFIX, id)
43 }
44
45 fn signer_list_key(&self) -> String {
46 format!("{}:{}", self.key_prefix, SIGNER_LIST_KEY)
47 }
48
49 async fn add_to_list(&self, id: &str) -> Result<(), RepositoryError> {
50 let key = self.signer_list_key();
51 let mut conn = self.client.as_ref().clone();
52
53 let result: Result<i64, RedisError> = conn.sadd(&key, id).await;
54 result.map_err(|e| {
55 error!("Failed to add signer {} to list: {}", id, e);
56 RepositoryError::Other(format!("Failed to add signer to list: {}", e))
57 })?;
58
59 debug!("Added signer {} to list", id);
60 Ok(())
61 }
62
63 async fn remove_from_list(&self, id: &str) -> Result<(), RepositoryError> {
64 let key = self.signer_list_key();
65 let mut conn = self.client.as_ref().clone();
66
67 let result: Result<i64, RedisError> = conn.srem(&key, id).await;
68 result.map_err(|e| {
69 error!("Failed to remove signer {} from list: {}", id, e);
70 RepositoryError::Other(format!("Failed to remove signer from list: {}", e))
71 })?;
72
73 debug!("Removed signer {} from list", id);
74 Ok(())
75 }
76
77 async fn get_all_ids(&self) -> Result<Vec<String>, RepositoryError> {
78 let key = self.signer_list_key();
79 let mut conn = self.client.as_ref().clone();
80
81 let result: Result<Vec<String>, RedisError> = conn.smembers(&key).await;
82 result.map_err(|e| {
83 error!("Failed to get signer IDs: {}", e);
84 RepositoryError::Other(format!("Failed to get signer IDs: {}", e))
85 })
86 }
87
88 async fn get_signers_by_ids(
90 &self,
91 ids: &[String],
92 ) -> Result<BatchRetrievalResult<SignerRepoModel>, RepositoryError> {
93 if ids.is_empty() {
94 debug!("No signer IDs provided for batch fetch");
95 return Ok(BatchRetrievalResult {
96 results: vec![],
97 failed_ids: vec![],
98 });
99 }
100
101 let mut conn = self.client.as_ref().clone();
102 let keys: Vec<String> = ids.iter().map(|id| self.signer_key(id)).collect();
103
104 debug!("Batch fetching {} signers", ids.len());
105
106 let values: Vec<Option<String>> = conn
107 .mget(&keys)
108 .await
109 .map_err(|e| self.map_redis_error(e, "batch_fetch_signers"))?;
110
111 let mut signers = Vec::new();
112 let mut failed_count = 0;
113 let mut failed_ids = Vec::new();
114
115 for (i, value) in values.into_iter().enumerate() {
116 match value {
117 Some(json) => {
118 match self.deserialize_entity::<SignerRepoModel>(&json, &ids[i], "signer") {
119 Ok(signer) => signers.push(signer),
120 Err(e) => {
121 failed_count += 1;
122 error!("Failed to deserialize signer {}: {}", ids[i], e);
123 failed_ids.push(ids[i].clone());
124 }
125 }
126 }
127 None => {
128 warn!("Signer {} not found in batch fetch", ids[i]);
129 }
130 }
131 }
132
133 if failed_count > 0 {
134 warn!(
135 "Failed to deserialize {} out of {} signers in batch",
136 failed_count,
137 ids.len()
138 );
139 warn!("Failed to deserialize signers: {:?}", failed_ids);
140 }
141
142 debug!("Successfully fetched {} signers", signers.len());
143 Ok(BatchRetrievalResult {
144 results: signers,
145 failed_ids,
146 })
147 }
148}
149
150impl fmt::Debug for RedisSignerRepository {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 f.debug_struct("RedisSignerRepository")
153 .field("key_prefix", &self.key_prefix)
154 .finish()
155 }
156}
157
158#[async_trait]
159impl Repository<SignerRepoModel, String> for RedisSignerRepository {
160 async fn create(&self, signer: SignerRepoModel) -> Result<SignerRepoModel, RepositoryError> {
161 if signer.id.is_empty() {
162 return Err(RepositoryError::InvalidData(
163 "Signer ID cannot be empty".to_string(),
164 ));
165 }
166
167 let key = self.signer_key(&signer.id);
168 let mut conn = self.client.as_ref().clone();
169
170 let exists: Result<bool, RedisError> = conn.exists(&key).await;
172 match exists {
173 Ok(true) => {
174 return Err(RepositoryError::ConstraintViolation(format!(
175 "Signer with ID {} already exists",
176 signer.id
177 )));
178 }
179 Ok(false) => {
180 }
182 Err(e) => {
183 error!("Failed to check if signer exists: {}", e);
184 return Err(RepositoryError::Other(format!(
185 "Failed to check signer existence: {}",
186 e
187 )));
188 }
189 }
190
191 let serialized = self.serialize_entity(&signer, |s| &s.id, "signer")?;
193
194 let result: Result<(), RedisError> = conn.set(&key, &serialized).await;
196 result.map_err(|e| {
197 error!("Failed to store signer {}: {}", signer.id, e);
198 RepositoryError::Other(format!("Failed to store signer: {}", e))
199 })?;
200
201 self.add_to_list(&signer.id).await?;
203
204 debug!("Created signer with ID: {}", signer.id);
205 Ok(signer)
206 }
207
208 async fn get_by_id(&self, id: String) -> Result<SignerRepoModel, RepositoryError> {
209 if id.is_empty() {
210 return Err(RepositoryError::InvalidData(
211 "Signer ID cannot be empty".to_string(),
212 ));
213 }
214
215 let key = self.signer_key(&id);
216 let mut conn = self.client.as_ref().clone();
217
218 let result: Result<Option<String>, RedisError> = conn.get(&key).await;
219 match result {
220 Ok(Some(data)) => {
221 let signer = self.deserialize_entity::<SignerRepoModel>(&data, &id, "signer")?;
223 debug!("Retrieved signer with ID: {}", id);
224 Ok(signer)
225 }
226 Ok(None) => {
227 debug!("Signer with ID {} not found", id);
228 Err(RepositoryError::NotFound(format!(
229 "Signer with ID {} not found",
230 id
231 )))
232 }
233 Err(e) => {
234 error!("Failed to retrieve signer {}: {}", id, e);
235 Err(RepositoryError::Other(format!(
236 "Failed to retrieve signer: {}",
237 e
238 )))
239 }
240 }
241 }
242
243 async fn update(
244 &self,
245 id: String,
246 signer: SignerRepoModel,
247 ) -> Result<SignerRepoModel, RepositoryError> {
248 if id.is_empty() {
249 return Err(RepositoryError::InvalidData(
250 "Signer ID cannot be empty".to_string(),
251 ));
252 }
253
254 if signer.id != id {
255 return Err(RepositoryError::InvalidData(
256 "Signer ID in data does not match provided ID".to_string(),
257 ));
258 }
259
260 let key = self.signer_key(&id);
261 let mut conn = self.client.as_ref().clone();
262
263 let exists: Result<bool, RedisError> = conn.exists(&key).await;
265 match exists {
266 Ok(false) => {
267 return Err(RepositoryError::NotFound(format!(
268 "Signer with ID {} not found",
269 id
270 )));
271 }
272 Ok(true) => {
273 }
275 Err(e) => {
276 error!("Failed to check if signer exists: {}", e);
277 return Err(RepositoryError::Other(format!(
278 "Failed to check signer existence: {}",
279 e
280 )));
281 }
282 }
283
284 let serialized = self.serialize_entity(&signer, |s| &s.id, "signer")?;
286
287 let result: Result<(), RedisError> = conn.set(&key, &serialized).await;
289 result.map_err(|e| {
290 error!("Failed to update signer {}: {}", id, e);
291 RepositoryError::Other(format!("Failed to update signer: {}", e))
292 })?;
293
294 debug!("Updated signer with ID: {}", id);
295 Ok(signer)
296 }
297
298 async fn delete_by_id(&self, id: String) -> Result<(), RepositoryError> {
299 if id.is_empty() {
300 return Err(RepositoryError::InvalidData(
301 "Signer ID cannot be empty".to_string(),
302 ));
303 }
304
305 let key = self.signer_key(&id);
306 let mut conn = self.client.as_ref().clone();
307
308 let exists: Result<bool, RedisError> = conn.exists(&key).await;
310 match exists {
311 Ok(false) => {
312 return Err(RepositoryError::NotFound(format!(
313 "Signer with ID {} not found",
314 id
315 )));
316 }
317 Ok(true) => {
318 }
320 Err(e) => {
321 error!("Failed to check if signer exists: {}", e);
322 return Err(RepositoryError::Other(format!(
323 "Failed to check signer existence: {}",
324 e
325 )));
326 }
327 }
328
329 let result: Result<i64, RedisError> = conn.del(&key).await;
331 result.map_err(|e| {
332 error!("Failed to delete signer {}: {}", id, e);
333 RepositoryError::Other(format!("Failed to delete signer: {}", e))
334 })?;
335
336 self.remove_from_list(&id).await?;
338
339 debug!("Deleted signer with ID: {}", id);
340 Ok(())
341 }
342
343 async fn list_all(&self) -> Result<Vec<SignerRepoModel>, RepositoryError> {
344 let ids = self.get_all_ids().await?;
345
346 if ids.is_empty() {
347 debug!("No signers found");
348 return Ok(Vec::new());
349 }
350
351 let signers = self.get_signers_by_ids(&ids).await?;
352 debug!("Successfully fetched {} signers", signers.results.len());
353 Ok(signers.results)
354 }
355
356 async fn list_paginated(
357 &self,
358 query: PaginationQuery,
359 ) -> Result<PaginatedResult<SignerRepoModel>, RepositoryError> {
360 if query.per_page == 0 {
361 return Err(RepositoryError::InvalidData(
362 "per_page must be greater than 0".to_string(),
363 ));
364 }
365
366 debug!(
367 "Listing paginated signers: page {}, per_page {}",
368 query.page, query.per_page
369 );
370
371 let all_ids: Vec<String> = self.get_all_ids().await?;
372 let total = all_ids.len() as u64;
373 let per_page = query.per_page as usize;
374 let page = query.page as usize;
375 let total_pages = all_ids.len().div_ceil(per_page);
376
377 if page > total_pages && !all_ids.is_empty() {
378 debug!(
379 "Requested page {} exceeds total pages {}",
380 page, total_pages
381 );
382 return Ok(PaginatedResult {
383 items: Vec::new(),
384 total,
385 page: query.page,
386 per_page: query.per_page,
387 });
388 }
389
390 let start_idx = (page - 1) * per_page;
391 let end_idx = std::cmp::min(start_idx + per_page, all_ids.len());
392
393 let page_ids = all_ids[start_idx..end_idx].to_vec();
394 let signers = self.get_signers_by_ids(&page_ids).await?;
395
396 debug!(
397 "Successfully retrieved {} signers for page {}",
398 signers.results.len(),
399 query.page
400 );
401 Ok(PaginatedResult {
402 items: signers.results.clone(),
403 total,
404 page: query.page,
405 per_page: query.per_page,
406 })
407 }
408
409 async fn count(&self) -> Result<usize, RepositoryError> {
410 let ids = self.get_all_ids().await?;
411 Ok(ids.len())
412 }
413
414 async fn has_entries(&self) -> Result<bool, RepositoryError> {
415 let mut conn = self.client.as_ref().clone();
416 let signer_list_key = self.signer_list_key();
417
418 debug!("Checking if signer entries exist");
419
420 let exists: bool = conn
421 .exists(&signer_list_key)
422 .await
423 .map_err(|e| self.map_redis_error(e, "has_entries_check"))?;
424
425 debug!("Signer entries exist: {}", exists);
426 Ok(exists)
427 }
428
429 async fn drop_all_entries(&self) -> Result<(), RepositoryError> {
430 let mut conn = self.client.as_ref().clone();
431 let signer_list_key = self.signer_list_key();
432
433 debug!("Dropping all signer entries");
434
435 let signer_ids: Vec<String> = conn
437 .smembers(&signer_list_key)
438 .await
439 .map_err(|e| self.map_redis_error(e, "drop_all_entries_get_ids"))?;
440
441 if signer_ids.is_empty() {
442 debug!("No signer entries to drop");
443 return Ok(());
444 }
445
446 let mut pipe = redis::pipe();
448 pipe.atomic();
449
450 for signer_id in &signer_ids {
452 let signer_key = self.signer_key(signer_id);
453 pipe.del(&signer_key);
454 }
455
456 pipe.del(&signer_list_key);
458
459 pipe.exec_async(&mut conn)
460 .await
461 .map_err(|e| self.map_redis_error(e, "drop_all_entries_pipeline"))?;
462
463 debug!("Dropped {} signer entries", signer_ids.len());
464 Ok(())
465 }
466}
467
468#[cfg(test)]
469mod tests {
470 use super::*;
471 use crate::models::{LocalSignerConfigStorage, SignerConfigStorage};
472 use secrets::SecretVec;
473 use std::sync::Arc;
474
475 fn create_local_signer(id: &str) -> SignerRepoModel {
476 SignerRepoModel {
477 id: id.to_string(),
478 config: SignerConfigStorage::Local(LocalSignerConfigStorage {
479 raw_key: SecretVec::new(32, |v| v.copy_from_slice(&[1; 32])),
480 }),
481 }
482 }
483
484 async fn setup_test_repo() -> RedisSignerRepository {
485 let client =
486 redis::Client::open("redis://127.0.0.1:6379/").expect("Failed to create Redis client");
487 let connection_manager = redis::aio::ConnectionManager::new(client)
488 .await
489 .expect("Failed to create connection manager");
490
491 RedisSignerRepository::new(Arc::new(connection_manager), "test".to_string())
492 .expect("Failed to create repository")
493 }
494
495 #[tokio::test]
496 #[ignore = "Requires active Redis instance"]
497 async fn test_new_repository_creation() {
498 let repo = setup_test_repo().await;
499 assert_eq!(repo.key_prefix, "test");
500 }
501
502 #[tokio::test]
503 #[ignore = "Requires active Redis instance"]
504 async fn test_new_repository_empty_prefix_fails() {
505 let client =
506 redis::Client::open("redis://127.0.0.1:6379/").expect("Failed to create Redis client");
507 let connection_manager = redis::aio::ConnectionManager::new(client)
508 .await
509 .expect("Failed to create connection manager");
510
511 let result = RedisSignerRepository::new(Arc::new(connection_manager), "".to_string());
512 assert!(result.is_err());
513 assert!(result
514 .unwrap_err()
515 .to_string()
516 .contains("key prefix cannot be empty"));
517 }
518
519 #[tokio::test]
520 #[ignore = "Requires active Redis instance"]
521 async fn test_key_generation() {
522 let repo = setup_test_repo().await;
523 let signer_key = repo.signer_key("test-id");
524 let list_key = repo.signer_list_key();
525
526 assert_eq!(signer_key, "test:signer:test-id");
527 assert_eq!(list_key, "test:signer_list");
528 }
529
530 #[tokio::test]
531 #[ignore = "Requires active Redis instance"]
532 async fn test_serialize_deserialize_signer() {
533 let repo = setup_test_repo().await;
534 let signer = create_local_signer("test-signer");
535
536 let serialized = repo.serialize_entity(&signer, |s| &s.id, "signer").unwrap();
537 let deserialized: SignerRepoModel = repo
538 .deserialize_entity(&serialized, &signer.id, "signer")
539 .unwrap();
540
541 assert_eq!(signer.id, deserialized.id);
542 assert!(matches!(signer.config, SignerConfigStorage::Local(_)));
543 assert!(matches!(deserialized.config, SignerConfigStorage::Local(_)));
544 }
545
546 #[tokio::test]
547 #[ignore = "Requires active Redis instance"]
548 async fn test_create_signer() {
549 let repo = setup_test_repo().await;
550 let signer_name = uuid::Uuid::new_v4().to_string();
551 let signer = create_local_signer(&signer_name);
552
553 let result = repo.create(signer).await;
554 assert!(result.is_ok());
555
556 let created_signer = result.unwrap();
557 assert_eq!(created_signer.id, signer_name);
558 }
559
560 #[tokio::test]
561 #[ignore = "Requires active Redis instance"]
562 async fn test_get_signer() {
563 let repo = setup_test_repo().await;
564 let signer_name = uuid::Uuid::new_v4().to_string();
565 let signer = create_local_signer(&signer_name);
566
567 repo.create(signer.clone()).await.unwrap();
569
570 let retrieved = repo.get_by_id(signer_name.clone()).await.unwrap();
572 assert_eq!(retrieved.id, signer.id);
573 assert!(matches!(retrieved.config, SignerConfigStorage::Local(_)));
574 }
575
576 #[tokio::test]
577 #[ignore = "Requires active Redis instance"]
578 async fn test_get_nonexistent_signer() {
579 let repo = setup_test_repo().await;
580 let result = repo.get_by_id("nonexistent-id".to_string()).await;
581
582 assert!(result.is_err());
583 assert!(matches!(result.unwrap_err(), RepositoryError::NotFound(_)));
584 }
585
586 #[tokio::test]
587 #[ignore = "Requires active Redis instance"]
588 async fn test_update_signer() {
589 let repo = setup_test_repo().await;
590 let signer_name = uuid::Uuid::new_v4().to_string();
591 let signer = create_local_signer(&signer_name);
592
593 repo.create(signer.clone()).await.unwrap();
595
596 let updated_signer = SignerRepoModel {
598 id: signer_name.clone(),
599 config: SignerConfigStorage::Local(LocalSignerConfigStorage {
600 raw_key: SecretVec::new(32, |v| v.copy_from_slice(&[2; 32])),
601 }),
602 };
603
604 let result = repo.update(signer_name.clone(), updated_signer).await;
605 assert!(result.is_ok());
606
607 let retrieved = repo.get_by_id(signer_name).await.unwrap();
609 assert!(matches!(retrieved.config, SignerConfigStorage::Local(_)));
610 }
611
612 #[tokio::test]
613 #[ignore = "Requires active Redis instance"]
614 async fn test_delete_signer() {
615 let repo = setup_test_repo().await;
616 let signer_name = uuid::Uuid::new_v4().to_string();
617 let signer = create_local_signer(&signer_name);
618
619 repo.create(signer).await.unwrap();
621
622 let result = repo.delete_by_id(signer_name.clone()).await;
624 assert!(result.is_ok());
625
626 let get_result = repo.get_by_id(signer_name).await;
628 assert!(get_result.is_err());
629 assert!(matches!(
630 get_result.unwrap_err(),
631 RepositoryError::NotFound(_)
632 ));
633 }
634
635 #[tokio::test]
636 #[ignore = "Requires active Redis instance"]
637 async fn test_list_all_signers() {
638 let repo = setup_test_repo().await;
639 let signer1_name = uuid::Uuid::new_v4().to_string();
640 let signer2_name = uuid::Uuid::new_v4().to_string();
641 let signer1 = create_local_signer(&signer1_name);
642 let signer2 = create_local_signer(&signer2_name);
643
644 repo.create(signer1).await.unwrap();
646 repo.create(signer2).await.unwrap();
647
648 let signers = repo.list_all().await.unwrap();
650 assert!(signers.len() >= 2);
651
652 let ids: Vec<String> = signers.iter().map(|s| s.id.clone()).collect();
653 assert!(ids.contains(&signer1_name));
654 assert!(ids.contains(&signer2_name));
655 }
656
657 #[tokio::test]
658 #[ignore = "Requires active Redis instance"]
659 async fn test_count_signers() {
660 let repo = setup_test_repo().await;
661 let initial_count = repo.count().await.unwrap();
662
663 let signer_name = uuid::Uuid::new_v4().to_string();
664 let signer = create_local_signer(&signer_name);
665
666 repo.create(signer).await.unwrap();
668
669 let new_count = repo.count().await.unwrap();
671 assert!(new_count > initial_count);
672 }
673
674 #[tokio::test]
675 #[ignore = "Requires active Redis instance"]
676 async fn test_list_paginated_signers() {
677 let repo = setup_test_repo().await;
678 let signer1_name = uuid::Uuid::new_v4().to_string();
679 let signer2_name = uuid::Uuid::new_v4().to_string();
680 let signer1 = create_local_signer(&signer1_name);
681 let signer2 = create_local_signer(&signer2_name);
682
683 repo.create(signer1).await.unwrap();
685 repo.create(signer2).await.unwrap();
686
687 let query = PaginationQuery {
689 page: 1,
690 per_page: 1,
691 };
692
693 let result = repo.list_paginated(query).await.unwrap();
694 assert_eq!(result.items.len(), 1);
695 assert!(result.total >= 2);
696 assert_eq!(result.page, 1);
697 assert_eq!(result.per_page, 1);
698 }
699
700 #[tokio::test]
701 #[ignore = "Requires active Redis instance"]
702 async fn test_duplicate_signer_creation() {
703 let repo = setup_test_repo().await;
704 let signer_name = uuid::Uuid::new_v4().to_string();
705 let signer = create_local_signer(&signer_name);
706
707 repo.create(signer.clone()).await.unwrap();
709
710 let result = repo.create(signer).await;
712 assert!(result.is_err());
713 assert!(matches!(
714 result.unwrap_err(),
715 RepositoryError::ConstraintViolation(_)
716 ));
717 }
718
719 #[tokio::test]
720 #[ignore = "Requires active Redis instance"]
721 async fn test_debug_implementation() {
722 let repo = setup_test_repo().await;
723 let debug_str = format!("{:?}", repo);
724 assert!(debug_str.contains("RedisSignerRepository"));
725 assert!(debug_str.contains("test"));
726 }
727
728 #[tokio::test]
729 #[ignore = "Requires active Redis instance"]
730 async fn test_error_handling_empty_id() {
731 let repo = setup_test_repo().await;
732
733 let result = repo.get_by_id("".to_string()).await;
734 assert!(result.is_err());
735 assert!(result
736 .unwrap_err()
737 .to_string()
738 .contains("ID cannot be empty"));
739 }
740
741 #[tokio::test]
742 #[ignore = "Requires active Redis instance"]
743 async fn test_create_signer_with_empty_id() {
744 let repo = setup_test_repo().await;
745 let signer = SignerRepoModel {
746 id: "".to_string(),
747 config: SignerConfigStorage::Local(LocalSignerConfigStorage {
748 raw_key: SecretVec::new(32, |v| v.copy_from_slice(&[1; 32])),
749 }),
750 };
751
752 let result = repo.create(signer).await;
753 assert!(result.is_err());
754 assert!(result
755 .unwrap_err()
756 .to_string()
757 .contains("ID cannot be empty"));
758 }
759
760 #[tokio::test]
761 #[ignore = "Requires active Redis instance"]
762 async fn test_update_nonexistent_signer() {
763 let repo = setup_test_repo().await;
764 let signer = create_local_signer("nonexistent-id");
765
766 let result = repo.update("nonexistent-id".to_string(), signer).await;
767 assert!(result.is_err());
768 assert!(matches!(result.unwrap_err(), RepositoryError::NotFound(_)));
769 }
770
771 #[tokio::test]
772 #[ignore = "Requires active Redis instance"]
773 async fn test_delete_nonexistent_signer() {
774 let repo = setup_test_repo().await;
775
776 let result = repo.delete_by_id("nonexistent-id".to_string()).await;
777 assert!(result.is_err());
778 assert!(matches!(result.unwrap_err(), RepositoryError::NotFound(_)));
779 }
780
781 #[tokio::test]
782 #[ignore = "Requires active Redis instance"]
783 async fn test_update_with_mismatched_id() {
784 let repo = setup_test_repo().await;
785 let signer_name = uuid::Uuid::new_v4().to_string();
786 let signer = create_local_signer(&signer_name);
787
788 repo.create(signer).await.unwrap();
790
791 let updated_signer = create_local_signer("different-id");
793 let result = repo.update(signer_name, updated_signer).await;
794 assert!(result.is_err());
795 assert!(result
796 .unwrap_err()
797 .to_string()
798 .contains("ID in data does not match"));
799 }
800
801 #[tokio::test]
802 #[ignore = "Requires active Redis instance"]
803 async fn test_has_entries() {
804 let repo = setup_test_repo().await;
805
806 let signer_id = uuid::Uuid::new_v4().to_string();
807 let signer = create_local_signer(&signer_id);
808 repo.create(signer.clone()).await.unwrap();
809 assert!(repo.has_entries().await.unwrap());
810 }
811
812 #[tokio::test]
813 #[ignore = "Requires active Redis instance"]
814 async fn test_drop_all_entries() {
815 let repo = setup_test_repo().await;
816 let signer_id = uuid::Uuid::new_v4().to_string();
817 let signer = create_local_signer(&signer_id);
818
819 repo.create(signer.clone()).await.unwrap();
820 assert!(repo.has_entries().await.unwrap());
821
822 repo.drop_all_entries().await.unwrap();
823 assert!(!repo.has_entries().await.unwrap());
824 }
825}