172 lines
5.8 KiB
Rust
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))
|
|
}
|
|
}
|
|
}
|