feat: delete LineItem service
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful

This commit is contained in:
Aravinth Manivannan 2024-07-23 18:02:29 +05:30
parent 15c0241598
commit 667e1549c9
Signed by: realaravinth
GPG key ID: F8F50389936984FF
4 changed files with 178 additions and 3 deletions

View file

@ -0,0 +1,132 @@
// 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::{delete_line_item_command::*, line_item_aggregate::*, line_item_deleted_event::*},
};
use crate::utils::uuid::*;
#[automock]
#[async_trait::async_trait]
pub trait DeleteLineItemUseCase: Send + Sync {
async fn delete_line_item(
&self,
cmd: DeleteLineItemCommand,
) -> OrderingResult<LineItemDeletedEvent>;
}
pub type DeleteLineItemServiceObj = Arc<dyn DeleteLineItemUseCase>;
#[derive(Clone, Builder)]
pub struct DeleteLineItemService {
db_line_item_id_exists: LineItemIDExistsDBPortObj,
}
#[async_trait::async_trait]
impl DeleteLineItemUseCase for DeleteLineItemService {
async fn delete_line_item(
&self,
cmd: DeleteLineItemCommand,
) -> OrderingResult<LineItemDeletedEvent> {
if !self
.db_line_item_id_exists
.line_item_id_exists(cmd.line_item().line_item_id())
.await?
{
return Err(OrderingError::LineItemIDNotFound);
}
let deleted_line_item = LineItemBuilder::default()
.sale_time(cmd.line_item().sale_time().clone())
.product_name(cmd.line_item().product_name().into())
.product_id(*cmd.line_item().product_id())
.line_item_id(*cmd.line_item().line_item_id())
.quantity(cmd.line_item().quantity().clone())
.deleted(true)
.build()
.unwrap();
Ok(LineItemDeletedEventBuilder::default()
.added_by_user(*cmd.adding_by())
.line_item(deleted_line_item)
.build()
.unwrap())
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use crate::ordering::domain::line_item_deleted_event::tests::get_deleted_line_item_event_from_command;
use crate::tests::bdd::*;
pub fn mock_delete_line_item_service(
times: Option<usize>,
cmd: DeleteLineItemCommand,
) -> DeleteLineItemServiceObj {
let mut m = MockDeleteLineItemUseCase::new();
let res = get_deleted_line_item_event_from_command(&cmd);
if let Some(times) = times {
m.expect_delete_line_item()
.times(times)
.returning(move |_| Ok(res.clone()));
} else {
m.expect_delete_line_item()
.returning(move |_| Ok(res.clone()));
}
Arc::new(m)
}
#[actix_rt::test]
async fn test_service() {
let cmd = DeleteLineItemCommand::get_cmd();
let s = DeleteLineItemServiceBuilder::default()
.db_line_item_id_exists(mock_line_item_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
.build()
.unwrap();
let res = s.delete_line_item(cmd.clone()).await.unwrap();
assert_eq!(
res.line_item().product_name(),
cmd.line_item().product_name()
);
assert_eq!(res.line_item().product_id(), cmd.line_item().product_id());
assert_eq!(res.line_item().quantity(), cmd.line_item().quantity());
assert_eq!(
res.line_item().line_item_id(),
cmd.line_item().line_item_id()
);
assert!(res.line_item().deleted());
assert_eq!(res.added_by_user(), cmd.adding_by());
}
#[actix_rt::test]
async fn test_service_line_item_id_doesnt_exist() {
let cmd = DeleteLineItemCommand::get_cmd();
let s = DeleteLineItemServiceBuilder::default()
.db_line_item_id_exists(mock_line_item_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
.build()
.unwrap();
assert_eq!(
s.delete_line_item(cmd.clone()).await,
Err(OrderingError::LineItemIDNotFound)
);
}
}

View file

@ -10,18 +10,21 @@ pub mod errors;
//services //services
pub mod add_line_item_service; pub mod add_line_item_service;
pub mod delete_line_item_service;
pub mod update_line_item_service; pub mod update_line_item_service;
#[automock] #[automock]
pub trait OrderingServicesInterface: Send + Sync { pub trait OrderingServicesInterface: Send + Sync {
fn add_line_item(&self) -> add_line_item_service::AddLineItemServiceObj; fn add_line_item(&self) -> add_line_item_service::AddLineItemServiceObj;
fn update_line_item(&self) -> update_line_item_service::UpdateLineItemServiceObj; fn update_line_item(&self) -> update_line_item_service::UpdateLineItemServiceObj;
fn delete_line_item(&self) -> delete_line_item_service::DeleteLineItemServiceObj;
} }
#[derive(Clone, Builder)] #[derive(Clone, Builder)]
pub struct OrderingServices { pub struct OrderingServices {
add_line_item: add_line_item_service::AddLineItemServiceObj, add_line_item: add_line_item_service::AddLineItemServiceObj,
update_line_item: update_line_item_service::UpdateLineItemServiceObj, update_line_item: update_line_item_service::UpdateLineItemServiceObj,
delete_line_item: delete_line_item_service::DeleteLineItemServiceObj,
} }
impl OrderingServicesInterface for OrderingServices { impl OrderingServicesInterface for OrderingServices {
@ -31,4 +34,8 @@ impl OrderingServicesInterface for OrderingServices {
fn update_line_item(&self) -> update_line_item_service::UpdateLineItemServiceObj { fn update_line_item(&self) -> update_line_item_service::UpdateLineItemServiceObj {
self.update_line_item.clone() self.update_line_item.clone()
} }
fn delete_line_item(&self) -> delete_line_item_service::DeleteLineItemServiceObj {
self.delete_line_item.clone()
}
} }

View file

@ -96,7 +96,8 @@ impl Aggregate for LineItem {
Ok(vec![OrderingEvent::LineItemUpdated(res)]) Ok(vec![OrderingEvent::LineItemUpdated(res)])
} }
OrderingCommand::DeleteLineItem(cmd) => { OrderingCommand::DeleteLineItem(cmd) => {
unimplemented!() let res = services.delete_line_item().delete_line_item(cmd).await?;
Ok(vec![OrderingEvent::LineItemDeleted(res)])
} // _ => Ok(Vec::default()), } // _ => Ok(Vec::default()),
} }
} }
@ -117,10 +118,13 @@ mod aggregate_tests {
use add_line_item_service::tests::mock_add_line_item_service; use add_line_item_service::tests::mock_add_line_item_service;
use cqrs_es::test::TestFramework; use cqrs_es::test::TestFramework;
use delete_line_item_service::tests::mock_delete_line_item_service;
use update_line_item_service::tests::mock_update_line_item_service; use update_line_item_service::tests::mock_update_line_item_service;
use super::*; use super::*;
use crate::ordering::domain::delete_line_item_command::DeleteLineItemCommand;
use crate::ordering::domain::line_item_deleted_event::tests::get_deleted_line_item_event_from_command;
use crate::ordering::domain::line_item_updated_event::tests::get_updated_line_item_event_from_command; 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::ordering::domain::update_line_item_command::UpdateLineItemCommand;
use crate::tests::bdd::*; use crate::tests::bdd::*;
@ -170,4 +174,25 @@ mod aggregate_tests {
.when(OrderingCommand::UpdateLineItem(cmd)) .when(OrderingCommand::UpdateLineItem(cmd))
.then_expect_events(vec![expected]); .then_expect_events(vec![expected]);
} }
#[test]
fn test_delete_line_item() {
let cmd = DeleteLineItemCommand::get_cmd();
let expected = get_deleted_line_item_event_from_command(&cmd);
let expected = OrderingEvent::LineItemDeleted(expected);
let mut services = MockOrderingServicesInterface::new();
services
.expect_delete_line_item()
.times(IS_CALLED_ONLY_ONCE.unwrap())
.return_const(mock_delete_line_item_service(
IS_CALLED_ONLY_ONCE,
cmd.clone(),
));
LineItemTestFramework::with(Arc::new(services))
.given_no_previous_events()
.when(OrderingCommand::DeleteLineItem(cmd))
.then_expect_events(vec![expected]);
}
} }

