Expand description

Testing utilities for thalo apps.

Examples

Create aggregate and events.

use thalo::{
    aggregate::{Aggregate, TypeId},
    event::EventType,
};
use thiserror::Error;

#[derive(Aggregate, Clone, Debug, Default, PartialEq, TypeId)]
struct BankAccount {
    id: String,
    opened: bool,
    balance: f64,
}

#[derive(Clone, Debug, EventType)]
enum BankAccountEvent {
    OpenedAccount { balance: f64 },
}

fn apply(bank_account: &mut BankAccount, event: BankAccountEvent) {
    use BankAccountEvent::*;

    match event {
        OpenedAccount { balance } => {
            bank_account.opened = true;
            bank_account.balance = balance;
        }
    }
}

Test aggregate events.

#[cfg(test)]
mod tests {
    use thalo_testing::*;
    use super::{BankAccount, BankAccountEvent};

    #[test]
    fn opened_account() {
        BankAccount::given(
            "account-123",
            BankAccountEvent::OpenedAccount {
                balance: 0.0,
            }
        )
        .should_eq(BankAccount {
            id: "account-123".to_string(),
            opened: true,
            balance: 0.0,
        });
    }
}

Test aggregate commands.

impl BankAccount {
    pub fn open_account(
        &self,
        initial_balance: f64,
    ) -> Result<BankAccountEvent, BankAccountError> {
        if self.opened {
            return Err(BankAccountError::AlreadyOpened);
        }

        if initial_balance < 0.0 {
            return Err(BankAccountError::NegativeAmount);
        }

        Ok(BankAccountEvent::OpenedAccount {
            balance: initial_balance,
        })
    }
}

#[derive(Debug, Error)]
pub enum BankAccountError {
    #[error("account already opened")]
    AlreadyOpened,
    #[error("negative amount")]
    NegativeAmount,
}

#[cfg(test)]
mod tests {
    use thalo_testing::*;
    use super::{BankAccount, BankAccountError, BankAccountEvent};

    #[test]
    fn open_account() {
        BankAccount::given_no_events("account-123")
            .when(|bank_account| bank_account.open_account(0.0))
            .then(Ok(BankAccountEvent::OpenedAccount {
                balance: 0.0,
            }));
    }
    #[test]
    fn open_account_already_opened() {
        BankAccount::given(
            "account-123",
            BankAccountEvent::OpenedAccount {
                balance: 0.0,
            },
        )
        .when(|bank_account| bank_account.open_account(50.0))
        .then(Err(BankAccountError::AlreadyOpened));
    }

    #[test]
    fn open_account_negative_amount() {
        BankAccount::given_no_events()
            .when(|bank_account| bank_account.open_account(-10.0))
            .then(Err(BankAccountError::NegativeAmount));
    }

Structs

An aggregate given events.

An aggregate when a command is performed.

Traits

Given events for an aggregate.