Explorar el Código

Add unit tests for state transition validation functions

Adds comprehensive test coverage for `check_state_transition` and
`check_melt_quote_state_transition` in the state module.

The tests verify both valid and invalid state transitions:

- Proof states: Validates the Unspent→Pending→Spent flow, ensures spent proofs
  cannot be modified, and confirms appropriate error types are returned for
  each invalid transition.

- Melt quote states: Covers the full state machine including Unpaid, Pending,
  Paid, Failed, and Unknown states, verifying that terminal states like Paid
  reject all transitions and that specific error variants are returned for
  different failure cases.
Cesar Rodas hace 1 mes
padre
commit
7ca6c92cce
Se han modificado 1 ficheros con 221 adiciones y 0 borrados
  1. 221 0
      crates/cdk-common/src/state.rs

+ 221 - 0
crates/cdk-common/src/state.rs

@@ -81,3 +81,224 @@ pub fn check_melt_quote_state_transition(
         Ok(())
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    mod proof_state_transitions {
+        use super::*;
+
+        #[test]
+        fn unspent_to_pending_is_valid() {
+            assert!(check_state_transition(State::Unspent, State::Pending).is_ok());
+        }
+
+        #[test]
+        fn unspent_to_spent_is_valid() {
+            assert!(check_state_transition(State::Unspent, State::Spent).is_ok());
+        }
+
+        #[test]
+        fn pending_to_unspent_is_valid() {
+            assert!(check_state_transition(State::Pending, State::Unspent).is_ok());
+        }
+
+        #[test]
+        fn pending_to_spent_is_valid() {
+            assert!(check_state_transition(State::Pending, State::Spent).is_ok());
+        }
+
+        #[test]
+        fn unspent_to_unspent_is_invalid() {
+            let result = check_state_transition(State::Unspent, State::Unspent);
+            assert!(matches!(result, Err(Error::InvalidTransition(_, _))));
+        }
+
+        #[test]
+        fn pending_to_pending_returns_pending_error() {
+            let result = check_state_transition(State::Pending, State::Pending);
+            assert!(matches!(result, Err(Error::Pending)));
+        }
+
+        #[test]
+        fn spent_to_any_returns_already_spent() {
+            assert!(matches!(
+                check_state_transition(State::Spent, State::Unspent),
+                Err(Error::AlreadySpent)
+            ));
+            assert!(matches!(
+                check_state_transition(State::Spent, State::Pending),
+                Err(Error::AlreadySpent)
+            ));
+            assert!(matches!(
+                check_state_transition(State::Spent, State::Spent),
+                Err(Error::AlreadySpent)
+            ));
+        }
+
+        #[test]
+        fn reserved_state_is_invalid_source() {
+            let result = check_state_transition(State::Reserved, State::Unspent);
+            assert!(matches!(result, Err(Error::InvalidTransition(_, _))));
+        }
+    }
+
+    mod melt_quote_state_transitions {
+        use super::*;
+
+        #[test]
+        fn unpaid_to_pending_is_valid() {
+            assert!(check_melt_quote_state_transition(
+                MeltQuoteState::Unpaid,
+                MeltQuoteState::Pending
+            )
+            .is_ok());
+        }
+
+        #[test]
+        fn unpaid_to_failed_is_valid() {
+            assert!(check_melt_quote_state_transition(
+                MeltQuoteState::Unpaid,
+                MeltQuoteState::Failed
+            )
+            .is_ok());
+        }
+
+        #[test]
+        fn pending_to_unpaid_is_valid() {
+            assert!(check_melt_quote_state_transition(
+                MeltQuoteState::Pending,
+                MeltQuoteState::Unpaid
+            )
+            .is_ok());
+        }
+
+        #[test]
+        fn pending_to_paid_is_valid() {
+            assert!(check_melt_quote_state_transition(
+                MeltQuoteState::Pending,
+                MeltQuoteState::Paid
+            )
+            .is_ok());
+        }
+
+        #[test]
+        fn pending_to_failed_is_valid() {
+            assert!(check_melt_quote_state_transition(
+                MeltQuoteState::Pending,
+                MeltQuoteState::Failed
+            )
+            .is_ok());
+        }
+
+        #[test]
+        fn failed_to_pending_is_valid() {
+            assert!(check_melt_quote_state_transition(
+                MeltQuoteState::Failed,
+                MeltQuoteState::Pending
+            )
+            .is_ok());
+        }
+
+        #[test]
+        fn failed_to_unpaid_is_valid() {
+            assert!(check_melt_quote_state_transition(
+                MeltQuoteState::Failed,
+                MeltQuoteState::Unpaid
+            )
+            .is_ok());
+        }
+
+        #[test]
+        fn unknown_to_any_is_valid() {
+            assert!(check_melt_quote_state_transition(
+                MeltQuoteState::Unknown,
+                MeltQuoteState::Unpaid
+            )
+            .is_ok());
+            assert!(check_melt_quote_state_transition(
+                MeltQuoteState::Unknown,
+                MeltQuoteState::Pending
+            )
+            .is_ok());
+            assert!(check_melt_quote_state_transition(
+                MeltQuoteState::Unknown,
+                MeltQuoteState::Paid
+            )
+            .is_ok());
+            assert!(check_melt_quote_state_transition(
+                MeltQuoteState::Unknown,
+                MeltQuoteState::Failed
+            )
+            .is_ok());
+        }
+
+        #[test]
+        fn unpaid_to_paid_is_invalid() {
+            let result =
+                check_melt_quote_state_transition(MeltQuoteState::Unpaid, MeltQuoteState::Paid);
+            assert!(matches!(
+                result,
+                Err(Error::InvalidMeltQuoteTransition(_, _))
+            ));
+        }
+
+        #[test]
+        fn unpaid_to_unpaid_is_invalid() {
+            let result =
+                check_melt_quote_state_transition(MeltQuoteState::Unpaid, MeltQuoteState::Unpaid);
+            assert!(matches!(
+                result,
+                Err(Error::InvalidMeltQuoteTransition(_, _))
+            ));
+        }
+
+        #[test]
+        fn pending_to_pending_returns_pending_error() {
+            let result =
+                check_melt_quote_state_transition(MeltQuoteState::Pending, MeltQuoteState::Pending);
+            assert!(matches!(result, Err(Error::Pending)));
+        }
+
+        #[test]
+        fn paid_to_any_returns_already_paid() {
+            assert!(matches!(
+                check_melt_quote_state_transition(MeltQuoteState::Paid, MeltQuoteState::Unpaid),
+                Err(Error::AlreadyPaid)
+            ));
+            assert!(matches!(
+                check_melt_quote_state_transition(MeltQuoteState::Paid, MeltQuoteState::Pending),
+                Err(Error::AlreadyPaid)
+            ));
+            assert!(matches!(
+                check_melt_quote_state_transition(MeltQuoteState::Paid, MeltQuoteState::Paid),
+                Err(Error::AlreadyPaid)
+            ));
+            assert!(matches!(
+                check_melt_quote_state_transition(MeltQuoteState::Paid, MeltQuoteState::Failed),
+                Err(Error::AlreadyPaid)
+            ));
+        }
+
+        #[test]
+        fn failed_to_paid_is_invalid() {
+            let result =
+                check_melt_quote_state_transition(MeltQuoteState::Failed, MeltQuoteState::Paid);
+            assert!(matches!(
+                result,
+                Err(Error::InvalidMeltQuoteTransition(_, _))
+            ));
+        }
+
+        #[test]
+        fn failed_to_failed_is_invalid() {
+            let result =
+                check_melt_quote_state_transition(MeltQuoteState::Failed, MeltQuoteState::Failed);
+            assert!(matches!(
+                result,
+                Err(Error::InvalidMeltQuoteTransition(_, _))
+            ));
+        }
+    }
+}