1mod transaction_in_memory;
22mod transaction_redis;
23
24use redis::aio::ConnectionManager;
25pub use transaction_in_memory::*;
26pub use transaction_redis::*;
27
28use crate::{
29 models::{
30 NetworkTransactionData, TransactionRepoModel, TransactionStatus, TransactionUpdateRequest,
31 },
32 repositories::*,
33};
34use async_trait::async_trait;
35use eyre::Result;
36use std::sync::Arc;
37
38#[async_trait]
40pub trait TransactionRepository: Repository<TransactionRepoModel, String> {
41 async fn find_by_relayer_id(
43 &self,
44 relayer_id: &str,
45 query: PaginationQuery,
46 ) -> Result<PaginatedResult<TransactionRepoModel>, RepositoryError>;
47
48 async fn find_by_status(
50 &self,
51 relayer_id: &str,
52 statuses: &[TransactionStatus],
53 ) -> Result<Vec<TransactionRepoModel>, RepositoryError>;
54
55 async fn find_by_nonce(
57 &self,
58 relayer_id: &str,
59 nonce: u64,
60 ) -> Result<Option<TransactionRepoModel>, RepositoryError>;
61
62 async fn update_status(
64 &self,
65 tx_id: String,
66 status: TransactionStatus,
67 ) -> Result<TransactionRepoModel, RepositoryError>;
68
69 async fn partial_update(
71 &self,
72 tx_id: String,
73 update: TransactionUpdateRequest,
74 ) -> Result<TransactionRepoModel, RepositoryError>;
75
76 async fn update_network_data(
78 &self,
79 tx_id: String,
80 network_data: NetworkTransactionData,
81 ) -> Result<TransactionRepoModel, RepositoryError>;
82
83 async fn set_sent_at(
85 &self,
86 tx_id: String,
87 sent_at: String,
88 ) -> Result<TransactionRepoModel, RepositoryError>;
89
90 async fn set_confirmed_at(
92 &self,
93 tx_id: String,
94 confirmed_at: String,
95 ) -> Result<TransactionRepoModel, RepositoryError>;
96}
97
98#[cfg(test)]
99mockall::mock! {
100 pub TransactionRepository {}
101
102 #[async_trait]
103 impl Repository<TransactionRepoModel, String> for TransactionRepository {
104 async fn create(&self, entity: TransactionRepoModel) -> Result<TransactionRepoModel, RepositoryError>;
105 async fn get_by_id(&self, id: String) -> Result<TransactionRepoModel, RepositoryError>;
106 async fn list_all(&self) -> Result<Vec<TransactionRepoModel>, RepositoryError>;
107 async fn list_paginated(&self, query: PaginationQuery) -> Result<PaginatedResult<TransactionRepoModel>, RepositoryError>;
108 async fn update(&self, id: String, entity: TransactionRepoModel) -> Result<TransactionRepoModel, RepositoryError>;
109 async fn delete_by_id(&self, id: String) -> Result<(), RepositoryError>;
110 async fn count(&self) -> Result<usize, RepositoryError>;
111 async fn has_entries(&self) -> Result<bool, RepositoryError>;
112 async fn drop_all_entries(&self) -> Result<(), RepositoryError>;
113 }
114
115 #[async_trait]
116 impl TransactionRepository for TransactionRepository {
117 async fn find_by_relayer_id(&self, relayer_id: &str, query: PaginationQuery) -> Result<PaginatedResult<TransactionRepoModel>, RepositoryError>;
118 async fn find_by_status(&self, relayer_id: &str, statuses: &[TransactionStatus]) -> Result<Vec<TransactionRepoModel>, RepositoryError>;
119 async fn find_by_nonce(&self, relayer_id: &str, nonce: u64) -> Result<Option<TransactionRepoModel>, RepositoryError>;
120 async fn update_status(&self, tx_id: String, status: TransactionStatus) -> Result<TransactionRepoModel, RepositoryError>;
121 async fn partial_update(&self, tx_id: String, update: TransactionUpdateRequest) -> Result<TransactionRepoModel, RepositoryError>;
122 async fn update_network_data(&self, tx_id: String, network_data: NetworkTransactionData) -> Result<TransactionRepoModel, RepositoryError>;
123 async fn set_sent_at(&self, tx_id: String, sent_at: String) -> Result<TransactionRepoModel, RepositoryError>;
124 async fn set_confirmed_at(&self, tx_id: String, confirmed_at: String) -> Result<TransactionRepoModel, RepositoryError>;
125
126 }
127}
128
129#[derive(Debug, Clone)]
131pub enum TransactionRepositoryStorage {
132 InMemory(InMemoryTransactionRepository),
133 Redis(RedisTransactionRepository),
134}
135
136impl TransactionRepositoryStorage {
137 pub fn new_in_memory() -> Self {
138 Self::InMemory(InMemoryTransactionRepository::new())
139 }
140 pub fn new_redis(
141 connection_manager: Arc<ConnectionManager>,
142 key_prefix: String,
143 ) -> Result<Self, RepositoryError> {
144 Ok(Self::Redis(RedisTransactionRepository::new(
145 connection_manager,
146 key_prefix,
147 )?))
148 }
149}
150
151#[async_trait]
152impl TransactionRepository for TransactionRepositoryStorage {
153 async fn find_by_relayer_id(
154 &self,
155 relayer_id: &str,
156 query: PaginationQuery,
157 ) -> Result<PaginatedResult<TransactionRepoModel>, RepositoryError> {
158 match self {
159 TransactionRepositoryStorage::InMemory(repo) => {
160 repo.find_by_relayer_id(relayer_id, query).await
161 }
162 TransactionRepositoryStorage::Redis(repo) => {
163 repo.find_by_relayer_id(relayer_id, query).await
164 }
165 }
166 }
167
168 async fn find_by_status(
169 &self,
170 relayer_id: &str,
171 statuses: &[TransactionStatus],
172 ) -> Result<Vec<TransactionRepoModel>, RepositoryError> {
173 match self {
174 TransactionRepositoryStorage::InMemory(repo) => {
175 repo.find_by_status(relayer_id, statuses).await
176 }
177 TransactionRepositoryStorage::Redis(repo) => {
178 repo.find_by_status(relayer_id, statuses).await
179 }
180 }
181 }
182
183 async fn find_by_nonce(
184 &self,
185 relayer_id: &str,
186 nonce: u64,
187 ) -> Result<Option<TransactionRepoModel>, RepositoryError> {
188 match self {
189 TransactionRepositoryStorage::InMemory(repo) => {
190 repo.find_by_nonce(relayer_id, nonce).await
191 }
192 TransactionRepositoryStorage::Redis(repo) => {
193 repo.find_by_nonce(relayer_id, nonce).await
194 }
195 }
196 }
197
198 async fn update_status(
199 &self,
200 tx_id: String,
201 status: TransactionStatus,
202 ) -> Result<TransactionRepoModel, RepositoryError> {
203 match self {
204 TransactionRepositoryStorage::InMemory(repo) => repo.update_status(tx_id, status).await,
205 TransactionRepositoryStorage::Redis(repo) => repo.update_status(tx_id, status).await,
206 }
207 }
208
209 async fn partial_update(
210 &self,
211 tx_id: String,
212 update: TransactionUpdateRequest,
213 ) -> Result<TransactionRepoModel, RepositoryError> {
214 match self {
215 TransactionRepositoryStorage::InMemory(repo) => {
216 repo.partial_update(tx_id, update).await
217 }
218 TransactionRepositoryStorage::Redis(repo) => repo.partial_update(tx_id, update).await,
219 }
220 }
221
222 async fn update_network_data(
223 &self,
224 tx_id: String,
225 network_data: NetworkTransactionData,
226 ) -> Result<TransactionRepoModel, RepositoryError> {
227 match self {
228 TransactionRepositoryStorage::InMemory(repo) => {
229 repo.update_network_data(tx_id, network_data).await
230 }
231 TransactionRepositoryStorage::Redis(repo) => {
232 repo.update_network_data(tx_id, network_data).await
233 }
234 }
235 }
236
237 async fn set_sent_at(
238 &self,
239 tx_id: String,
240 sent_at: String,
241 ) -> Result<TransactionRepoModel, RepositoryError> {
242 match self {
243 TransactionRepositoryStorage::InMemory(repo) => repo.set_sent_at(tx_id, sent_at).await,
244 TransactionRepositoryStorage::Redis(repo) => repo.set_sent_at(tx_id, sent_at).await,
245 }
246 }
247
248 async fn set_confirmed_at(
249 &self,
250 tx_id: String,
251 confirmed_at: String,
252 ) -> Result<TransactionRepoModel, RepositoryError> {
253 match self {
254 TransactionRepositoryStorage::InMemory(repo) => {
255 repo.set_confirmed_at(tx_id, confirmed_at).await
256 }
257 TransactionRepositoryStorage::Redis(repo) => {
258 repo.set_confirmed_at(tx_id, confirmed_at).await
259 }
260 }
261 }
262}
263
264#[async_trait]
265impl Repository<TransactionRepoModel, String> for TransactionRepositoryStorage {
266 async fn create(
267 &self,
268 entity: TransactionRepoModel,
269 ) -> Result<TransactionRepoModel, RepositoryError> {
270 match self {
271 TransactionRepositoryStorage::InMemory(repo) => repo.create(entity).await,
272 TransactionRepositoryStorage::Redis(repo) => repo.create(entity).await,
273 }
274 }
275
276 async fn get_by_id(&self, id: String) -> Result<TransactionRepoModel, RepositoryError> {
277 match self {
278 TransactionRepositoryStorage::InMemory(repo) => repo.get_by_id(id).await,
279 TransactionRepositoryStorage::Redis(repo) => repo.get_by_id(id).await,
280 }
281 }
282
283 async fn list_all(&self) -> Result<Vec<TransactionRepoModel>, RepositoryError> {
284 match self {
285 TransactionRepositoryStorage::InMemory(repo) => repo.list_all().await,
286 TransactionRepositoryStorage::Redis(repo) => repo.list_all().await,
287 }
288 }
289
290 async fn list_paginated(
291 &self,
292 query: PaginationQuery,
293 ) -> Result<PaginatedResult<TransactionRepoModel>, RepositoryError> {
294 match self {
295 TransactionRepositoryStorage::InMemory(repo) => repo.list_paginated(query).await,
296 TransactionRepositoryStorage::Redis(repo) => repo.list_paginated(query).await,
297 }
298 }
299
300 async fn update(
301 &self,
302 id: String,
303 entity: TransactionRepoModel,
304 ) -> Result<TransactionRepoModel, RepositoryError> {
305 match self {
306 TransactionRepositoryStorage::InMemory(repo) => repo.update(id, entity).await,
307 TransactionRepositoryStorage::Redis(repo) => repo.update(id, entity).await,
308 }
309 }
310
311 async fn delete_by_id(&self, id: String) -> Result<(), RepositoryError> {
312 match self {
313 TransactionRepositoryStorage::InMemory(repo) => repo.delete_by_id(id).await,
314 TransactionRepositoryStorage::Redis(repo) => repo.delete_by_id(id).await,
315 }
316 }
317
318 async fn count(&self) -> Result<usize, RepositoryError> {
319 match self {
320 TransactionRepositoryStorage::InMemory(repo) => repo.count().await,
321 TransactionRepositoryStorage::Redis(repo) => repo.count().await,
322 }
323 }
324
325 async fn has_entries(&self) -> Result<bool, RepositoryError> {
326 match self {
327 TransactionRepositoryStorage::InMemory(repo) => repo.has_entries().await,
328 TransactionRepositoryStorage::Redis(repo) => repo.has_entries().await,
329 }
330 }
331
332 async fn drop_all_entries(&self) -> Result<(), RepositoryError> {
333 match self {
334 TransactionRepositoryStorage::InMemory(repo) => repo.drop_all_entries().await,
335 TransactionRepositoryStorage::Redis(repo) => repo.drop_all_entries().await,
336 }
337 }
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343 use crate::models::{
344 EvmTransactionData, NetworkTransactionData, TransactionStatus, TransactionUpdateRequest,
345 };
346 use crate::repositories::PaginationQuery;
347 use crate::utils::mocks::mockutils::create_mock_transaction;
348 use chrono::Utc;
349 use color_eyre::Result;
350
351 fn create_test_transaction(id: &str, relayer_id: &str) -> TransactionRepoModel {
352 let mut transaction = create_mock_transaction();
353 transaction.id = id.to_string();
354 transaction.relayer_id = relayer_id.to_string();
355 transaction
356 }
357
358 fn create_test_transaction_with_status(
359 id: &str,
360 relayer_id: &str,
361 status: TransactionStatus,
362 ) -> TransactionRepoModel {
363 let mut transaction = create_test_transaction(id, relayer_id);
364 transaction.status = status;
365 transaction
366 }
367
368 fn create_test_transaction_with_nonce(
369 id: &str,
370 relayer_id: &str,
371 nonce: u64,
372 ) -> TransactionRepoModel {
373 let mut transaction = create_test_transaction(id, relayer_id);
374 if let NetworkTransactionData::Evm(ref mut evm_data) = transaction.network_data {
375 evm_data.nonce = Some(nonce);
376 }
377 transaction
378 }
379
380 fn create_test_update_request() -> TransactionUpdateRequest {
381 TransactionUpdateRequest {
382 status: Some(TransactionStatus::Sent),
383 status_reason: Some("Test reason".to_string()),
384 sent_at: Some(Utc::now().to_string()),
385 confirmed_at: None,
386 network_data: None,
387 priced_at: None,
388 hashes: Some(vec!["test_hash".to_string()]),
389 noop_count: None,
390 is_canceled: None,
391 delete_at: None,
392 }
393 }
394
395 #[tokio::test]
396 async fn test_new_in_memory() {
397 let storage = TransactionRepositoryStorage::new_in_memory();
398
399 match storage {
400 TransactionRepositoryStorage::InMemory(_) => {
401 }
403 TransactionRepositoryStorage::Redis(_) => {
404 panic!("Expected InMemory variant, got Redis");
405 }
406 }
407 }
408
409 #[tokio::test]
410 async fn test_create_in_memory() -> Result<()> {
411 let storage = TransactionRepositoryStorage::new_in_memory();
412 let transaction = create_test_transaction("test-tx", "test-relayer");
413
414 let created = storage.create(transaction.clone()).await?;
415 assert_eq!(created.id, transaction.id);
416 assert_eq!(created.relayer_id, transaction.relayer_id);
417 assert_eq!(created.status, transaction.status);
418
419 Ok(())
420 }
421
422 #[tokio::test]
423 async fn test_get_by_id_in_memory() -> Result<()> {
424 let storage = TransactionRepositoryStorage::new_in_memory();
425 let transaction = create_test_transaction("test-tx", "test-relayer");
426
427 storage.create(transaction.clone()).await?;
429
430 let retrieved = storage.get_by_id("test-tx".to_string()).await?;
432 assert_eq!(retrieved.id, transaction.id);
433 assert_eq!(retrieved.relayer_id, transaction.relayer_id);
434 assert_eq!(retrieved.status, transaction.status);
435
436 Ok(())
437 }
438
439 #[tokio::test]
440 async fn test_get_by_id_not_found_in_memory() -> Result<()> {
441 let storage = TransactionRepositoryStorage::new_in_memory();
442
443 let result = storage.get_by_id("non-existent".to_string()).await;
444 assert!(result.is_err());
445
446 Ok(())
447 }
448
449 #[tokio::test]
450 async fn test_list_all_in_memory() -> Result<()> {
451 let storage = TransactionRepositoryStorage::new_in_memory();
452
453 let transactions = storage.list_all().await?;
455 assert!(transactions.is_empty());
456
457 let tx1 = create_test_transaction("tx-1", "relayer-1");
459 let tx2 = create_test_transaction("tx-2", "relayer-2");
460
461 storage.create(tx1.clone()).await?;
462 storage.create(tx2.clone()).await?;
463
464 let all_transactions = storage.list_all().await?;
465 assert_eq!(all_transactions.len(), 2);
466
467 let ids: Vec<&str> = all_transactions.iter().map(|t| t.id.as_str()).collect();
468 assert!(ids.contains(&"tx-1"));
469 assert!(ids.contains(&"tx-2"));
470
471 Ok(())
472 }
473
474 #[tokio::test]
475 async fn test_list_paginated_in_memory() -> Result<()> {
476 let storage = TransactionRepositoryStorage::new_in_memory();
477
478 for i in 1..=5 {
480 let tx = create_test_transaction(&format!("tx-{}", i), "test-relayer");
481 storage.create(tx).await?;
482 }
483
484 let query = PaginationQuery {
486 page: 1,
487 per_page: 2,
488 };
489 let page = storage.list_paginated(query).await?;
490
491 assert_eq!(page.items.len(), 2);
492 assert_eq!(page.total, 5);
493 assert_eq!(page.page, 1);
494 assert_eq!(page.per_page, 2);
495
496 let query2 = PaginationQuery {
498 page: 2,
499 per_page: 2,
500 };
501 let page2 = storage.list_paginated(query2).await?;
502
503 assert_eq!(page2.items.len(), 2);
504 assert_eq!(page2.total, 5);
505 assert_eq!(page2.page, 2);
506 assert_eq!(page2.per_page, 2);
507
508 Ok(())
509 }
510
511 #[tokio::test]
512 async fn test_update_in_memory() -> Result<()> {
513 let storage = TransactionRepositoryStorage::new_in_memory();
514 let transaction = create_test_transaction("test-tx", "test-relayer");
515
516 storage.create(transaction.clone()).await?;
518
519 let mut updated_transaction = transaction.clone();
521 updated_transaction.status = TransactionStatus::Sent;
522 updated_transaction.status_reason = Some("Updated reason".to_string());
523
524 let result = storage
525 .update("test-tx".to_string(), updated_transaction.clone())
526 .await?;
527 assert_eq!(result.id, "test-tx");
528 assert_eq!(result.status, TransactionStatus::Sent);
529 assert_eq!(result.status_reason, Some("Updated reason".to_string()));
530
531 let retrieved = storage.get_by_id("test-tx".to_string()).await?;
533 assert_eq!(retrieved.status, TransactionStatus::Sent);
534 assert_eq!(retrieved.status_reason, Some("Updated reason".to_string()));
535
536 Ok(())
537 }
538
539 #[tokio::test]
540 async fn test_update_not_found_in_memory() -> Result<()> {
541 let storage = TransactionRepositoryStorage::new_in_memory();
542 let transaction = create_test_transaction("non-existent", "test-relayer");
543
544 let result = storage
545 .update("non-existent".to_string(), transaction)
546 .await;
547 assert!(result.is_err());
548
549 Ok(())
550 }
551
552 #[tokio::test]
553 async fn test_delete_by_id_in_memory() -> Result<()> {
554 let storage = TransactionRepositoryStorage::new_in_memory();
555 let transaction = create_test_transaction("test-tx", "test-relayer");
556
557 storage.create(transaction.clone()).await?;
559
560 let retrieved = storage.get_by_id("test-tx".to_string()).await?;
562 assert_eq!(retrieved.id, "test-tx");
563
564 storage.delete_by_id("test-tx".to_string()).await?;
566
567 let result = storage.get_by_id("test-tx".to_string()).await;
569 assert!(result.is_err());
570
571 Ok(())
572 }
573
574 #[tokio::test]
575 async fn test_delete_by_id_not_found_in_memory() -> Result<()> {
576 let storage = TransactionRepositoryStorage::new_in_memory();
577
578 let result = storage.delete_by_id("non-existent".to_string()).await;
579 assert!(result.is_err());
580
581 Ok(())
582 }
583
584 #[tokio::test]
585 async fn test_count_in_memory() -> Result<()> {
586 let storage = TransactionRepositoryStorage::new_in_memory();
587
588 let count = storage.count().await?;
590 assert_eq!(count, 0);
591
592 let tx1 = create_test_transaction("tx-1", "relayer-1");
594 let tx2 = create_test_transaction("tx-2", "relayer-2");
595
596 storage.create(tx1).await?;
597 let count_after_one = storage.count().await?;
598 assert_eq!(count_after_one, 1);
599
600 storage.create(tx2).await?;
601 let count_after_two = storage.count().await?;
602 assert_eq!(count_after_two, 2);
603
604 storage.delete_by_id("tx-1".to_string()).await?;
606 let count_after_delete = storage.count().await?;
607 assert_eq!(count_after_delete, 1);
608
609 Ok(())
610 }
611
612 #[tokio::test]
613 async fn test_has_entries_in_memory() -> Result<()> {
614 let storage = TransactionRepositoryStorage::new_in_memory();
615
616 let has_entries = storage.has_entries().await?;
618 assert!(!has_entries);
619
620 let transaction = create_test_transaction("test-tx", "test-relayer");
622 storage.create(transaction).await?;
623
624 let has_entries_after_create = storage.has_entries().await?;
625 assert!(has_entries_after_create);
626
627 storage.delete_by_id("test-tx".to_string()).await?;
629
630 let has_entries_after_delete = storage.has_entries().await?;
631 assert!(!has_entries_after_delete);
632
633 Ok(())
634 }
635
636 #[tokio::test]
637 async fn test_drop_all_entries_in_memory() -> Result<()> {
638 let storage = TransactionRepositoryStorage::new_in_memory();
639
640 for i in 1..=5 {
642 let tx = create_test_transaction(&format!("tx-{}", i), "test-relayer");
643 storage.create(tx).await?;
644 }
645
646 let count_before = storage.count().await?;
648 assert_eq!(count_before, 5);
649
650 let has_entries_before = storage.has_entries().await?;
651 assert!(has_entries_before);
652
653 storage.drop_all_entries().await?;
655
656 let count_after = storage.count().await?;
658 assert_eq!(count_after, 0);
659
660 let has_entries_after = storage.has_entries().await?;
661 assert!(!has_entries_after);
662
663 let all_transactions = storage.list_all().await?;
664 assert!(all_transactions.is_empty());
665
666 Ok(())
667 }
668
669 #[tokio::test]
670 async fn test_find_by_relayer_id_in_memory() -> Result<()> {
671 let storage = TransactionRepositoryStorage::new_in_memory();
672
673 let tx1 = create_test_transaction("tx-1", "relayer-1");
675 let tx2 = create_test_transaction("tx-2", "relayer-1");
676 let tx3 = create_test_transaction("tx-3", "relayer-2");
677
678 storage.create(tx1).await?;
679 storage.create(tx2).await?;
680 storage.create(tx3).await?;
681
682 let query = PaginationQuery {
684 page: 1,
685 per_page: 10,
686 };
687 let result = storage.find_by_relayer_id("relayer-1", query).await?;
688
689 assert_eq!(result.items.len(), 2);
690 assert_eq!(result.total, 2);
691
692 for tx in result.items {
694 assert_eq!(tx.relayer_id, "relayer-1");
695 }
696
697 Ok(())
698 }
699
700 #[tokio::test]
701 async fn test_find_by_status_in_memory() -> Result<()> {
702 let storage = TransactionRepositoryStorage::new_in_memory();
703
704 let tx1 =
706 create_test_transaction_with_status("tx-1", "relayer-1", TransactionStatus::Pending);
707 let tx2 = create_test_transaction_with_status("tx-2", "relayer-1", TransactionStatus::Sent);
708 let tx3 =
709 create_test_transaction_with_status("tx-3", "relayer-1", TransactionStatus::Pending);
710 let tx4 =
711 create_test_transaction_with_status("tx-4", "relayer-2", TransactionStatus::Pending);
712
713 storage.create(tx1).await?;
714 storage.create(tx2).await?;
715 storage.create(tx3).await?;
716 storage.create(tx4).await?;
717
718 let statuses = vec![TransactionStatus::Pending];
720 let result = storage.find_by_status("relayer-1", &statuses).await?;
721
722 assert_eq!(result.len(), 2);
723
724 for tx in result {
726 assert_eq!(tx.status, TransactionStatus::Pending);
727 assert_eq!(tx.relayer_id, "relayer-1");
728 }
729
730 Ok(())
731 }
732
733 #[tokio::test]
734 async fn test_find_by_nonce_in_memory() -> Result<()> {
735 let storage = TransactionRepositoryStorage::new_in_memory();
736
737 let tx1 = create_test_transaction_with_nonce("tx-1", "relayer-1", 10);
739 let tx2 = create_test_transaction_with_nonce("tx-2", "relayer-1", 20);
740 let tx3 = create_test_transaction_with_nonce("tx-3", "relayer-2", 10);
741
742 storage.create(tx1).await?;
743 storage.create(tx2).await?;
744 storage.create(tx3).await?;
745
746 let result = storage.find_by_nonce("relayer-1", 10).await?;
748
749 assert!(result.is_some());
750 let found_tx = result.unwrap();
751 assert_eq!(found_tx.id, "tx-1");
752 assert_eq!(found_tx.relayer_id, "relayer-1");
753
754 if let NetworkTransactionData::Evm(evm_data) = found_tx.network_data {
756 assert_eq!(evm_data.nonce, Some(10));
757 }
758
759 let not_found = storage.find_by_nonce("relayer-1", 99).await?;
761 assert!(not_found.is_none());
762
763 Ok(())
764 }
765
766 #[tokio::test]
767 async fn test_update_status_in_memory() -> Result<()> {
768 let storage = TransactionRepositoryStorage::new_in_memory();
769 let transaction = create_test_transaction("test-tx", "test-relayer");
770
771 storage.create(transaction).await?;
773
774 let updated = storage
776 .update_status("test-tx".to_string(), TransactionStatus::Sent)
777 .await?;
778
779 assert_eq!(updated.id, "test-tx");
780 assert_eq!(updated.status, TransactionStatus::Sent);
781
782 let retrieved = storage.get_by_id("test-tx".to_string()).await?;
784 assert_eq!(retrieved.status, TransactionStatus::Sent);
785
786 Ok(())
787 }
788
789 #[tokio::test]
790 async fn test_partial_update_in_memory() -> Result<()> {
791 let storage = TransactionRepositoryStorage::new_in_memory();
792 let transaction = create_test_transaction("test-tx", "test-relayer");
793
794 storage.create(transaction).await?;
796
797 let update_request = create_test_update_request();
799 let updated = storage
800 .partial_update("test-tx".to_string(), update_request)
801 .await?;
802
803 assert_eq!(updated.id, "test-tx");
804 assert_eq!(updated.status, TransactionStatus::Sent);
805 assert_eq!(updated.status_reason, Some("Test reason".to_string()));
806 assert!(updated.sent_at.is_some());
807 assert_eq!(updated.hashes, vec!["test_hash".to_string()]);
808
809 Ok(())
810 }
811
812 #[tokio::test]
813 async fn test_update_network_data_in_memory() -> Result<()> {
814 let storage = TransactionRepositoryStorage::new_in_memory();
815 let transaction = create_test_transaction("test-tx", "test-relayer");
816
817 storage.create(transaction).await?;
819
820 let new_evm_data = EvmTransactionData {
822 nonce: Some(42),
823 gas_limit: Some(21000),
824 ..Default::default()
825 };
826 let new_network_data = NetworkTransactionData::Evm(new_evm_data);
827
828 let updated = storage
829 .update_network_data("test-tx".to_string(), new_network_data)
830 .await?;
831
832 assert_eq!(updated.id, "test-tx");
833 if let NetworkTransactionData::Evm(evm_data) = updated.network_data {
834 assert_eq!(evm_data.nonce, Some(42));
835 assert_eq!(evm_data.gas_limit, Some(21000));
836 } else {
837 panic!("Expected EVM network data");
838 }
839
840 Ok(())
841 }
842
843 #[tokio::test]
844 async fn test_set_sent_at_in_memory() -> Result<()> {
845 let storage = TransactionRepositoryStorage::new_in_memory();
846 let transaction = create_test_transaction("test-tx", "test-relayer");
847
848 storage.create(transaction).await?;
850
851 let sent_at = Utc::now().to_string();
853 let updated = storage
854 .set_sent_at("test-tx".to_string(), sent_at.clone())
855 .await?;
856
857 assert_eq!(updated.id, "test-tx");
858 assert_eq!(updated.sent_at, Some(sent_at));
859
860 Ok(())
861 }
862
863 #[tokio::test]
864 async fn test_set_confirmed_at_in_memory() -> Result<()> {
865 let storage = TransactionRepositoryStorage::new_in_memory();
866 let transaction = create_test_transaction("test-tx", "test-relayer");
867
868 storage.create(transaction).await?;
870
871 let confirmed_at = Utc::now().to_string();
873 let updated = storage
874 .set_confirmed_at("test-tx".to_string(), confirmed_at.clone())
875 .await?;
876
877 assert_eq!(updated.id, "test-tx");
878 assert_eq!(updated.confirmed_at, Some(confirmed_at));
879
880 Ok(())
881 }
882
883 #[tokio::test]
884 async fn test_create_duplicate_id_in_memory() -> Result<()> {
885 let storage = TransactionRepositoryStorage::new_in_memory();
886 let transaction = create_test_transaction("duplicate-id", "test-relayer");
887
888 storage.create(transaction.clone()).await?;
890
891 let result = storage.create(transaction.clone()).await;
893 assert!(result.is_err());
894
895 Ok(())
896 }
897
898 #[tokio::test]
899 async fn test_workflow_in_memory() -> Result<()> {
900 let storage = TransactionRepositoryStorage::new_in_memory();
901
902 assert!(!storage.has_entries().await?);
904 assert_eq!(storage.count().await?, 0);
905
906 let transaction = create_test_transaction("workflow-test", "test-relayer");
908 let created = storage.create(transaction.clone()).await?;
909 assert_eq!(created.id, "workflow-test");
910
911 assert!(storage.has_entries().await?);
913 assert_eq!(storage.count().await?, 1);
914
915 let retrieved = storage.get_by_id("workflow-test".to_string()).await?;
917 assert_eq!(retrieved.id, "workflow-test");
918
919 let updated = storage
921 .update_status("workflow-test".to_string(), TransactionStatus::Sent)
922 .await?;
923 assert_eq!(updated.status, TransactionStatus::Sent);
924
925 let retrieved_updated = storage.get_by_id("workflow-test".to_string()).await?;
927 assert_eq!(retrieved_updated.status, TransactionStatus::Sent);
928
929 storage.delete_by_id("workflow-test".to_string()).await?;
931
932 assert!(!storage.has_entries().await?);
934 assert_eq!(storage.count().await?, 0);
935
936 let result = storage.get_by_id("workflow-test".to_string()).await;
937 assert!(result.is_err());
938
939 Ok(())
940 }
941
942 #[tokio::test]
943 async fn test_multiple_relayers_workflow() -> Result<()> {
944 let storage = TransactionRepositoryStorage::new_in_memory();
945
946 let tx1 =
948 create_test_transaction_with_status("tx-1", "relayer-1", TransactionStatus::Pending);
949 let tx2 = create_test_transaction_with_status("tx-2", "relayer-1", TransactionStatus::Sent);
950 let tx3 =
951 create_test_transaction_with_status("tx-3", "relayer-2", TransactionStatus::Pending);
952
953 storage.create(tx1).await?;
954 storage.create(tx2).await?;
955 storage.create(tx3).await?;
956
957 let query = PaginationQuery {
959 page: 1,
960 per_page: 10,
961 };
962 let relayer1_txs = storage.find_by_relayer_id("relayer-1", query).await?;
963 assert_eq!(relayer1_txs.items.len(), 2);
964
965 let pending_txs = storage
967 .find_by_status("relayer-1", &[TransactionStatus::Pending])
968 .await?;
969 assert_eq!(pending_txs.len(), 1);
970 assert_eq!(pending_txs[0].id, "tx-1");
971
972 assert_eq!(storage.count().await?, 3);
974
975 Ok(())
976 }
977
978 #[tokio::test]
979 async fn test_pagination_edge_cases_in_memory() -> Result<()> {
980 let storage = TransactionRepositoryStorage::new_in_memory();
981
982 let query = PaginationQuery {
984 page: 1,
985 per_page: 10,
986 };
987 let page = storage.list_paginated(query).await?;
988 assert_eq!(page.items.len(), 0);
989 assert_eq!(page.total, 0);
990 assert_eq!(page.page, 1);
991 assert_eq!(page.per_page, 10);
992
993 let transaction = create_test_transaction("single-tx", "test-relayer");
995 storage.create(transaction).await?;
996
997 let query = PaginationQuery {
999 page: 1,
1000 per_page: 10,
1001 };
1002 let page = storage.list_paginated(query).await?;
1003 assert_eq!(page.items.len(), 1);
1004 assert_eq!(page.total, 1);
1005 assert_eq!(page.page, 1);
1006 assert_eq!(page.per_page, 10);
1007
1008 let query = PaginationQuery {
1010 page: 3,
1011 per_page: 10,
1012 };
1013 let page = storage.list_paginated(query).await?;
1014 assert_eq!(page.items.len(), 0);
1015 assert_eq!(page.total, 1);
1016 assert_eq!(page.page, 3);
1017 assert_eq!(page.per_page, 10);
1018
1019 Ok(())
1020 }
1021
1022 #[tokio::test]
1023 async fn test_find_by_relayer_id_pagination() -> Result<()> {
1024 let storage = TransactionRepositoryStorage::new_in_memory();
1025
1026 for i in 1..=10 {
1028 let tx = create_test_transaction(&format!("tx-{}", i), "test-relayer");
1029 storage.create(tx).await?;
1030 }
1031
1032 let query = PaginationQuery {
1034 page: 1,
1035 per_page: 3,
1036 };
1037 let page1 = storage.find_by_relayer_id("test-relayer", query).await?;
1038 assert_eq!(page1.items.len(), 3);
1039 assert_eq!(page1.total, 10);
1040 assert_eq!(page1.page, 1);
1041 assert_eq!(page1.per_page, 3);
1042
1043 let query = PaginationQuery {
1045 page: 2,
1046 per_page: 3,
1047 };
1048 let page2 = storage.find_by_relayer_id("test-relayer", query).await?;
1049 assert_eq!(page2.items.len(), 3);
1050 assert_eq!(page2.total, 10);
1051 assert_eq!(page2.page, 2);
1052 assert_eq!(page2.per_page, 3);
1053
1054 Ok(())
1055 }
1056
1057 #[tokio::test]
1058 async fn test_find_by_multiple_statuses() -> Result<()> {
1059 let storage = TransactionRepositoryStorage::new_in_memory();
1060
1061 let tx1 =
1063 create_test_transaction_with_status("tx-1", "test-relayer", TransactionStatus::Pending);
1064 let tx2 =
1065 create_test_transaction_with_status("tx-2", "test-relayer", TransactionStatus::Sent);
1066 let tx3 = create_test_transaction_with_status(
1067 "tx-3",
1068 "test-relayer",
1069 TransactionStatus::Confirmed,
1070 );
1071 let tx4 =
1072 create_test_transaction_with_status("tx-4", "test-relayer", TransactionStatus::Failed);
1073
1074 storage.create(tx1).await?;
1075 storage.create(tx2).await?;
1076 storage.create(tx3).await?;
1077 storage.create(tx4).await?;
1078
1079 let statuses = vec![TransactionStatus::Pending, TransactionStatus::Sent];
1081 let result = storage.find_by_status("test-relayer", &statuses).await?;
1082
1083 assert_eq!(result.len(), 2);
1084
1085 let found_statuses: Vec<TransactionStatus> =
1087 result.iter().map(|tx| tx.status.clone()).collect();
1088 assert!(found_statuses.contains(&TransactionStatus::Pending));
1089 assert!(found_statuses.contains(&TransactionStatus::Sent));
1090
1091 Ok(())
1092 }
1093}