esrc 0.1.0

Primitives for implementing event sourcing and CQRS systems
Documentation
#![cfg(test)]

use super::*;

fn open_tab() {
    let table_number: u64 = 42;
    let waiter: String = "Derek".into();

    let command = TabCommand::Open {
        table_number,
        waiter: waiter.clone(),
    };
    let aggregate = Tab::default();

    let expected_event = TabEvent::Opened {
        table_number,
        waiter,
    };
    let actual_event = aggregate.process(command);

    assert!(actual_event.is_ok());
    assert_eq!(expected_event, actual_event.unwrap());
}

#[test]
fn order_with_unopened_tab() {
    let item = Item {
        menu_number: 1,
        description: "drink".into(),
        price: 5.50,
    };

    let command = TabCommand::PlaceOrder { items: vec![item] };
    let aggregate = Tab::default();

    let expected_error = TabError::NotOpen;
    let actual_error = aggregate.process(command);

    assert!(actual_error.is_err());
    assert_eq!(expected_error, actual_error.unwrap_err());
}

#[test]
fn order() {
    let table_number: u64 = 42;
    let waiter: String = "Derek".into();
    let item = Item {
        menu_number: 1,
        description: "drink".into(),
        price: 5.50,
    };

    let command = TabCommand::PlaceOrder {
        items: vec![item.clone()],
    };
    let aggregate = Tab::default().apply(&TabEvent::Opened {
        table_number,
        waiter,
    });

    let expected_event = TabEvent::Ordered { items: vec![item] };
    let actual_event = aggregate.process(command);

    assert!(actual_event.is_ok());
    assert_eq!(expected_event, actual_event.unwrap());
}

#[test]
fn serve_twice() {
    let table_number: u64 = 42;
    let waiter: String = "Derek".into();
    let item = Item {
        menu_number: 1,
        description: "drink".into(),
        price: 5.50,
    };

    let command = TabCommand::MarkServed {
        menu_numbers: vec![item.menu_number],
    };
    let aggregate = Tab::default()
        .apply(&TabEvent::Opened {
            table_number,
            waiter,
        })
        .apply(&TabEvent::Ordered {
            items: vec![item.clone()],
        })
        .apply(&TabEvent::Served {
            menu_numbers: vec![item.menu_number],
        });

    let expected_error = TabError::AlreadyServed;
    let actual_error = aggregate.process(command);

    assert!(actual_error.is_err());
    assert_eq!(expected_error, actual_error.unwrap_err());
}

#[test]
fn serve() {
    let table_number: u64 = 42;
    let waiter: String = "Derek".into();
    let item = Item {
        menu_number: 1,
        description: "drink".into(),
        price: 5.50,
    };

    let command = TabCommand::MarkServed {
        menu_numbers: vec![item.menu_number],
    };
    let aggregate = Tab::default()
        .apply(&TabEvent::Opened {
            table_number,
            waiter,
        })
        .apply(&TabEvent::Ordered {
            items: vec![item.clone()],
        });

    let expected_event = TabEvent::Served {
        menu_numbers: vec![item.menu_number],
    };
    let actual_event = aggregate.process(command);

    assert!(actual_event.is_ok());
    assert_eq!(expected_event, actual_event.unwrap());
}

#[test]
fn close_with_underpayment() {
    let table_number: u64 = 42;
    let waiter: String = "Derek".into();
    let item = Item {
        menu_number: 1,
        description: "drink".into(),
        price: 5.50,
    };
    let amount_paid = 5.00;

    let command = TabCommand::Close { amount_paid };
    let aggregate = Tab::default()
        .apply(&TabEvent::Opened {
            table_number,
            waiter,
        })
        .apply(&TabEvent::Ordered {
            items: vec![item.clone()],
        })
        .apply(&TabEvent::Served {
            menu_numbers: vec![item.menu_number],
        });

    let expected_error = TabError::Unpaid;
    let actual_error = aggregate.process(command);

    assert!(actual_error.is_err());
    assert_eq!(expected_error, actual_error.unwrap_err());
}

#[test]
fn close() {
    let table_number: u64 = 42;
    let waiter: String = "Derek".into();
    let item = Item {
        menu_number: 1,
        description: "drink".into(),
        price: 5.50,
    };
    let amount_paid = 6.00;

    let command = TabCommand::Close { amount_paid };
    let aggregate = Tab::default()
        .apply(&TabEvent::Opened {
            table_number,
            waiter,
        })
        .apply(&TabEvent::Ordered {
            items: vec![item.clone()],
        })
        .apply(&TabEvent::Served {
            menu_numbers: vec![item.menu_number],
        });

    let expected_event = TabEvent::Closed {
        amount_paid,
        order_value: item.price,
        tip_value: amount_paid - item.price,
    };
    let actual_event = aggregate.process(command);

    assert!(actual_event.is_ok());
    assert_eq!(expected_event, actual_event.unwrap());
}