Bug: [FEATURE] Implement `MerchantConnectorAccountInterface` for `MockDb` 



Spin off from #172. Refer to the parent issue for more information
diff --git a/crates/router/src/db/merchant_connector_account.rs b/crates/router/src/db/merchant_connector_account.rs
index 997cc130c7d..05507593808 100644
--- a/crates/router/src/db/merchant_connector_account.rs
+++ b/crates/router/src/db/merchant_connector_account.rs
@@ -1,5 +1,6 @@
 use common_utils::ext_traits::{AsyncExt, ByteSliceExt, Encode};
 use error_stack::{IntoReport, ResultExt};
+use storage_models::merchant_connector_account::MerchantConnectorAccountUpdateInternal;
 
 #[cfg(feature = "accounts_cache")]
 use super::cache;
@@ -138,7 +139,7 @@ where
     async fn update_merchant_connector_account(
         &self,
         this: domain::MerchantConnectorAccount,
-        merchant_connector_account: storage::MerchantConnectorAccountUpdateInternal,
+        merchant_connector_account: MerchantConnectorAccountUpdateInternal,
     ) -> CustomResult<domain::MerchantConnectorAccount, errors::StorageError>;
 
     async fn delete_merchant_connector_account_by_merchant_id_merchant_connector_id(
@@ -256,7 +257,7 @@ impl MerchantConnectorAccountInterface for Store {
     async fn update_merchant_connector_account(
         &self,
         this: domain::MerchantConnectorAccount,
-        merchant_connector_account: storage::MerchantConnectorAccountUpdateInternal,
+        merchant_connector_account: MerchantConnectorAccountUpdateInternal,
     ) -> CustomResult<domain::MerchantConnectorAccount, errors::StorageError> {
         let _merchant_connector_id = this.merchant_connector_id.clone();
         let update_call = || async {
@@ -307,37 +308,71 @@ impl MerchantConnectorAccountInterface for Store {
 
 #[async_trait::async_trait]
 impl MerchantConnectorAccountInterface for MockDb {
-    // safety: only used for testing
-    #[allow(clippy::unwrap_used)]
     async fn find_merchant_connector_account_by_merchant_id_connector_label(
         &self,
         merchant_id: &str,
         connector: &str,
     ) -> CustomResult<domain::MerchantConnectorAccount, errors::StorageError> {
-        let accounts = self.merchant_connector_accounts.lock().await;
-        let account = accounts
+        match self
+            .merchant_connector_accounts
+            .lock()
+            .await
             .iter()
             .find(|account| {
                 account.merchant_id == merchant_id && account.connector_name == connector
             })
             .cloned()
-            .unwrap();
-        account
-            .convert(self, merchant_id)
+            .async_map(|account| async {
+                account
+                    .convert(self, merchant_id)
+                    .await
+                    .change_context(errors::StorageError::DecryptionError)
+            })
             .await
-            .change_context(errors::StorageError::DecryptionError)
+        {
+            Some(result) => result,
+            None => {
+                return Err(errors::StorageError::ValueNotFound(
+                    "cannot find merchant connector account".to_string(),
+                )
+                .into())
+            }
+        }
     }
 
     async fn find_by_merchant_connector_account_merchant_id_merchant_connector_id(
         &self,
-        _merchant_id: &str,
-        _merchant_connector_id: &str,
+        merchant_id: &str,
+        merchant_connector_id: &str,
     ) -> CustomResult<domain::MerchantConnectorAccount, errors::StorageError> {
-        // [#172]: Implement function for `MockDb`
-        Err(errors::StorageError::MockDbError)?
+        match self
+            .merchant_connector_accounts
+            .lock()
+            .await
+            .iter()
+            .find(|account| {
+                account.merchant_id == merchant_id
+                    && account.merchant_connector_id == merchant_connector_id
+            })
+            .cloned()
+            .async_map(|account| async {
+                account
+                    .convert(self, merchant_id)
+                    .await
+                    .change_context(errors::StorageError::DecryptionError)
+            })
+            .await
+        {
+            Some(result) => result,
+            None => {
+                return Err(errors::StorageError::ValueNotFound(
+                    "cannot find merchant connector account".to_string(),
+                )
+                .into())
+            }
+        }
     }
 
-    #[allow(clippy::panic)]
     async fn insert_merchant_connector_account(
         &self,
         t: domain::MerchantConnectorAccount,
@@ -373,28 +408,91 @@ impl MerchantConnectorAccountInterface for MockDb {
 
     async fn find_merchant_connector_account_by_merchant_id_and_disabled_list(
         &self,
-        _merchant_id: &str,
-        _get_disabled: bool,
+        merchant_id: &str,
+        get_disabled: bool,
     ) -> CustomResult<Vec<domain::MerchantConnectorAccount>, errors::StorageError> {
-        // [#172]: Implement function for `MockDb`
-        Err(errors::StorageError::MockDbError)?
+        let accounts = self
+            .merchant_connector_accounts
+            .lock()
+            .await
+            .iter()
+            .filter(|account: &&storage::MerchantConnectorAccount| {
+                if get_disabled {
+                    account.merchant_id == merchant_id
+                } else {
+                    account.merchant_id == merchant_id && account.disabled == Some(false)
+                }
+            })
+            .cloned()
+            .collect::<Vec<storage::MerchantConnectorAccount>>();
+
+        let mut output = Vec::with_capacity(accounts.len());
+        for account in accounts.into_iter() {
+            output.push(
+                account
+                    .convert(self, merchant_id)
+                    .await
+                    .change_context(errors::StorageError::DecryptionError)?,
+            )
+        }
+        Ok(output)
     }
 
     async fn update_merchant_connector_account(
         &self,
-        _this: domain::MerchantConnectorAccount,
-        _merchant_connector_account: storage::MerchantConnectorAccountUpdateInternal,
+        merchant_connector_account: domain::MerchantConnectorAccount,
+        updated_merchant_connector_account: MerchantConnectorAccountUpdateInternal,
     ) -> CustomResult<domain::MerchantConnectorAccount, errors::StorageError> {
-        // [#172]: Implement function for `MockDb`
-        Err(errors::StorageError::MockDbError)?
+        match self
+            .merchant_connector_accounts
+            .lock()
+            .await
+            .iter_mut()
+            .find(|account| Some(account.id) == merchant_connector_account.id)
+            .map(|a| {
+                let updated =
+                    updated_merchant_connector_account.create_merchant_connector_account(a.clone());
+                *a = updated.clone();
+                updated
+            })
+            .async_map(|account| async {
+                account
+                    .convert(self, &merchant_connector_account.merchant_id)
+                    .await
+                    .change_context(errors::StorageError::DecryptionError)
+            })
+            .await
+        {
+            Some(result) => result,
+            None => {
+                return Err(errors::StorageError::ValueNotFound(
+                    "cannot find merchant connector account to update".to_string(),
+                )
+                .into())
+            }
+        }
     }
 
     async fn delete_merchant_connector_account_by_merchant_id_merchant_connector_id(
         &self,
-        _merchant_id: &str,
-        _merchant_connector_id: &str,
+        merchant_id: &str,
+        merchant_connector_id: &str,
     ) -> CustomResult<bool, errors::StorageError> {
-        // [#172]: Implement function for `MockDb`
-        Err(errors::StorageError::MockDbError)?
+        let mut accounts = self.merchant_connector_accounts.lock().await;
+        match accounts.iter().position(|account| {
+            account.merchant_id == merchant_id
+                && account.merchant_connector_id == merchant_connector_id
+        }) {
+            Some(index) => {
+                accounts.remove(index);
+                return Ok(true);
+            }
+            None => {
+                return Err(errors::StorageError::ValueNotFound(
+                    "cannot find merchant connector account to delete".to_string(),
+                )
+                .into())
+            }
+        }
     }
 }
diff --git a/crates/storage_models/src/merchant_connector_account.rs b/crates/storage_models/src/merchant_connector_account.rs
index c798602f646..ed34ab26736 100644
--- a/crates/storage_models/src/merchant_connector_account.rs
+++ b/crates/storage_models/src/merchant_connector_account.rs
@@ -1,5 +1,8 @@
+use std::fmt::Debug;
+
 use common_utils::pii;
 use diesel::{AsChangeset, Identifiable, Insertable, Queryable};
+use masking::Secret;
 
 use crate::{encryption::Encryption, enums as storage_enums, schema::merchant_connector_account};
 
@@ -29,7 +32,7 @@ pub struct MerchantConnectorAccount {
     pub business_country: storage_enums::CountryAlpha2,
     pub business_label: String,
     pub business_sub_label: Option<String>,
-    pub frm_configs: Option<masking::Secret<serde_json::Value>>,
+    pub frm_configs: Option<Secret<serde_json::Value>>,
     pub created_at: time::PrimitiveDateTime,
     pub modified_at: time::PrimitiveDateTime,
 }
@@ -50,7 +53,7 @@ pub struct MerchantConnectorAccountNew {
     pub business_country: storage_enums::CountryAlpha2,
     pub business_label: String,
     pub business_sub_label: Option<String>,
-    pub frm_configs: Option<masking::Secret<serde_json::Value>>,
+    pub frm_configs: Option<Secret<serde_json::Value>>,
     pub created_at: time::PrimitiveDateTime,
     pub modified_at: time::PrimitiveDateTime,
 }
@@ -67,6 +70,31 @@ pub struct MerchantConnectorAccountUpdateInternal {
     pub merchant_connector_id: Option<String>,
     pub payment_methods_enabled: Option<Vec<serde_json::Value>>,
     pub metadata: Option<pii::SecretSerdeValue>,
-    pub frm_configs: Option<masking::Secret<serde_json::Value>>,
+    pub frm_configs: Option<Secret<serde_json::Value>>,
     pub modified_at: Option<time::PrimitiveDateTime>,
 }
+
+impl MerchantConnectorAccountUpdateInternal {
+    pub fn create_merchant_connector_account(
+        self,
+        source: MerchantConnectorAccount,
+    ) -> MerchantConnectorAccount {
+        MerchantConnectorAccount {
+            merchant_id: self.merchant_id.unwrap_or(source.merchant_id),
+            connector_type: self.connector_type.unwrap_or(source.connector_type),
+            connector_account_details: self
+                .connector_account_details
+                .unwrap_or(source.connector_account_details),
+            test_mode: self.test_mode,
+            disabled: self.disabled,
+            merchant_connector_id: self
+                .merchant_connector_id
+                .unwrap_or(source.merchant_connector_id),
+            payment_methods_enabled: self.payment_methods_enabled,
+            frm_configs: self.frm_configs,
+            modified_at: self.modified_at.unwrap_or(source.modified_at),
+
+            ..source
+        }
+    }
+}

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [x] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description


### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!-- 
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
The main motivation is to have MockDb stubs, help to void mocking, and invocation of external database api's.
For more information check https://github.com/juspay/hyperswitch/issues/172

Fixes [1123](https://github.com/juspay/hyperswitch/issues/1123)


## How did you test it?

## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed submitted code
- [ ] I added unit tests for my changes where possible
- [ ] I added a [CHANGELOG](/CHANGELOG.md) entry if applicable
