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

172 lines
5.8 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 crate::identity::application::port::output::{
db::{
create_login_otp::*, delete_login_otp::*, emp_id_exists::*,
get_emp_id_from_phone_number::*, get_login_otp::*, phone_exists::*,
},
phone::account_login_otp::*,
};
use crate::identity::domain::{
employee_aggregate::*, resend_login_otp_command::*, resend_login_otp_event::*,
};
use crate::utils::random_number::*;
use super::employee_register_service::OTP_LEN;
use super::errors::*;
#[automock]
#[async_trait::async_trait]
pub trait EmployeeResendLoginOTPUseCase: Send + Sync {
async fn resend_otp(&self, cmd: ResendLoginOTPCommand) -> IdentityResult<ResentLoginOTPEvent>;
}
pub type EmployeeResendLoginOTPServiceObj = std::sync::Arc<dyn EmployeeResendLoginOTPUseCase>;
#[derive(Clone, Builder)]
pub struct EmployeeResendLoginOTPService {
db_phone_exists_adapter: PhoneNumberExistsOutDBPortObj,
db_get_emp_id_from_phone_number_adapter: GetEmpIDFromPhoneNumberOutDBPortObj,
db_get_login_otp: GetLoginOTPOutDBPortObj,
phone_login_otp_adapter: AccountLoginOTPOutPhonePortObj,
}
#[async_trait::async_trait]
impl EmployeeResendLoginOTPUseCase for EmployeeResendLoginOTPService {
async fn resend_otp(&self, cmd: ResendLoginOTPCommand) -> IdentityResult<ResentLoginOTPEvent> {
if !self
.db_phone_exists_adapter
.phone_exists(cmd.phone_number())
.await?
{
return Err(IdentityError::PhoneNumberNotFound);
}
let otp = match self
.db_get_login_otp
.get_login_otp(cmd.phone_number())
.await?
{
Some(otp) => otp,
None => {
return Err(IdentityError::LoginOTPNotFound);
}
};
self.phone_login_otp_adapter
.account_login_otp(cmd.phone_number(), otp)
.await?;
let emp_id = self
.db_get_emp_id_from_phone_number_adapter
.get_emp_id_from_phone_number(cmd.phone_number())
.await?;
Ok(ResentLoginOTPEventBuilder::default()
.emp_id(emp_id)
.build()
.unwrap())
}
}
#[cfg(test)]
mod tests {
use crate::{
tests::bdd::*,
utils::{random_number::tests::mock_generate_random_number, uuid::tests::*},
};
use super::*;
impl EmployeeResendLoginOTPService {
pub fn mock_service(
times: Option<usize>,
_cmd: ResendLoginOTPCommand,
) -> EmployeeResendLoginOTPServiceObj {
let res = ResentLoginOTPEvent::get_event();
let mut m = MockEmployeeResendLoginOTPUseCase::default();
if let Some(times) = times {
m.expect_resend_otp()
.times(times)
.returning(move |_| Ok(res.clone()));
} else {
m.expect_resend_otp().returning(move |_| Ok(res.clone()));
}
std::sync::Arc::new(m)
}
}
#[actix_rt::test]
async fn test_service_init_login() {
let s = EmployeeResendLoginOTPServiceBuilder::default()
.db_phone_exists_adapter(mock_phone_exists_db_port(IS_CALLED_ONLY_ONCE, true))
.db_get_emp_id_from_phone_number_adapter(mock_get_emp_id_from_phone_number_db_port(
IS_CALLED_ONLY_ONCE,
))
.db_get_login_otp(mock_get_login_otp_db_port(IS_CALLED_ONLY_ONCE))
.phone_login_otp_adapter(mock_account_login_otp_phone_port(IS_CALLED_ONLY_ONCE))
.build()
.unwrap();
{
let cmd = ResendLoginOTPCommandBuilder::default()
.phone_number(PhoneNumber::default())
.build()
.unwrap();
let res = s.resend_otp(cmd.clone()).await.unwrap();
assert_eq!(res.emp_id(), &UUID);
}
}
#[actix_rt::test]
async fn test_service_phone_doesnt_exist() {
let s = EmployeeResendLoginOTPServiceBuilder::default()
.db_phone_exists_adapter(mock_phone_exists_db_port(IS_CALLED_ONLY_ONCE, false))
.db_get_emp_id_from_phone_number_adapter(mock_get_emp_id_from_phone_number_db_port(
IS_NEVER_CALLED,
))
.db_get_login_otp(mock_get_login_otp_db_port_returns_none(IS_NEVER_CALLED))
.phone_login_otp_adapter(mock_account_login_otp_phone_port(IS_NEVER_CALLED))
.build()
.unwrap();
{
let cmd = ResendLoginOTPCommandBuilder::default()
.phone_number(PhoneNumber::default())
.build()
.unwrap();
let res = s.resend_otp(cmd.clone()).await;
assert_eq!(res.err(), Some(IdentityError::PhoneNumberNotFound))
}
}
#[actix_rt::test]
async fn test_service_otp_doesnt_exist() {
let s = EmployeeResendLoginOTPServiceBuilder::default()
.db_phone_exists_adapter(mock_phone_exists_db_port(IS_CALLED_ONLY_ONCE, true))
.db_get_emp_id_from_phone_number_adapter(mock_get_emp_id_from_phone_number_db_port(
IS_NEVER_CALLED,
))
.db_get_login_otp(mock_get_login_otp_db_port_returns_none(IS_CALLED_ONLY_ONCE))
.phone_login_otp_adapter(mock_account_login_otp_phone_port(IS_NEVER_CALLED))
.build()
.unwrap();
{
let cmd = ResendLoginOTPCommandBuilder::default()
.phone_number(PhoneNumber::default())
.build()
.unwrap();
let res = s.resend_otp(cmd.clone()).await;
assert_eq!(res.err(), Some(IdentityError::LoginOTPNotFound))
}
}
}