1use crate::models::{RepositoryError, SignerRepoModel};
4use crate::repositories::redis_base::RedisRepository;
5use crate::repositories::*;
6use async_trait::async_trait;
7use redis::aio::ConnectionManager;
8use redis::{AsyncCommands, RedisError};
9use std::fmt;
10use std::sync::Arc;
11use tracing::{debug, error, warn};
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!(signer_id = %id, error = %e, "failed to add signer to list");
56 RepositoryError::Other(format!("Failed to add signer to list: {}", e))
57 })?;
58
59 debug!(signer_id = %id, "added signer to list");
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!(signer_id = %id, error = %e, "failed to remove signer from list");
70 RepositoryError::Other(format!("Failed to remove signer from list: {}", e))
71 })?;
72
73 debug!(signer_id = %id, "removed signer from list");
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!(error = %e, "failed to get signer IDs");
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!(count = ids.len(), "batch fetching signers");
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!(signer_id = %ids[i], error = %e, "failed to deserialize signer");
123 failed_ids.push(ids[i].clone());
124 }
125 }
126 }
127 None => {
128 warn!(signer_id = %ids[i], "signer not found in batch fetch");
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_ids = ?failed_ids, "failed to deserialize signers");
140 }
141
142 debug!(count = signers.len(), "successfully fetched signers");
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!(error = %e, "failed to check if signer exists");
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!(signer_id = %signer.id, error = %e, "failed to store signer");
198 RepositoryError::Other(format!("Failed to store signer: {}", e))
199 })?;
200
201 self.add_to_list(&signer.id).await?;
203
204 debug!(signer_id = %signer.id, "created signer");
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!(signer_id = %id, "retrieved signer");
224 Ok(signer)
225 }
226 Ok(None) => {
227 debug!(signer_id = %id, "signer not found");
228 Err(RepositoryError::NotFound(format!(
229 "Signer with ID {} not found",
230 id
231 )))
232 }
233 Err(e) => {
234 error!(signer_id = %id, error = %e, "failed to retrieve signer");
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!(error = %e, "failed to check if signer exists");
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!(signer_id = %id, error = %e, "failed to update signer");
291 RepositoryError::Other(format!("Failed to update signer: {}", e))
292 })?;
293
294 debug!(signer_id = %id, "updated signer");
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!(error = %e, "failed to check if signer exists");
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!(signer_id = %id, error = %e, "failed to delete signer");
333 RepositoryError::Other(format!("Failed to delete signer: {}", e))
334 })?;
335
336 self.remove_from_list(&id).await?;
338
339 debug!(signer_id = %id, "deleted signer");
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!(
353 count = signers.results.len(),
354 "successfully fetched signers"
355 );
356 Ok(signers.results)
357 }
358
359 async fn list_paginated(
360 &self,
361 query: PaginationQuery,
362 ) -> Result<PaginatedResult<SignerRepoModel>, RepositoryError> {
363 if query.per_page == 0 {
364 return Err(RepositoryError::InvalidData(
365 "per_page must be greater than 0".to_string(),
366 ));
367 }
368
369 debug!(
370 "Listing paginated signers: page {}, per_page {}",
371 query.page, query.per_page
372 );
373
374 let all_ids: Vec<String> = self.get_all_ids().await?;
375 let total = all_ids.len() as u64;
376 let per_page = query.per_page as usize;
377 let page = query.page as usize;
378 let total_pages = all_ids.len().div_ceil(per_page);
379
380 if page > total_pages && !all_ids.is_empty() {
381 debug!(
382 "Requested page {} exceeds total pages {}",
383 page, total_pages
384 );
385 return Ok(PaginatedResult {
386 items: Vec::new(),
387 total,
388 page: query.page,
389 per_page: query.per_page,
390 });
391 }
392
393 let start_idx = (page - 1) * per_page;
394 let end_idx = std::cmp::min(start_idx + per_page, all_ids.len());
395
396 let page_ids = all_ids[start_idx..end_idx].to_vec();
397 let signers = self.get_signers_by_ids(&page_ids).await?;
398
399 debug!(
400 "Successfully retrieved {} signers for page {}",
401 signers.results.len(),
402 query.page
403 );
404 Ok(PaginatedResult {
405 items: signers.results.clone(),
406 total,
407 page: query.page,
408 per_page: query.per_page,
409 })
410 }
411
412 async fn count(&self) -> Result<usize, RepositoryError> {
413 let ids = self.get_all_ids().await?;
414 Ok(ids.len())
415 }
416
417 async fn has_entries(&self) -> Result<bool, RepositoryError> {
418 let mut conn = self.client.as_ref().clone();
419 let signer_list_key = self.signer_list_key();
420
421 debug!("Checking if signer entries exist");
422
423 let exists: bool = conn
424 .exists(&signer_list_key)
425 .await
426 .map_err(|e| self.map_redis_error(e, "has_entries_check"))?;
427
428 debug!(exists = %exists, "signer entries exist");
429 Ok(exists)
430 }
431
432 async fn drop_all_entries(&self) -> Result<(), RepositoryError> {
433 let mut conn = self.client.as_ref().clone();
434 let signer_list_key = self.signer_list_key();
435
436 debug!("Dropping all signer entries");
437
438 let signer_ids: Vec<String> = conn
440 .smembers(&signer_list_key)
441 .await
442 .map_err(|e| self.map_redis_error(e, "drop_all_entries_get_ids"))?;
443
444 if signer_ids.is_empty() {
445 debug!("No signer entries to drop");
446 return Ok(());
447 }
448
449 let mut pipe = redis::pipe();
451 pipe.atomic();
452
453 for signer_id in &signer_ids {
455 let signer_key = self.signer_key(signer_id);
456 pipe.del(&signer_key);
457 }
458
459 pipe.del(&signer_list_key);
461
462 pipe.exec_async(&mut conn)
463 .await
464 .map_err(|e| self.map_redis_error(e, "drop_all_entries_pipeline"))?;
465
466 debug!(count = %signer_ids.len(), "dropped signer entries");
467 Ok(())
468 }
469}
470
471#[cfg(test)]
472mod tests {
473 use super::*;
474 use crate::models::{LocalSignerConfigStorage, SignerConfigStorage};
475 use secrets::SecretVec;
476 use std::sync::Arc;
477
478 fn create_local_signer(id: &str) -> SignerRepoModel {
479 SignerRepoModel {
480 id: id.to_string(),
481 config: SignerConfigStorage::Local(LocalSignerConfigStorage {
482 raw_key: SecretVec::new(32, |v| v.copy_from_slice(&[1; 32])),
483 }),
484 }
485 }
486
487 async fn setup_test_repo() -> RedisSignerRepository {
488 let client =
489 redis::Client::open("redis://127.0.0.1:6379/").expect("Failed to create Redis client");
490 let connection_manager = redis::aio::ConnectionManager::new(client)
491 .await
492 .expect("Failed to create connection manager");
493
494 RedisSignerRepository::new(Arc::new(connection_manager), "test".to_string())
495 .expect("Failed to create repository")
496 }
497
498 #[tokio::test]
499 #[ignore = "Requires active Redis instance"]
500 async fn test_new_repository_creation() {
501 let repo = setup_test_repo().await;
502 assert_eq!(repo.key_prefix, "test");
503 }
504
505 #[tokio::test]
506 #[ignore = "Requires active Redis instance"]
507 async fn test_new_repository_empty_prefix_fails() {
508 let client =
509 redis::Client::open("redis://127.0.0.1:6379/").expect("Failed to create Redis client");
510 let connection_manager = redis::aio::ConnectionManager::new(client)
511 .await
512 .expect("Failed to create connection manager");
513
514 let result = RedisSignerRepository::new(Arc::new(connection_manager), "".to_string());
515 assert!(result.is_err());
516 assert!(result
517 .unwrap_err()
518 .to_string()
519 .contains("key prefix cannot be empty"));
520 }
521
522 #[tokio::test]
523 #[ignore = "Requires active Redis instance"]
524 async fn test_key_generation() {
525 let repo = setup_test_repo().await;
526 let signer_key = repo.signer_key("test-id");
527 let list_key = repo.signer_list_key();
528
529 assert_eq!(signer_key, "test:signer:test-id");
530 assert_eq!(list_key, "test:signer_list");
531 }
532
533 #[tokio::test]
534 #[ignore = "Requires active Redis instance"]
535 async fn test_serialize_deserialize_signer() {
536 let repo = setup_test_repo().await;
537 let signer = create_local_signer("test-signer");
538
539 let serialized = repo.serialize_entity(&signer, |s| &s.id, "signer").unwrap();
540 let deserialized: SignerRepoModel = repo
541 .deserialize_entity(&serialized, &signer.id, "signer")
542 .unwrap();
543
544 assert_eq!(signer.id, deserialized.id);
545 assert!(matches!(signer.config, SignerConfigStorage::Local(_)));
546 assert!(matches!(deserialized.config, SignerConfigStorage::Local(_)));
547 }
548
549 #[tokio::test]
550 #[ignore = "Requires active Redis instance"]
551 async fn test_create_signer() {
552 let repo = setup_test_repo().await;
553 let signer_name = uuid::Uuid::new_v4().to_string();
554 let signer = create_local_signer(&signer_name);
555
556 let result = repo.create(signer).await;
557 assert!(result.is_ok());
558
559 let created_signer = result.unwrap();
560 assert_eq!(created_signer.id, signer_name);
561 }
562
563 #[tokio::test]
564 #[ignore = "Requires active Redis instance"]
565 async fn test_get_signer() {
566 let repo = setup_test_repo().await;
567 let signer_name = uuid::Uuid::new_v4().to_string();
568 let signer = create_local_signer(&signer_name);
569
570 repo.create(signer.clone()).await.unwrap();
572
573 let retrieved = repo.get_by_id(signer_name.clone()).await.unwrap();
575 assert_eq!(retrieved.id, signer.id);
576 assert!(matches!(retrieved.config, SignerConfigStorage::Local(_)));
577 }
578
579 #[tokio::test]
580 #[ignore = "Requires active Redis instance"]
581 async fn test_get_nonexistent_signer() {
582 let repo = setup_test_repo().await;
583 let result = repo.get_by_id("nonexistent-id".to_string()).await;
584
585 assert!(result.is_err());
586 assert!(matches!(result.unwrap_err(), RepositoryError::NotFound(_)));
587 }
588
589 #[tokio::test]
590 #[ignore = "Requires active Redis instance"]
591 async fn test_update_signer() {
592 let repo = setup_test_repo().await;
593 let signer_name = uuid::Uuid::new_v4().to_string();
594 let signer = create_local_signer(&signer_name);
595
596 repo.create(signer.clone()).await.unwrap();
598
599 let updated_signer = SignerRepoModel {
601 id: signer_name.clone(),
602 config: SignerConfigStorage::Local(LocalSignerConfigStorage {
603 raw_key: SecretVec::new(32, |v| v.copy_from_slice(&[2; 32])),
604 }),
605 };
606
607 let result = repo.update(signer_name.clone(), updated_signer).await;
608 assert!(result.is_ok());
609
610 let retrieved = repo.get_by_id(signer_name).await.unwrap();
612 assert!(matches!(retrieved.config, SignerConfigStorage::Local(_)));
613 }
614
615 #[tokio::test]
616 #[ignore = "Requires active Redis instance"]
617 async fn test_delete_signer() {
618 let repo = setup_test_repo().await;
619 let signer_name = uuid::Uuid::new_v4().to_string();
620 let signer = create_local_signer(&signer_name);
621
622 repo.create(signer).await.unwrap();
624
625 let result = repo.delete_by_id(signer_name.clone()).await;
627 assert!(result.is_ok());
628
629 let get_result = repo.get_by_id(signer_name).await;
631 assert!(get_result.is_err());
632 assert!(matches!(
633 get_result.unwrap_err(),
634 RepositoryError::NotFound(_)
635 ));
636 }
637
638 #[tokio::test]
639 #[ignore = "Requires active Redis instance"]
640 async fn test_list_all_signers() {
641 let repo = setup_test_repo().await;
642 let signer1_name = uuid::Uuid::new_v4().to_string();
643 let signer2_name = uuid::Uuid::new_v4().to_string();
644 let signer1 = create_local_signer(&signer1_name);
645 let signer2 = create_local_signer(&signer2_name);
646
647 repo.create(signer1).await.unwrap();
649 repo.create(signer2).await.unwrap();
650
651 let signers = repo.list_all().await.unwrap();
653 assert!(signers.len() >= 2);
654
655 let ids: Vec<String> = signers.iter().map(|s| s.id.clone()).collect();
656 assert!(ids.contains(&signer1_name));
657 assert!(ids.contains(&signer2_name));
658 }
659
660 #[tokio::test]
661 #[ignore = "Requires active Redis instance"]
662 async fn test_count_signers() {
663 let repo = setup_test_repo().await;
664 let initial_count = repo.count().await.unwrap();
665
666 let signer_name = uuid::Uuid::new_v4().to_string();
667 let signer = create_local_signer(&signer_name);
668
669 repo.create(signer).await.unwrap();
671
672 let new_count = repo.count().await.unwrap();
674 assert!(new_count > initial_count);
675 }
676
677 #[tokio::test]
678 #[ignore = "Requires active Redis instance"]
679 async fn test_list_paginated_signers() {
680 let repo = setup_test_repo().await;
681 let signer1_name = uuid::Uuid::new_v4().to_string();
682 let signer2_name = uuid::Uuid::new_v4().to_string();
683 let signer1 = create_local_signer(&signer1_name);
684 let signer2 = create_local_signer(&signer2_name);
685
686 repo.create(signer1).await.unwrap();
688 repo.create(signer2).await.unwrap();
689
690 let query = PaginationQuery {
692 page: 1,
693 per_page: 1,
694 };
695
696 let result = repo.list_paginated(query).await.unwrap();
697 assert_eq!(result.items.len(), 1);
698 assert!(result.total >= 2);
699 assert_eq!(result.page, 1);
700 assert_eq!(result.per_page, 1);
701 }
702
703 #[tokio::test]
704 #[ignore = "Requires active Redis instance"]
705 async fn test_duplicate_signer_creation() {
706 let repo = setup_test_repo().await;
707 let signer_name = uuid::Uuid::new_v4().to_string();
708 let signer = create_local_signer(&signer_name);
709
710 repo.create(signer.clone()).await.unwrap();
712
713 let result = repo.create(signer).await;
715 assert!(result.is_err());
716 assert!(matches!(
717 result.unwrap_err(),
718 RepositoryError::ConstraintViolation(_)
719 ));
720 }
721
722 #[tokio::test]
723 #[ignore = "Requires active Redis instance"]
724 async fn test_debug_implementation() {
725 let repo = setup_test_repo().await;
726 let debug_str = format!("{:?}", repo);
727 assert!(debug_str.contains("RedisSignerRepository"));
728 assert!(debug_str.contains("test"));
729 }
730
731 #[tokio::test]
732 #[ignore = "Requires active Redis instance"]
733 async fn test_error_handling_empty_id() {
734 let repo = setup_test_repo().await;
735
736 let result = repo.get_by_id("".to_string()).await;
737 assert!(result.is_err());
738 assert!(result
739 .unwrap_err()
740 .to_string()
741 .contains("ID cannot be empty"));
742 }
743
744 #[tokio::test]
745 #[ignore = "Requires active Redis instance"]
746 async fn test_create_signer_with_empty_id() {
747 let repo = setup_test_repo().await;
748 let signer = SignerRepoModel {
749 id: "".to_string(),
750 config: SignerConfigStorage::Local(LocalSignerConfigStorage {
751 raw_key: SecretVec::new(32, |v| v.copy_from_slice(&[1; 32])),
752 }),
753 };
754
755 let result = repo.create(signer).await;
756 assert!(result.is_err());
757 assert!(result
758 .unwrap_err()
759 .to_string()
760 .contains("ID cannot be empty"));
761 }
762
763 #[tokio::test]
764 #[ignore = "Requires active Redis instance"]
765 async fn test_update_nonexistent_signer() {
766 let repo = setup_test_repo().await;
767 let signer = create_local_signer("nonexistent-id");
768
769 let result = repo.update("nonexistent-id".to_string(), signer).await;
770 assert!(result.is_err());
771 assert!(matches!(result.unwrap_err(), RepositoryError::NotFound(_)));
772 }
773
774 #[tokio::test]
775 #[ignore = "Requires active Redis instance"]
776 async fn test_delete_nonexistent_signer() {
777 let repo = setup_test_repo().await;
778
779 let result = repo.delete_by_id("nonexistent-id".to_string()).await;
780 assert!(result.is_err());
781 assert!(matches!(result.unwrap_err(), RepositoryError::NotFound(_)));
782 }
783
784 #[tokio::test]
785 #[ignore = "Requires active Redis instance"]
786 async fn test_update_with_mismatched_id() {
787 let repo = setup_test_repo().await;
788 let signer_name = uuid::Uuid::new_v4().to_string();
789 let signer = create_local_signer(&signer_name);
790
791 repo.create(signer).await.unwrap();
793
794 let updated_signer = create_local_signer("different-id");
796 let result = repo.update(signer_name, updated_signer).await;
797 assert!(result.is_err());
798 assert!(result
799 .unwrap_err()
800 .to_string()
801 .contains("ID in data does not match"));
802 }
803
804 #[tokio::test]
805 #[ignore = "Requires active Redis instance"]
806 async fn test_has_entries() {
807 let repo = setup_test_repo().await;
808
809 let signer_id = uuid::Uuid::new_v4().to_string();
810 let signer = create_local_signer(&signer_id);
811 repo.create(signer.clone()).await.unwrap();
812 assert!(repo.has_entries().await.unwrap());
813 }
814
815 #[tokio::test]
816 #[ignore = "Requires active Redis instance"]
817 async fn test_drop_all_entries() {
818 let repo = setup_test_repo().await;
819 let signer_id = uuid::Uuid::new_v4().to_string();
820 let signer = create_local_signer(&signer_id);
821
822 repo.create(signer.clone()).await.unwrap();
823 assert!(repo.has_entries().await.unwrap());
824
825 repo.drop_all_entries().await.unwrap();
826 assert!(!repo.has_entries().await.unwrap());
827 }
828}