// SPDX-FileCopyrightText: 2024 Aravinth Manivannan // // 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; } pub type EmployeeResendLoginOTPServiceObj = std::sync::Arc; #[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 { 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, _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)) } } }