openzeppelin_relayer/domain/transaction/
common.rs1use chrono::{DateTime, Duration, Utc};
8
9use crate::constants::FINAL_TRANSACTION_STATUSES;
10use crate::models::{TransactionError, TransactionRepoModel, TransactionStatus};
11
12pub fn is_final_state(tx_status: &TransactionStatus) -> bool {
26 FINAL_TRANSACTION_STATUSES.contains(tx_status)
27}
28
29pub fn is_pending_transaction(tx_status: &TransactionStatus) -> bool {
30 matches!(
31 tx_status,
32 TransactionStatus::Pending | TransactionStatus::Sent | TransactionStatus::Submitted
33 )
34}
35
36pub fn get_age_of_sent_at(tx: &TransactionRepoModel) -> Result<Duration, TransactionError> {
38 let now = Utc::now();
39 let sent_at_str = tx.sent_at.as_ref().ok_or_else(|| {
40 TransactionError::UnexpectedError("Transaction sent_at time is missing".to_string())
41 })?;
42 let sent_time = DateTime::parse_from_rfc3339(sent_at_str)
43 .map_err(|_| TransactionError::UnexpectedError("Error parsing sent_at time".to_string()))?
44 .with_timezone(&Utc);
45 Ok(now.signed_duration_since(sent_time))
46}
47
48#[cfg(test)]
49mod tests {
50 use crate::utils::mocks::mockutils::create_mock_transaction;
51
52 use super::*;
53
54 #[test]
55 fn test_is_final_state() {
56 assert!(is_final_state(&TransactionStatus::Confirmed));
58 assert!(is_final_state(&TransactionStatus::Failed));
59 assert!(is_final_state(&TransactionStatus::Expired));
60 assert!(is_final_state(&TransactionStatus::Canceled));
61
62 assert!(!is_final_state(&TransactionStatus::Pending));
64 assert!(!is_final_state(&TransactionStatus::Sent));
65 assert!(!is_final_state(&TransactionStatus::Submitted));
66 assert!(!is_final_state(&TransactionStatus::Mined));
67 }
68
69 #[test]
70 fn test_is_pending_transaction() {
71 assert!(is_pending_transaction(&TransactionStatus::Pending));
73
74 assert!(is_pending_transaction(&TransactionStatus::Sent));
76
77 assert!(is_pending_transaction(&TransactionStatus::Submitted));
79
80 assert!(!is_pending_transaction(&TransactionStatus::Confirmed));
82 assert!(!is_pending_transaction(&TransactionStatus::Failed));
83 assert!(!is_pending_transaction(&TransactionStatus::Canceled));
84 assert!(!is_pending_transaction(&TransactionStatus::Mined));
85 assert!(!is_pending_transaction(&TransactionStatus::Expired));
86 }
87
88 #[test]
89 fn test_get_age_of_sent_at() {
90 let now = Utc::now();
91
92 let sent_at_time = now - Duration::hours(1);
94 let mut tx = create_mock_transaction();
95 tx.sent_at = Some(sent_at_time.to_rfc3339());
96
97 let age_result = get_age_of_sent_at(&tx);
98 assert!(age_result.is_ok());
99 let age = age_result.unwrap();
100 assert!(age.num_minutes() >= 59 && age.num_minutes() <= 61);
102 }
103
104 #[test]
105 fn test_get_age_of_sent_at_missing_sent_at() {
106 let mut tx = create_mock_transaction();
107 tx.sent_at = None; let result = get_age_of_sent_at(&tx);
110 assert!(result.is_err());
111 match result.unwrap_err() {
112 TransactionError::UnexpectedError(msg) => {
113 assert!(msg.contains("sent_at time is missing"));
114 }
115 _ => panic!("Expected UnexpectedError for missing sent_at"),
116 }
117 }
118
119 #[test]
120 fn test_get_age_of_sent_at_invalid_timestamp() {
121 let mut tx = create_mock_transaction();
122 tx.sent_at = Some("invalid-timestamp".to_string()); let result = get_age_of_sent_at(&tx);
125 assert!(result.is_err());
126 match result.unwrap_err() {
127 TransactionError::UnexpectedError(msg) => {
128 assert!(msg.contains("Error parsing sent_at time"));
129 }
130 _ => panic!("Expected UnexpectedError for invalid timestamp"),
131 }
132 }
133}