feat: order: LineItem add and update cmd,events&service #59
3 changed files with 167 additions and 2 deletions
|
@ -10,19 +10,25 @@ pub mod errors;
|
|||
|
||||
//services
|
||||
pub mod add_line_item_service;
|
||||
pub mod update_line_item_service;
|
||||
|
||||
#[automock]
|
||||
pub trait OrderingServicesInterface: Send + Sync {
|
||||
fn add_line_item(&self) -> add_line_item_service::AddLineItemServiceObj;
|
||||
fn update_line_item(&self) -> update_line_item_service::UpdateLineItemServiceObj;
|
||||
}
|
||||
|
||||
#[derive(Clone, Builder)]
|
||||
pub struct OrderingServices {
|
||||
add_line_item: add_line_item_service::AddLineItemServiceObj,
|
||||
update_line_item: update_line_item_service::UpdateLineItemServiceObj,
|
||||
}
|
||||
|
||||
impl OrderingServicesInterface for OrderingServices {
|
||||
fn add_line_item(&self) -> add_line_item_service::AddLineItemServiceObj {
|
||||
self.add_line_item.clone()
|
||||
}
|
||||
fn update_line_item(&self) -> update_line_item_service::UpdateLineItemServiceObj {
|
||||
self.update_line_item.clone()
|
||||
}
|
||||
}
|
||||
|
|
134
src/ordering/application/services/update_line_item_service.rs
Normal file
134
src/ordering/application/services/update_line_item_service.rs
Normal file
|
@ -0,0 +1,134 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use derive_builder::Builder;
|
||||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use super::errors::*;
|
||||
use crate::ordering::{
|
||||
application::port::output::db::line_item_id_exists::*,
|
||||
domain::{line_item_aggregate::*, line_item_updated_event::*, update_line_item_command::*},
|
||||
};
|
||||
use crate::utils::uuid::*;
|
||||
|
||||
#[automock]
|
||||
#[async_trait::async_trait]
|
||||
pub trait UpdateLineItemUseCase: Send + Sync {
|
||||
async fn update_line_item(
|
||||
&self,
|
||||
cmd: UpdateLineItemCommand,
|
||||
) -> OrderingResult<LineItemUpdatedEvent>;
|
||||
}
|
||||
|
||||
pub type UpdateLineItemServiceObj = Arc<dyn UpdateLineItemUseCase>;
|
||||
|
||||
#[derive(Clone, Builder)]
|
||||
pub struct UpdateLineItemService {
|
||||
db_line_item_id_exists: LineItemIDExistsDBPortObj,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl UpdateLineItemUseCase for UpdateLineItemService {
|
||||
async fn update_line_item(
|
||||
&self,
|
||||
cmd: UpdateLineItemCommand,
|
||||
) -> OrderingResult<LineItemUpdatedEvent> {
|
||||
if !self
|
||||
.db_line_item_id_exists
|
||||
.line_item_id_exists(cmd.old_line_item().line_item_id())
|
||||
.await?
|
||||
{
|
||||
return Err(OrderingError::LineItemIDNotFound);
|
||||
}
|
||||
|
||||
let new_line_item = LineItemBuilder::default()
|
||||
.sale_time(cmd.sale_time().clone())
|
||||
.product_name(cmd.product_name().into())
|
||||
.product_id(*cmd.product_id())
|
||||
.line_item_id(*cmd.old_line_item().line_item_id())
|
||||
.quantity(cmd.quantity().clone())
|
||||
.deleted(false)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
Ok(LineItemUpdatedEventBuilder::default()
|
||||
.added_by_user(*cmd.adding_by())
|
||||
.new_line_item(new_line_item)
|
||||
.old_line_item(cmd.old_line_item().clone())
|
||||
.build()
|
||||
.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::ordering::domain::line_item_updated_event::tests::get_updated_line_item_event_from_command;
|
||||
use crate::utils::uuid::tests::UUID;
|
||||
use crate::{tests::bdd::*, utils::uuid::tests::mock_get_uuid};
|
||||
|
||||
pub fn mock_update_line_item_service(
|
||||
times: Option<usize>,
|
||||
cmd: UpdateLineItemCommand,
|
||||
) -> UpdateLineItemServiceObj {
|
||||
let mut m = MockUpdateLineItemUseCase::new();
|
||||
|
||||
let res = get_updated_line_item_event_from_command(&cmd);
|
||||
if let Some(times) = times {
|
||||
m.expect_update_line_item()
|
||||
.times(times)
|
||||
.returning(move |_| Ok(res.clone()));
|
||||
} else {
|
||||
m.expect_update_line_item()
|
||||
.returning(move |_| Ok(res.clone()));
|
||||
}
|
||||
|
||||
Arc::new(m)
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service() {
|
||||
let cmd = UpdateLineItemCommand::get_cmd();
|
||||
|
||||
let s = UpdateLineItemServiceBuilder::default()
|
||||
.db_line_item_id_exists(mock_line_item_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let res = s.update_line_item(cmd.clone()).await.unwrap();
|
||||
assert_eq!(res.new_line_item().product_name(), cmd.product_name());
|
||||
assert_eq!(res.new_line_item().product_id(), cmd.product_id());
|
||||
assert_eq!(res.new_line_item().quantity(), cmd.quantity());
|
||||
assert_eq!(res.new_line_item().quantity(), cmd.quantity());
|
||||
assert_eq!(
|
||||
res.new_line_item().line_item_id(),
|
||||
cmd.old_line_item().line_item_id()
|
||||
);
|
||||
assert!(!res.new_line_item().deleted());
|
||||
|
||||
assert_eq!(res.old_line_item(), cmd.old_line_item());
|
||||
|
||||
assert_eq!(res.added_by_user(), cmd.adding_by());
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_line_item_id_doesnt_exist() {
|
||||
let cmd = UpdateLineItemCommand::get_cmd();
|
||||
|
||||
let s = UpdateLineItemServiceBuilder::default()
|
||||
.db_line_item_id_exists(mock_line_item_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
s.update_line_item(cmd.clone()).await,
|
||||
Err(OrderingError::LineItemIDNotFound)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -92,7 +92,8 @@ impl Aggregate for LineItem {
|
|||
Ok(vec![OrderingEvent::LineItemAdded(res)])
|
||||
}
|
||||
OrderingCommand::UpdateLineItem(cmd) => {
|
||||
unimplemented!()
|
||||
let res = services.update_line_item().update_line_item(cmd).await?;
|
||||
Ok(vec![OrderingEvent::LineItemUpdated(res)])
|
||||
}
|
||||
OrderingCommand::DeleteLineItem(cmd) => {
|
||||
unimplemented!()
|
||||
|
@ -116,9 +117,12 @@ mod aggregate_tests {
|
|||
|
||||
use add_line_item_service::tests::mock_add_line_item_service;
|
||||
use cqrs_es::test::TestFramework;
|
||||
use update_line_item_service::tests::mock_update_line_item_service;
|
||||
|
||||
use super::*;
|
||||
|
||||
use crate::ordering::domain::line_item_updated_event::tests::get_updated_line_item_event_from_command;
|
||||
use crate::ordering::domain::update_line_item_command::UpdateLineItemCommand;
|
||||
use crate::tests::bdd::*;
|
||||
|
||||
use crate::ordering::domain::{
|
||||
|
@ -129,7 +133,7 @@ mod aggregate_tests {
|
|||
type LineItemTestFramework = TestFramework<LineItem>;
|
||||
|
||||
#[test]
|
||||
fn test_create_product() {
|
||||
fn test_add_line_item() {
|
||||
let cmd = AddLineItemCommand::get_cmd();
|
||||
let expected = get_added_line_item_event_from_command(&cmd);
|
||||
let expected = OrderingEvent::LineItemAdded(expected);
|
||||
|
@ -145,4 +149,25 @@ mod aggregate_tests {
|
|||
.when(OrderingCommand::AddLineItem(cmd))
|
||||
.then_expect_events(vec![expected]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_line_item() {
|
||||
let cmd = UpdateLineItemCommand::get_cmd();
|
||||
let expected = get_updated_line_item_event_from_command(&cmd);
|
||||
let expected = OrderingEvent::LineItemUpdated(expected);
|
||||
|
||||
let mut services = MockOrderingServicesInterface::new();
|
||||
services
|
||||
.expect_update_line_item()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.return_const(mock_update_line_item_service(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
cmd.clone(),
|
||||
));
|
||||
|
||||
LineItemTestFramework::with(Arc::new(services))
|
||||
.given_no_previous_events()
|
||||
.when(OrderingCommand::UpdateLineItem(cmd))
|
||||
.then_expect_events(vec![expected]);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue