소스 검색

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 1 개월 전
부모
커밋
7ca6c92cce
1개의 변경된 파일221개의 추가작업 그리고 0개의 파일을 삭제
  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(_, _))
+            ));
+        }
+    }
+}