diff --git a/src/ordering/domain/add_line_item_command.rs b/src/ordering/domain/add_line_item_command.rs index d059735..16cc2b9 100644 --- a/src/ordering/domain/add_line_item_command.rs +++ b/src/ordering/domain/add_line_item_command.rs @@ -64,6 +64,8 @@ pub struct AddLineItemCommand { #[cfg(test)] mod tests { + use time::macros::datetime; + use crate::utils::uuid::tests::UUID; use super::*; @@ -78,6 +80,7 @@ mod tests { UnvalidatedAddLineItemCommandBuilder::default() .product_name(product_name.into()) .adding_by(adding_by) + .sale_time(datetime!(1970-01-01 0:00 UTC)) .quantity(quantity.clone()) .product_id(product_id) .build() diff --git a/src/ordering/domain/line_item_aggregate.rs b/src/ordering/domain/line_item_aggregate.rs index b0f4fc4..29efd76 100644 --- a/src/ordering/domain/line_item_aggregate.rs +++ b/src/ordering/domain/line_item_aggregate.rs @@ -10,6 +10,10 @@ use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use uuid::Uuid; +use crate::ordering::{ + application::services::{errors::*, *}, + domain::{commands::*, events::*}, +}; use crate::types::quantity::Quantity; #[derive( @@ -26,6 +30,19 @@ pub struct LineItem { deleted: bool, } +impl Default for LineItem { + fn default() -> Self { + Self { + sale_time: OffsetDateTime::now_utc(), + product_name: String::default(), + product_id: Default::default(), + line_item_id: Default::default(), + quantity: Default::default(), + deleted: false, + } + } +} + #[cfg(test)] mod tests { use crate::{ @@ -49,3 +66,83 @@ mod tests { } } } + +#[async_trait] +impl Aggregate for LineItem { + type Command = OrderingCommand; + type Event = OrderingEvent; + type Error = OrderingError; + type Services = std::sync::Arc; + + // This identifier should be unique to the system. + fn aggregate_type() -> String { + "ordering.line_item".to_string() + } + + // The aggregate logic goes here. Note that this will be the _bulk_ of a CQRS system + // so expect to use helper functions elsewhere to keep the code clean. + async fn handle( + &self, + command: Self::Command, + services: &Self::Services, + ) -> Result, Self::Error> { + match command { + OrderingCommand::AddLineItem(cmd) => { + let res = services.add_line_item().add_line_item(cmd).await?; + Ok(vec![OrderingEvent::LineItemAdded(res)]) + } + OrderingCommand::UpdateLineItem(cmd) => { + unimplemented!() + } + OrderingCommand::DeleteLineItem(cmd) => { + unimplemented!() + } // _ => Ok(Vec::default()), + } + } + + fn apply(&mut self, event: Self::Event) { + match event { + OrderingEvent::LineItemAdded(e) => *self = e.line_item().clone(), + OrderingEvent::LineItemUpdated(e) => *self = e.new_line_item().clone(), + OrderingEvent::LineItemDeleted(e) => *self = e.line_item().clone(), + // _ => (), + } + } +} + +#[cfg(test)] +mod aggregate_tests { + use std::sync::Arc; + + use add_line_item_service::tests::mock_add_line_item_service; + use cqrs_es::test::TestFramework; + + use super::*; + + use crate::tests::bdd::*; + + use crate::ordering::domain::{ + add_line_item_command::*, + line_item_added_event::tests::get_added_line_item_event_from_command, + }; + + type LineItemTestFramework = TestFramework; + + #[test] + fn test_create_product() { + let cmd = AddLineItemCommand::get_cmd(); + let expected = get_added_line_item_event_from_command(&cmd); + let expected = OrderingEvent::LineItemAdded(expected); + + let mut services = MockOrderingServicesInterface::new(); + services + .expect_add_line_item() + .times(IS_CALLED_ONLY_ONCE.unwrap()) + .return_const(mock_add_line_item_service(IS_CALLED_ONLY_ONCE, cmd.clone())); + + LineItemTestFramework::with(Arc::new(services)) + .given_no_previous_events() + .when(OrderingCommand::AddLineItem(cmd)) + .then_expect_events(vec![expected]); + } +}