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: {e}"
186 )));
187 }
188 }
189
190 let serialized = self.serialize_entity(&signer, |s| &s.id, "signer")?;
192
193 let result: Result<(), RedisError> = conn.set(&key, &serialized).await;
195 result.map_err(|e| {
196 error!(signer_id = %signer.id, error = %e, "failed to store signer");
197 RepositoryError::Other(format!("Failed to store signer: {e}"))
198 })?;
199
200 self.add_to_list(&signer.id).await?;
202
203 debug!(signer_id = %signer.id, "created signer");
204 Ok(signer)
205 }
206
207 async fn get_by_id(&self, id: String) -> Result<SignerRepoModel, RepositoryError> {
208 if id.is_empty() {
209 return Err(RepositoryError::InvalidData(
210 "Signer ID cannot be empty".to_string(),
211 ));
212 }
213
214 let key = self.signer_key(&id);
215 let mut conn = self.client.as_ref().clone();
216
217 let result: Result<Option<String>, RedisError> = conn.get(&key).await;
218 match result {
219 Ok(Some(data)) => {
220 let signer = self.deserialize_entity::<SignerRepoModel>(&data, &id, "signer")?;
222 debug!(signer_id = %id, "retrieved signer");
223 Ok(signer)
224 }
225 Ok(None) => {
226 debug!(signer_id = %id, "signer not found");
227 Err(RepositoryError::NotFound(format!(
228 "Signer with ID {id} not found"
229 )))
230 }
231 Err(e) => {
232 error!(signer_id = %id, error = %e, "failed to retrieve signer");
233 Err(RepositoryError::Other(format!(
234 "Failed to retrieve signer: {e}"
235 )))
236 }
237 }
238 }
239
240 async fn update(
241 &self,
242 id: String,
243 signer: SignerRepoModel,
244 ) -> Result<SignerRepoModel, RepositoryError> {
245 if id.is_empty() {
246 return Err(RepositoryError::InvalidData(
247 "Signer ID cannot be empty".to_string(),
248 ));
249 }
250
251 if signer.id != id {
252 return Err(RepositoryError::InvalidData(
253 "Signer ID in data does not match provided ID".to_string(),
254 ));
255 }
256
257 let key = self.signer_key(&id);
258 let mut conn = self.client.as_ref().clone();
259
260 let exists: Result<bool, RedisError> = conn.exists(&key).await;
262 match exists {
263 Ok(false) => {
264 return Err(RepositoryError::NotFound(format!(
265 "Signer with ID {id} not found"
266 )));
267 }
268 Ok(true) => {
269 }
271 Err(e) => {
272 error!(error = %e, "failed to check if signer exists");
273 return Err(RepositoryError::Other(format!(
274 "Failed to check signer existence: {e}"
275 )));
276 }
277 }
278
279 let serialized = self.serialize_entity(&signer, |s| &s.id, "signer")?;
281
282 let result: Result<(), RedisError> = conn.set(&key, &serialized).await;
284 result.map_err(|e| {
285 error!(signer_id = %id, error = %e, "failed to update signer");
286 RepositoryError::Other(format!("Failed to update signer: {e}"))
287 })?;
288
289 debug!(signer_id = %id, "updated signer");
290 Ok(signer)
291 }
292
293 async fn delete_by_id(&self, id: String) -> Result<(), RepositoryError> {
294 if id.is_empty() {
295 return Err(RepositoryError::InvalidData(
296 "Signer ID cannot be empty".to_string(),
297 ));
298 }
299
300 let key = self.signer_key(&id);
301 let mut conn = self.client.as_ref().clone();
302
303 let exists: Result<bool, RedisError> = conn.exists(&key).await;
305 match exists {
306 Ok(false) => {
307 return Err(RepositoryError::NotFound(format!(
308 "Signer with ID {id} not found"
309 )));
310 }
311 Ok(true) => {
312 }
314 Err(e) => {
315 error!(error = %e, "failed to check if signer exists");
316 return Err(RepositoryError::Other(format!(
317 "Failed to check signer existence: {e}"
318 )));
319 }
320 }
321
322 let result: Result<i64, RedisError> = conn.del(&key).await;
324 result.map_err(|e| {
325 error!(signer_id = %id, error = %e, "failed to delete signer");
326 RepositoryError::Other(format!("Failed to delete signer: {e}"))
327 })?;
328
329 self.remove_from_list(&id).await?;
331
332 debug!(signer_id = %id, "deleted signer");
333 Ok(())
334 }
335
336 async fn list_all(&self) -> Result<Vec<SignerRepoModel>, RepositoryError> {
337 let ids = self.get_all_ids().await?;
338
339 if ids.is_empty() {
340 debug!("No signers found");
341 return Ok(Vec::new());
342 }
343
344 let signers = self.get_signers_by_ids(&ids).await?;
345 debug!(
346 count = signers.results.len(),
347 "successfully fetched signers"
348 );
349 Ok(signers.results)
350 }
351
352 async fn list_paginated(
353 &self,
354 query: PaginationQuery,
355 ) -> Result<PaginatedResult<SignerRepoModel>, RepositoryError> {
356 if query.per_page == 0 {
357 return Err(RepositoryError::InvalidData(
358 "per_page must be greater than 0".to_string(),
359 ));
360 }
361
362 debug!(
363 "Listing paginated signers: page {}, per_page {}",
364 query.page, query.per_page
365 );
366
367 let all_ids: Vec<String> = self.get_all_ids().await?;
368 let total = all_ids.len() as u64;
369 let per_page = query.per_page as usize;
370 let page = query.page as usize;
371 let total_pages = all_ids.len().div_ceil(per_page);
372
373 if page > total_pages && !all_ids.is_empty() {
374 debug!(
375 "Requested page {} exceeds total pages {}",
376 page, total_pages
377 );
378 return Ok(PaginatedResult {
379 items: Vec::new(),
380 total,
381 page: query.page,
382 per_page: query.per_page,
383 });
384 }
385
386 let start_idx = (page - 1) * per_page;
387 let end_idx = std::cmp::min(start_idx + per_page, all_ids.len());
388
389 let page_ids = all_ids[start_idx..end_idx].to_vec();
390 let signers = self.get_signers_by_ids(&page_ids).await?;
391
392 debug!(
393 "Successfully retrieved {} signers for page {}",
394 signers.results.len(),
395 query.page
396 );
397 Ok(PaginatedResult {
398 items: signers.results.clone(),
399 total,
400 page: query.page,
401 per_page: query.per_page,
402 })
403 }
404
405 async fn count(&self) -> Result<usize, RepositoryError> {
406 let ids = self.get_all_ids().await?;
407 Ok(ids.len())
408 }
409
410 async fn has_entries(&self) -> Result<bool, RepositoryError> {
411 let mut conn = self.client.as_ref().clone();
412 let signer_list_key = self.signer_list_key();
413
414 debug!("Checking if signer entries exist");
415
416 let exists: bool = conn
417 .exists(&signer_list_key)
418 .await
419 .map_err(|e| self.map_redis_error(e, "has_entries_check"))?;
420
421 debug!(exists = %exists, "signer entries exist");
422 Ok(exists)
423 }
424
425 async fn drop_all_entries(&self) -> Result<(), RepositoryError> {
426 let mut conn = self.client.as_ref().clone();
427 let signer_list_key = self.signer_list_key();
428
429 debug!("Dropping all signer entries");
430
431 let signer_ids: Vec<String> = conn
433 .smembers(&signer_list_key)
434 .await
435 .map_err(|e| self.map_redis_error(e, "drop_all_entries_get_ids"))?;
436
437 if signer_ids.is_empty() {
438 debug!("No signer entries to drop");
439 return Ok(());
440 }
441
442 let mut pipe = redis::pipe();
444 pipe.atomic();
445
446 for signer_id in &signer_ids {
448 let signer_key = self.signer_key(signer_id);
449 pipe.del(&signer_key);
450 }
451
452 pipe.del(&signer_list_key);
454
455 pipe.exec_async(&mut conn)
456 .await
457 .map_err(|e| self.map_redis_error(e, "drop_all_entries_pipeline"))?;
458
459 debug!(count = %signer_ids.len(), "dropped signer entries");
460 Ok(())
461 }
462}
463
464#[cfg(test)]
465mod tests {
466 use super::*;
467 use crate::models::{LocalSignerConfigStorage, SignerConfigStorage};
468 use secrets::SecretVec;
469 use std::sync::Arc;
470
471 fn create_local_signer(id: &str) -> SignerRepoModel {
472 SignerRepoModel {
473 id: id.to_string(),
474 config: SignerConfigStorage::Local(LocalSignerConfigStorage {
475 raw_key: SecretVec::new(32, |v| v.copy_from_slice(&[1; 32])),
476 }),
477 }
478 }
479
480 async fn setup_test_repo() -> RedisSignerRepository {
481 let client =
482 redis::Client::open("redis://127.0.0.1:6379/").expect("Failed to create Redis client");
483 let connection_manager = redis::aio::ConnectionManager::new(client)
484 .await
485 .expect("Failed to create connection manager");
486
487 RedisSignerRepository::new(Arc::new(connection_manager), "test".to_string())
488 .expect("Failed to create repository")
489 }
490
491 #[tokio::test]
492 #[ignore = "Requires active Redis instance"]
493 async fn test_new_repository_creation() {
494 let repo = setup_test_repo().await;
495 assert_eq!(repo.key_prefix, "test");
496 }
497
498 #[tokio::test]
499 #[ignore = "Requires active Redis instance"]
500 async fn test_new_repository_empty_prefix_fails() {
501 let client =
502 redis::Client::open("redis://127.0.0.1:6379/").expect("Failed to create Redis client");
503 let connection_manager = redis::aio::ConnectionManager::new(client)
504 .await
505 .expect("Failed to create connection manager");
506
507 let result = RedisSignerRepository::new(Arc::new(connection_manager), "".to_string());
508 assert!(result.is_err());
509 assert!(result
510 .unwrap_err()
511 .to_string()
512 .contains("key prefix cannot be empty"));
513 }
514
515 #[tokio::test]
516 #[ignore = "Requires active Redis instance"]
517 async fn test_key_generation() {
518 let repo = setup_test_repo().await;
519 let signer_key = repo.signer_key("test-id");
520 let list_key = repo.signer_list_key();
521
522 assert_eq!(signer_key, "test:signer:test-id");
523 assert_eq!(list_key, "test:signer_list");
524 }
525
526 #[tokio::test]
527 #[ignore = "Requires active Redis instance"]
528 async fn test_serialize_deserialize_signer() {
529 let repo = setup_test_repo().await;
530 let signer = create_local_signer("test-signer");
531
532 let serialized = repo.serialize_entity(&signer, |s| &s.id, "signer").unwrap();
533 let deserialized: SignerRepoModel = repo
534 .deserialize_entity(&serialized, &signer.id, "signer")
535 .unwrap();
536
537 assert_eq!(signer.id, deserialized.id);
538 assert!(matches!(signer.config, SignerConfigStorage::Local(_)));
539 assert!(matches!(deserialized.config, SignerConfigStorage::Local(_)));
540 }
541
542 #[tokio::test]
543 #[ignore = "Requires active Redis instance"]
544 async fn test_create_signer() {
545 let repo = setup_test_repo().await;
546 let signer_name = uuid::Uuid::new_v4().to_string();
547 let signer = create_local_signer(&signer_name);
548
549 let result = repo.create(signer).await;
550 assert!(result.is_ok());
551
552 let created_signer = result.unwrap();
553 assert_eq!(created_signer.id, signer_name);
554 }
555
556 #[tokio::test]
557 #[ignore = "Requires active Redis instance"]
558 async fn test_get_signer() {
559 let repo = setup_test_repo().await;
560 let signer_name = uuid::Uuid::new_v4().to_string();
561 let signer = create_local_signer(&signer_name);
562
563 repo.create(signer.clone()).await.unwrap();
565
566 let retrieved = repo.get_by_id(signer_name.clone()).await.unwrap();
568 assert_eq!(retrieved.id, signer.id);
569 assert!(matches!(retrieved.config, SignerConfigStorage::Local(_)));
570 }
571
572 #[tokio::test]
573 #[ignore = "Requires active Redis instance"]
574 async fn test_get_nonexistent_signer() {
575 let repo = setup_test_repo().await;
576 let result = repo.get_by_id("nonexistent-id".to_string()).await;
577
578 assert!(result.is_err());
579 assert!(matches!(result.unwrap_err(), RepositoryError::NotFound(_)));
580 }
581
582 #[tokio::test]
583 #[ignore = "Requires active Redis instance"]
584 async fn test_update_signer() {
585 let repo = setup_test_repo().await;
586 let signer_name = uuid::Uuid::new_v4().to_string();
587 let signer = create_local_signer(&signer_name);
588
589 repo.create(signer.clone()).await.unwrap();
591
592 let updated_signer = SignerRepoModel {
594 id: signer_name.clone(),
595 config: SignerConfigStorage::Local(LocalSignerConfigStorage {
596 raw_key: SecretVec::new(32, |v| v.copy_from_slice(&[2; 32])),
597 }),
598 };
599
600 let result = repo.update(signer_name.clone(), updated_signer).await;
601 assert!(result.is_ok());
602
603 let retrieved = repo.get_by_id(signer_name).await.unwrap();
605 assert!(matches!(retrieved.config, SignerConfigStorage::Local(_)));
606 }
607
608 #[tokio::test]
609 #[ignore = "Requires active Redis instance"]
610 async fn test_delete_signer() {
611 let repo = setup_test_repo().await;
612 let signer_name = uuid::Uuid::new_v4().to_string();
613 let signer = create_local_signer(&signer_name);
614
615 repo.create(signer).await.unwrap();
617
618 let result = repo.delete_by_id(signer_name.clone()).await;
620 assert!(result.is_ok());
621
622 let get_result = repo.get_by_id(signer_name).await;
624 assert!(get_result.is_err());
625 assert!(matches!(
626 get_result.unwrap_err(),
627 RepositoryError::NotFound(_)
628 ));
629 }
630
631 #[tokio::test]
632 #[ignore = "Requires active Redis instance"]
633 async fn test_list_all_signers() {
634 let repo = setup_test_repo().await;
635 let signer1_name = uuid::Uuid::new_v4().to_string();
636 let signer2_name = uuid::Uuid::new_v4().to_string();
637 let signer1 = create_local_signer(&signer1_name);
638 let signer2 = create_local_signer(&signer2_name);
639
640 repo.create(signer1).await.unwrap();
642 repo.create(signer2).await.unwrap();
643
644 let signers = repo.list_all().await.unwrap();
646 assert!(signers.len() >= 2);
647
648 let ids: Vec<String> = signers.iter().map(|s| s.id.clone()).collect();
649 assert!(ids.contains(&signer1_name));
650 assert!(ids.contains(&signer2_name));
651 }
652
653 #[tokio::test]
654 #[ignore = "Requires active Redis instance"]
655 async fn test_count_signers() {
656 let repo = setup_test_repo().await;
657 let initial_count = repo.count().await.unwrap();
658
659 let signer_name = uuid::Uuid::new_v4().to_string();
660 let signer = create_local_signer(&signer_name);
661
662 repo.create(signer).await.unwrap();
664
665 let new_count = repo.count().await.unwrap();
667 assert!(new_count > initial_count);
668 }
669
670 #[tokio::test]
671 #[ignore = "Requires active Redis instance"]
672 async fn test_list_paginated_signers() {
673 let repo = setup_test_repo().await;
674 let signer1_name = uuid::Uuid::new_v4().to_string();
675 let signer2_name = uuid::Uuid::new_v4().to_string();
676 let signer1 = create_local_signer(&signer1_name);
677 let signer2 = create_local_signer(&signer2_name);
678
679 repo.create(signer1).await.unwrap();
681 repo.create(signer2).await.unwrap();
682
683 let query = PaginationQuery {
685 page: 1,
686 per_page: 1,
687 };
688
689 let result = repo.list_paginated(query).await.unwrap();
690 assert_eq!(result.items.len(), 1);
691 assert!(result.total >= 2);
692 assert_eq!(result.page, 1);
693 assert_eq!(result.per_page, 1);
694 }
695
696 #[tokio::test]
697 #[ignore = "Requires active Redis instance"]
698 async fn test_duplicate_signer_creation() {
699 let repo = setup_test_repo().await;
700 let signer_name = uuid::Uuid::new_v4().to_string();
701 let signer = create_local_signer(&signer_name);
702
703 repo.create(signer.clone()).await.unwrap();
705
706 let result = repo.create(signer).await;
708 assert!(result.is_err());
709 assert!(matches!(
710 result.unwrap_err(),
711 RepositoryError::ConstraintViolation(_)
712 ));
713 }
714
715 #[tokio::test]
716 #[ignore = "Requires active Redis instance"]
717 async fn test_debug_implementation() {
718 let repo = setup_test_repo().await;
719 let debug_str = format!("{:?}", repo);
720 assert!(debug_str.contains("RedisSignerRepository"));
721 assert!(debug_str.contains("test"));
722 }
723
724 #[tokio::test]
725 #[ignore = "Requires active Redis instance"]
726 async fn test_error_handling_empty_id() {
727 let repo = setup_test_repo().await;
728
729 let result = repo.get_by_id("".to_string()).await;
730 assert!(result.is_err());
731 assert!(result
732 .unwrap_err()
733 .to_string()
734 .contains("ID cannot be empty"));
735 }
736
737 #[tokio::test]
738 #[ignore = "Requires active Redis instance"]
739 async fn test_create_signer_with_empty_id() {
740 let repo = setup_test_repo().await;
741 let signer = SignerRepoModel {
742 id: "".to_string(),
743 config: SignerConfigStorage::Local(LocalSignerConfigStorage {
744 raw_key: SecretVec::new(32, |v| v.copy_from_slice(&[1; 32])),
745 }),
746 };
747
748 let result = repo.create(signer).await;
749 assert!(result.is_err());
750 assert!(result
751 .unwrap_err()
752 .to_string()
753 .contains("ID cannot be empty"));
754 }
755
756 #[tokio::test]
757 #[ignore = "Requires active Redis instance"]
758 async fn test_update_nonexistent_signer() {
759 let repo = setup_test_repo().await;
760 let signer = create_local_signer("nonexistent-id");
761
762 let result = repo.update("nonexistent-id".to_string(), signer).await;
763 assert!(result.is_err());
764 assert!(matches!(result.unwrap_err(), RepositoryError::NotFound(_)));
765 }
766
767 #[tokio::test]
768 #[ignore = "Requires active Redis instance"]
769 async fn test_delete_nonexistent_signer() {
770 let repo = setup_test_repo().await;
771
772 let result = repo.delete_by_id("nonexistent-id".to_string()).await;
773 assert!(result.is_err());
774 assert!(matches!(result.unwrap_err(), RepositoryError::NotFound(_)));
775 }
776
777 #[tokio::test]
778 #[ignore = "Requires active Redis instance"]
779 async fn test_update_with_mismatched_id() {
780 let repo = setup_test_repo().await;
781 let signer_name = uuid::Uuid::new_v4().to_string();
782 let signer = create_local_signer(&signer_name);
783
784 repo.create(signer).await.unwrap();
786
787 let updated_signer = create_local_signer("different-id");
789 let result = repo.update(signer_name, updated_signer).await;
790 assert!(result.is_err());
791 assert!(result
792 .unwrap_err()
793 .to_string()
794 .contains("ID in data does not match"));
795 }
796
797 #[tokio::test]
798 #[ignore = "Requires active Redis instance"]
799 async fn test_has_entries() {
800 let repo = setup_test_repo().await;
801
802 let signer_id = uuid::Uuid::new_v4().to_string();
803 let signer = create_local_signer(&signer_id);
804 repo.create(signer.clone()).await.unwrap();
805 assert!(repo.has_entries().await.unwrap());
806 }
807
808 #[tokio::test]
809 #[ignore = "Requires active Redis instance"]
810 async fn test_drop_all_entries() {
811 let repo = setup_test_repo().await;
812 let signer_id = uuid::Uuid::new_v4().to_string();
813 let signer = create_local_signer(&signer_id);
814
815 repo.create(signer.clone()).await.unwrap();
816 assert!(repo.has_entries().await.unwrap());
817
818 repo.drop_all_entries().await.unwrap();
819 assert!(!repo.has_entries().await.unwrap());
820 }
821}