feat: test add store service with cqrs_es infra #27

Merged
realaravinth merged 1 commit from store-aggregate-test into master 2024-07-13 21:26:14 +05:30
2 changed files with 85 additions and 3 deletions

View file

@ -1,9 +1,11 @@
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net> // SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
// //
// SPDX-License-Identifier: AGPL-3.0-or-later // SPDX-License-Identifier: AGPL-3.0-or-later
use std::sync::Arc;
use derive_builder::Builder; use derive_builder::Builder;
use uuid::Uuid; use mockall::predicate::*;
use mockall::*;
use super::errors::*; use super::errors::*;
use crate::inventory::{ use crate::inventory::{
@ -16,12 +18,13 @@ use crate::inventory::{
}; };
use crate::utils::uuid::*; use crate::utils::uuid::*;
#[automock]
#[async_trait::async_trait] #[async_trait::async_trait]
pub trait AddStoreUseCase: Send + Sync { pub trait AddStoreUseCase: Send + Sync {
async fn add_store(&self, cmd: AddStoreCommand) -> InventoryResult<StoreAddedEvent>; async fn add_store(&self, cmd: AddStoreCommand) -> InventoryResult<StoreAddedEvent>;
} }
pub type AddStoreServiceObj = std::sync::Arc<dyn AddStoreUseCase>; pub type AddStoreServiceObj = Arc<dyn AddStoreUseCase>;
#[derive(Clone, Builder)] #[derive(Clone, Builder)]
pub struct AddStoreService { pub struct AddStoreService {
@ -66,12 +69,37 @@ impl AddStoreUseCase for AddStoreService {
} }
#[cfg(test)] #[cfg(test)]
mod tests { pub mod tests {
use super::*; use super::*;
use crate::tests::bdd::*; use crate::tests::bdd::*;
use crate::utils::uuid::tests::*; use crate::utils::uuid::tests::*;
pub fn mock_add_store_service(
times: Option<usize>,
cmd: AddStoreCommand,
) -> AddStoreServiceObj {
let mut m = MockAddStoreUseCase::new();
let res = StoreAddedEventBuilder::default()
.name(cmd.name().into())
.address(cmd.address().as_ref().map(|s| s.to_string()))
.owner(cmd.owner().into())
.store_id(UUID.clone())
.build()
.unwrap();
if let Some(times) = times {
m.expect_add_store()
.times(times)
.returning(move |_| Ok(res.clone()));
} else {
m.expect_add_store().returning(move |_| Ok(res.clone()));
}
Arc::new(m)
}
#[actix_rt::test] #[actix_rt::test]
async fn test_service_store_id_doesnt_exist() { async fn test_service_store_id_doesnt_exist() {
let name = "foo"; let name = "foo";

View file

@ -72,3 +72,57 @@ impl Aggregate for Store {
} }
} }
} }
// The aggregate tests are the most important part of a CQRS system.
// The simplicity and flexibility of these tests are a good part of what
// makes an event sourced system so friendly to changing business requirements.
#[cfg(test)]
mod aggregate_tests {
use std::sync::Arc;
use cqrs_es::test::TestFramework;
use super::*;
use crate::inventory::{
application::services::{add_store_service::tests::*, *},
domain::{
add_store_command::*, commands::InventoryCommand, events::InventoryEvent,
store_added_event::*,
},
};
use crate::tests::bdd::*;
use crate::utils::uuid::tests::*;
// A test framework that will apply our events and command
// and verify that the logic works as expected.
type StoreTestFramework = TestFramework<Store>;
#[test]
fn test_create_store() {
let name = "store_name";
let address = Some("store_address".to_string());
let owner = "store_owner";
let store_id = UUID;
let expected = StoreAddedEventBuilder::default()
.name(name.into())
.address(address.clone())
.store_id(store_id.clone())
.owner(owner.into())
.build()
.unwrap();
let expected = InventoryEvent::StoreAdded(expected);
let cmd = AddStoreCommand::new(name.into(), address.clone(), owner.into()).unwrap();
let mut services = MockInventoryServicesInterface::new();
services
.expect_add_store()
.times(IS_CALLED_ONLY_ONCE.unwrap())
.return_const(mock_add_store_service(IS_CALLED_ONLY_ONCE, cmd.clone()));
StoreTestFramework::with(Arc::new(services))
.given_no_previous_events()
.when(InventoryCommand::AddStore(cmd))
.then_expect_events(vec![expected]);
}
}