hw-wallet 0.1.2

Shared wallet orchestration utilities for CLI and FFI
Documentation
use std::time::Duration;

use trezor_connect::thp::state::HandshakeCache;
use trezor_connect::thp::{BackendError, PairingMethod, Phase, ThpState, ThpWorkflowError};

use super::workflow_error::WorkflowErrorExt;
use super::*;

#[test]
fn device_locked_and_transport_busy_retry_handshake() {
    for (err, retryable) in [
        (BackendError::DeviceLocked, true),
        (BackendError::TransportBusy, true),
        (BackendError::PinExpected, false),
    ] {
        assert_eq!(
            ThpWorkflowError::Backend(err).is_retryable_handshake(),
            retryable
        );
    }
}

#[test]
fn session_phase_transition_sequence_progresses_to_ready() {
    let mut state = ThpState::new();
    assert_eq!(
        SessionPhase::from_state(&state, false),
        SessionPhase::NeedsChannel
    );

    state.set_handshake_cache(HandshakeCache {
        channel: 7,
        pairing_methods: vec![PairingMethod::CodeEntry],
    });
    assert_eq!(
        SessionPhase::from_state(&state, false),
        SessionPhase::NeedsHandshake
    );

    state.set_phase(Phase::Pairing);
    state.set_is_paired(false);
    assert_eq!(
        SessionPhase::from_state(&state, false),
        SessionPhase::NeedsPairingCode
    );

    state.set_is_paired(true);
    assert_eq!(
        SessionPhase::from_state(&state, false),
        SessionPhase::NeedsConnectionConfirmation
    );

    state.set_phase(Phase::Paired);
    assert_eq!(
        SessionPhase::from_state(&state, false),
        SessionPhase::NeedsSession
    );
    assert_eq!(SessionPhase::from_state(&state, true), SessionPhase::Ready);

    state.reset();
    assert_eq!(
        SessionPhase::from_state(&state, true),
        SessionPhase::NeedsChannel
    );
}

#[test]
fn session_state_flags_follow_phase() {
    let pairing = SessionState::new(
        SessionPhase::NeedsPairingCode,
        Some("Enter code".to_string()),
    );
    assert!(pairing.can_pair_only);
    assert!(pairing.can_connect);
    assert!(!pairing.can_get_address);
    assert!(!pairing.can_sign_tx);
    assert!(pairing.requires_pairing_code);
    assert_eq!(pairing.prompt_message.as_deref(), Some("Enter code"));

    let ready = SessionState::new(SessionPhase::Ready, None);
    assert!(!ready.can_pair_only);
    assert!(!ready.can_connect);
    assert!(ready.can_get_address);
    assert!(ready.can_sign_tx);
    assert!(!ready.requires_pairing_code);
    assert!(ready.prompt_message.is_none());
}

#[test]
fn retry_policy_defaults_and_minimums() {
    for (policy, expected_attempts, expected_delay) in [
        (SessionRetryPolicy::default(), (3, 2, 3), 800),
        (
            SessionRetryPolicy {
                create_channel_attempts: 0,
                handshake_attempts: 0,
                create_session_attempts: 0,
                retry_delay_ms: 0,
            },
            (1, 1, 1),
            1,
        ),
    ] {
        assert_eq!(
            (
                policy.create_channel_attempts(),
                policy.handshake_attempts(),
                policy.create_session_attempts()
            ),
            expected_attempts
        );
        assert_eq!(policy.retry_delay(), Duration::from_millis(expected_delay));
    }
}