vanikam/src/identity/application/services/employee_register_service.rs

221 lines
7.1 KiB
Rust

// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
use derive_builder::Builder;
use mockall::predicate::*;
use mockall::*;
use super::errors::*;
use super::*;
use crate::identity::application::port::output::{
db::{create_verification_otp::*, emp_id_exists::*, phone_exists::*},
phone::account_validation_otp::*,
};
use crate::identity::domain::{
employee_aggregate::*, employee_register_command::*, employee_registered_event::*,
};
use crate::utils::random_number::*;
pub const OTP_LEN: u32 = 6;
#[derive(Builder)]
pub struct EmployeeRegisterUserService {
db_phone_exists_adapter: PhoneNumberExistsOutDBPortObj,
db_emp_id_exists_adapter: EmpIDExistsOutDBPortObj,
db_create_verification_otp_adapter: CreateVerificationOTPOutDBPortObj,
phone_account_validation_otp_adapter: AccountValidationOTPOutPhonePortObj,
random_number_adapter: GenerateRandomNumberInterfaceObj,
}
pub type EmployeeRegisterServiceObj = std::sync::Arc<dyn EmployeeRegisterUserUseCase>;
#[automock]
#[async_trait::async_trait]
pub trait EmployeeRegisterUserUseCase: Send + Sync {
async fn register_employee(
&self,
cmd: EmployeeRegisterCommand,
) -> IdentityResult<EmployeeRegisteredEvent>;
}
#[async_trait::async_trait]
impl EmployeeRegisterUserUseCase for EmployeeRegisterUserService {
async fn register_employee(
&self,
cmd: EmployeeRegisterCommand,
) -> IdentityResult<EmployeeRegisteredEvent> {
if self
.db_phone_exists_adapter
.phone_exists(cmd.phone_number())
.await
.unwrap()
{
return Err(IdentityError::DuplicatePhoneNumber);
}
if self
.db_emp_id_exists_adapter
.emp_id_exists(cmd.emp_id())
.await
.unwrap()
{
return Err(IdentityError::DuplicateEmployeeID);
}
let otp = self.random_number_adapter.get_random(OTP_LEN);
self.db_create_verification_otp_adapter
.create_verification_otp(
CreateOTPMsgBuilder::default()
.otp(otp.clone())
.emp_id(*cmd.emp_id())
.build()
.unwrap(),
)
.await
.unwrap();
self.phone_account_validation_otp_adapter
.account_validation_otp(cmd.phone_number(), otp)
.await?;
let e = EmployeeBuilder::default()
.first_name(cmd.first_name().clone())
.last_name(cmd.last_name().clone())
.emp_id(*cmd.emp_id())
.phone_number(cmd.phone_number().clone())
.phone_verified(false)
.deleted(false)
.build()
.unwrap();
Ok(EmployeeRegisteredEventBuilder::default()
.employee(e)
.build()
.unwrap())
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use crate::tests::bdd::*;
use crate::utils::random_number::tests::*;
use crate::utils::uuid::tests::*;
impl EmployeeRegisterUserService {
pub fn mock(
times: Option<usize>,
cmd: EmployeeRegisterCommand,
) -> EmployeeRegisterServiceObj {
let res = EmployeeRegisteredEventBuilder::default()
.employee(
EmployeeBuilder::default()
.first_name(cmd.first_name().clone())
.last_name(cmd.last_name().clone())
.emp_id(*cmd.emp_id())
.phone_number(cmd.phone_number().clone())
.phone_verified(false)
.deleted(false)
.build()
.unwrap(),
)
.build()
.unwrap();
let mut m = MockEmployeeRegisterUserUseCase::default();
if let Some(times) = times {
m.expect_register_employee()
.times(times)
.returning(move |_| Ok(res.clone()));
} else {
m.expect_register_employee()
.returning(move |_| Ok(res.clone()));
}
std::sync::Arc::new(m)
}
}
#[actix_rt::test]
async fn test_service() {
let username = "realaravinth";
let phone_number = PhoneNumber::default();
let cmd = EmployeeRegisterCommandBuilder::default()
.first_name(username.into())
.last_name(username.into())
.phone_number(phone_number)
.emp_id(UUID)
.build()
.unwrap();
let s = EmployeeRegisterUserServiceBuilder::default()
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(
IS_CALLED_ONLY_ONCE,
RETURNS_FALSE,
))
.db_create_verification_otp_adapter(mock_create_verification_otp_db_port(
IS_CALLED_ONLY_ONCE,
))
.db_phone_exists_adapter(mock_phone_exists_db_port(
IS_CALLED_ONLY_ONCE,
RETURNS_FALSE,
))
.random_number_adapter(mock_generate_random_number(
IS_CALLED_ONLY_ONCE,
RETURNS_RANDOM_NUMBER.into(),
))
.phone_account_validation_otp_adapter(mock_account_validation_otp_phone_port(
IS_CALLED_ONLY_ONCE,
))
.build()
.unwrap();
let res = s.register_employee(cmd.clone()).await.unwrap();
let emp = res.employee();
assert_eq!(emp.first_name(), cmd.first_name());
assert_eq!(emp.last_name(), cmd.last_name());
assert_eq!(emp.emp_id(), &UUID);
assert_eq!(emp.phone_number(), cmd.phone_number());
assert!(!emp.deleted());
assert!(!emp.phone_verified());
}
#[actix_rt::test]
async fn test_service_phone_exists() {
let username = "realaravinth";
let phone_number = PhoneNumber::default();
let cmd = EmployeeRegisterCommandBuilder::default()
.first_name(username.into())
.last_name(username.into())
.phone_number(phone_number)
.emp_id(UUID)
.build()
.unwrap();
let s = EmployeeRegisterUserServiceBuilder::default()
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(IGNORE_CALL_COUNT, RETURNS_FALSE))
.db_create_verification_otp_adapter(mock_create_verification_otp_db_port(
IS_NEVER_CALLED,
))
.db_phone_exists_adapter(mock_phone_exists_db_port(IS_CALLED_ONLY_ONCE, RETURNS_TRUE))
.random_number_adapter(mock_generate_random_number(
IS_NEVER_CALLED,
RETURNS_RANDOM_NUMBER.into(),
))
.phone_account_validation_otp_adapter(mock_account_validation_otp_phone_port(
IS_NEVER_CALLED,
))
.build()
.unwrap();
assert_eq!(
s.register_employee(cmd.clone()).await.err(),
Some(IdentityError::DuplicatePhoneNumber)
);
}
}