liminal-sdk 0.3.3

Application-facing SDK traits for liminal messaging clients
Documentation
use alloc::sync::Arc;

use spin::Mutex;

use crate::SdkError;

use super::{ConnectionEvent, ConnectionLifecycle, ConnectionState, DisconnectReason};

#[test]
fn disconnected_to_connected_is_rejected() -> Result<(), SdkError> {
    let mut lifecycle = ConnectionLifecycle::default();
    lifecycle.disconnect(DisconnectReason::Normal)?;

    assert!(lifecycle.connected().is_err());
    lifecycle.connect()?;
    lifecycle.connected()?;
    assert_eq!(lifecycle.state(), &ConnectionState::Connected);
    Ok(())
}

#[test]
fn observers_receive_successful_transitions() -> Result<(), SdkError> {
    let events = Arc::new(Mutex::new(Vec::new()));
    let observed = Arc::clone(&events);
    let mut lifecycle = ConnectionLifecycle::default();

    lifecycle.observe(move |event| observed.lock().push(event.clone()));
    lifecycle.connected()?;
    lifecycle.disconnect(DisconnectReason::Timeout)?;

    assert_eq!(events.lock().len(), 2);
    assert_eq!(
        events.lock()[0],
        ConnectionEvent::new(ConnectionState::Connecting, ConnectionState::Connected)
    );
    Ok(())
}

#[test]
fn disconnect_from_connecting_is_observable() -> Result<(), SdkError> {
    let events = Arc::new(Mutex::new(Vec::new()));
    let observed = Arc::clone(&events);
    let mut lifecycle = ConnectionLifecycle::default();

    lifecycle.observe(move |event| observed.lock().push(event.clone()));
    lifecycle.disconnect(DisconnectReason::Error)?;

    let disconnected = ConnectionState::Disconnected {
        reason: DisconnectReason::Error,
    };
    assert_eq!(lifecycle.state(), &disconnected);
    assert_eq!(
        events.lock()[0],
        ConnectionEvent::new(ConnectionState::Connecting, disconnected)
    );
    Ok(())
}

#[test]
fn externally_authorized_reconnect_has_no_delay_or_retry_counter() -> Result<(), SdkError> {
    let mut lifecycle = ConnectionLifecycle::default();
    lifecycle.connected()?;

    lifecycle.reconnect_started()?;
    assert_eq!(lifecycle.state(), &ConnectionState::Reconnecting);
    assert!(lifecycle.reconnect_started().is_err());

    lifecycle.connected()?;
    lifecycle.reconnect_started()?;
    assert_eq!(lifecycle.state(), &ConnectionState::Reconnecting);
    Ok(())
}

#[test]
fn disconnect_from_reconnecting_is_observable() -> Result<(), SdkError> {
    let events = Arc::new(Mutex::new(Vec::new()));
    let observed = Arc::clone(&events);
    let mut lifecycle = ConnectionLifecycle::default();
    lifecycle.connected()?;
    lifecycle.reconnect_started()?;

    lifecycle.observe(move |event| observed.lock().push(event.clone()));
    lifecycle.disconnect(DisconnectReason::Error)?;

    assert_eq!(
        events.lock()[0],
        ConnectionEvent::new(
            ConnectionState::Reconnecting,
            ConnectionState::Disconnected {
                reason: DisconnectReason::Error,
            },
        )
    );
    Ok(())
}

#[test]
fn disconnect_from_disconnected_is_rejected_without_mutation() -> Result<(), SdkError> {
    let mut lifecycle = ConnectionLifecycle::default();
    lifecycle.disconnect(DisconnectReason::Normal)?;
    assert!(lifecycle.disconnect(DisconnectReason::Error).is_err());
    assert_eq!(
        lifecycle.state(),
        &ConnectionState::Disconnected {
            reason: DisconnectReason::Normal,
        }
    );
    Ok(())
}