timesource 0.1.3

Event sourcing with TimescaleDb
Documentation
mod common;

use timesource::error::Error;
use timesource::store::{CommitOrder, EventStore, EventStoreBuilder};
use uuid::Uuid;

use crate::common::data::{bootstrap_test, TestData, DSN};
use crate::common::order::{OrderCommand, OrderEvent, OrderItem};

#[tokio::test]
async fn repository_should_reconstruct_state_after_appending_all_variants_of_events(
) -> Result<(), Box<dyn std::error::Error>> {
    let TestData {
        aggregate_id,
        repository,
        mut root,
        ..
    } = bootstrap_test(false).await;

    // append unit, tuple and struct enum variants
    root.handle(OrderCommand::Create)?;
    root.handle(OrderCommand::AddItem {
        item: OrderItem {
            item_sku: "sku-123".into(),
            quantity: 1,
            price: 12,
        },
    })?;
    root.handle(OrderCommand::Empty("some reason".into()))?;

    repository.commit_orderly(&mut root).await?;

    let stored = repository.get(aggregate_id).await?;

    let stored_state = stored.state().to_owned().unwrap();
    let expected_state = root.state().unwrap();

    assert_eq!(
        expected_state.items, stored_state.items,
        "Stored items are not what's expected"
    );
    assert_eq!(
        expected_state.created_at, stored_state.created_at,
        "Order created_at timestamp doesn't match"
    );

    Ok(())
}

#[tokio::test]
async fn repository_should_err_if_not_found() {
    let TestData { repository, .. } = bootstrap_test(false).await;

    let output = repository.get(Uuid::new_v4()).await;

    match output {
        Ok(_) => unreachable!(),
        Err(Error::AggregateRootNotFound) => (),
        Err(_) => unreachable!(),
    }
}

#[tokio::test]
#[should_panic(expected = "Aggregate(AlreadyCreated)")]
async fn should_propagate_aggregate_err() {
    let TestData {
        aggregate_type_name,
        repository,
        aggregate_id,
        ..
    } = bootstrap_test(false).await;

    let store = EventStoreBuilder::new(DSN)
        .build::<OrderEvent>(&aggregate_type_name)
        .await
        .expect("store to be created");

    // Order aggregate should fail with invalid events
    store
        .commit(
            aggregate_id,
            CommitOrder::None,
            &[OrderEvent::Created.into(), OrderEvent::Created.into()],
        )
        .await
        .unwrap();

    repository.get(aggregate_id).await.unwrap();
}