View file

@ -27,15 +27,26 @@ pub mod tests {
pub fn get_deleted_line_item_event_from_command( pub fn get_deleted_line_item_event_from_command(
cmd: &DeleteLineItemCommand, cmd: &DeleteLineItemCommand,
) -> LineItemDeletedEvent { ) -> LineItemDeletedEvent {
let deleted_line_item = LineItemBuilder::default()
.sale_time(cmd.line_item().sale_time().clone())
.product_name(cmd.line_item().product_name().into())
.product_id(*cmd.line_item().product_id())
.line_item_id(*cmd.line_item().line_item_id())
.quantity(cmd.line_item().quantity().clone())
.deleted(true)
.build()
.unwrap();
LineItemDeletedEventBuilder::default() LineItemDeletedEventBuilder::default()
.added_by_user(cmd.adding_by().clone()) .added_by_user(cmd.adding_by().clone())
.line_item(cmd.line_item().clone()) .line_item(deleted_line_item)
.build() .build()
.unwrap() .unwrap()
} }
#[test] #[test]
fn test_event() { fn test_event() {
get_deleted_line_item_event_from_command(&DeleteLineItemCommand::get_cmd()); let event = get_deleted_line_item_event_from_command(&DeleteLineItemCommand::get_cmd());
assert!(event.line_item().deleted());
} }
} }