# All transition tests (unit + property-based)
cargo test --lib test_transitions
# Only property-based tests
cargo test --lib test_transitions prop_
# With detailed output
cargo test --lib test_transitions -- --nocapture --test-threads=1Located in src/transitions.rs and src/test_transitions.rs:
test_valid_transition_*- Verify specific valid transitionstest_invalid_transition_*- Verify specific invalid transitionstest_idempotent_transition_*- Verify same-state transitionstest_is_terminal_status_*- Verify terminal state detectiontest_valid_next_states_*- Verify state graph structuretest_lifecycle_*- End-to-end remittance flowstest_state_machine_graph_coverage- Verify all edges existtest_terminal_states_comprehensive- Verify terminal immutability
Located in src/test_transitions.rs:
prop_terminal_states_are_immutable- Terminal states cannot transitionprop_valid_transitions_allowed- Valid transitions are allowedprop_invalid_transitions_rejected- Invalid transitions are rejectedprop_idempotent_transitions_allowed- Same-state transitions workprop_terminal_states_block_further_transitions- Terminal finalityprop_no_cycles_in_state_graph- State graph is acyclicprop_disputed_only_from_failed- Dispute reachabilityprop_pending_is_initial_only- Initial state uniquenessprop_non_terminal_states_have_exits- No stuck statesprop_transition_validation_is_deterministic- Reproducible behavior
Pending ──→ Processing ──→ Completed (terminal)
│ │
└───→ Failed ──→ Disputed
│ │
└───────────┴──→ Cancelled (terminal)
| From | To | Reason |
|---|---|---|
| Pending | Processing | Agent accepts payout |
| Pending | Cancelled | Sender cancels |
| Pending | Failed | Payout fails immediately |
| Processing | Completed | Payout confirmed |
| Processing | Cancelled | Payout fails during processing |
| Processing | Failed | Payout fails |
| Failed | Disputed | Sender disputes failure |
| Any | Same | Idempotent (safe for retries) |
- Completed: Payout confirmed, funds released to agent
- Cancelled: Remittance cancelled, funds refunded to sender
Terminal states cannot transition further.
#[test]
fn test_my_transition() {
let from = RemittanceStatus::Pending;
let to = RemittanceStatus::Processing;
assert!(from.can_transition_to(&to));
}proptest! {
#[test]
fn prop_my_invariant(status in arb_status()) {
// Your invariant check here
prop_assert!(status.is_terminal() || /* condition */);
}
}When a property test fails, proptest:
- Shrinks the input to a minimal reproducer
- Saves the seed to
proptest/regressions/src_test_transitions_rs.txt - Replays the same seed on subsequent runs
To debug:
# Run with the saved seed (automatic)
cargo test --lib test_transitions prop_my_test
# View the regression file
cat proptest/regressions/src_test_transitions_rs.txtFor unit tests, check:
- The transition is in the valid set
- The state machine graph is correct
- Terminal states are properly marked
✅ Immutability: Terminal states cannot transition
✅ Validity: Only defined transitions are allowed
✅ Idempotency: Same-state transitions are safe
✅ Acyclicity: No cycles in state graph
✅ Reachability: Disputed only from Failed
✅ Initialization: Pending is initial-only
✅ Completeness: Non-terminal states have exits
✅ Determinism: Validation is reproducible
- Unit tests: <100ms
- Property tests: <1s (100 cases per property)
- Total: <2s for all transition tests
Tests run automatically in CI:
cargo test --libFailures block PR merges. To check locally before pushing:
cargo test --lib test_transitionsCause: Trying to transition from Completed or Cancelled
Fix: Check that the state is not terminal before transitioning
Cause: Attempting a transition not in the state graph
Fix: Verify the transition is in the valid set (see table above)
Cause: Same-state transition rejected
Fix: Ensure can_transition_to() allows same-state transitions
- Implementation:
src/transitions.rs - Types:
src/types.rs(RemittanceStatus enum) - Tests:
src/test_transitions.rs - Documentation:
PROPERTY_BASED_TESTS.md