Bug: Implement `LockerMockInterface` for `MockDb`



Spin out from https://github.com/juspay/hyperswitch/issues/172. Please refer to that issue for more information.
diff --git a/crates/router/src/db.rs b/crates/router/src/db.rs
index d5f05bfb73a..0bec1c7c081 100644
--- a/crates/router/src/db.rs
+++ b/crates/router/src/db.rs
@@ -111,6 +111,7 @@ pub struct MockDb {
     cards_info: Arc<Mutex<Vec<storage::CardInfo>>>,
     events: Arc<Mutex<Vec<storage::Event>>>,
     disputes: Arc<Mutex<Vec<storage::Dispute>>>,
+    lockers: Arc<Mutex<Vec<storage::LockerMockUp>>>,
 }
 
 impl MockDb {
@@ -130,6 +131,7 @@ impl MockDb {
             cards_info: Default::default(),
             events: Default::default(),
             disputes: Default::default(),
+            lockers: Default::default(),
         }
     }
 }
diff --git a/crates/router/src/db/locker_mock_up.rs b/crates/router/src/db/locker_mock_up.rs
index 30b1beee454..5011b77ce6a 100644
--- a/crates/router/src/db/locker_mock_up.rs
+++ b/crates/router/src/db/locker_mock_up.rs
@@ -62,25 +62,180 @@ impl LockerMockUpInterface for Store {
 impl LockerMockUpInterface for MockDb {
     async fn find_locker_by_card_id(
         &self,
-        _card_id: &str,
+        card_id: &str,
     ) -> CustomResult<storage::LockerMockUp, errors::StorageError> {
-        // [#172]: Implement function for `MockDb`
-        Err(errors::StorageError::MockDbError)?
+        self.lockers
+            .lock()
+            .await
+            .iter()
+            .find(|l| l.card_id == card_id)
+            .cloned()
+            .ok_or(errors::StorageError::MockDbError.into())
     }
 
     async fn insert_locker_mock_up(
         &self,
-        _new: storage::LockerMockUpNew,
+        new: storage::LockerMockUpNew,
     ) -> CustomResult<storage::LockerMockUp, errors::StorageError> {
-        // [#172]: Implement function for `MockDb`
-        Err(errors::StorageError::MockDbError)?
+        let mut locked_lockers = self.lockers.lock().await;
+
+        if locked_lockers.iter().any(|l| l.card_id == new.card_id) {
+            Err(errors::StorageError::MockDbError)?;
+        }
+
+        let created_locker = storage::LockerMockUp {
+            #[allow(clippy::as_conversions)]
+            id: locked_lockers.len() as i32,
+            card_id: new.card_id,
+            external_id: new.external_id,
+            card_fingerprint: new.card_fingerprint,
+            card_global_fingerprint: new.card_global_fingerprint,
+            merchant_id: new.merchant_id,
+            card_number: new.card_number,
+            card_exp_year: new.card_exp_year,
+            card_exp_month: new.card_exp_month,
+            name_on_card: new.name_on_card,
+            nickname: None,
+            customer_id: new.customer_id,
+            duplicate: None,
+            card_cvc: new.card_cvc,
+            payment_method_id: new.payment_method_id,
+        };
+
+        locked_lockers.push(created_locker.clone());
+
+        Ok(created_locker)
     }
 
     async fn delete_locker_mock_up(
         &self,
-        _card_id: &str,
+        card_id: &str,
     ) -> CustomResult<storage::LockerMockUp, errors::StorageError> {
-        // [#172]: Implement function for `MockDb`
-        Err(errors::StorageError::MockDbError)?
+        let mut locked_lockers = self.lockers.lock().await;
+
+        let position = locked_lockers
+            .iter()
+            .position(|l| l.card_id == card_id)
+            .ok_or(errors::StorageError::MockDbError)?;
+
+        Ok(locked_lockers.remove(position))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    #[allow(clippy::unwrap_used)]
+    mod mockdb_locker_mock_up_interface {
+        use crate::{
+            db::{locker_mock_up::LockerMockUpInterface, MockDb},
+            types::storage,
+        };
+
+        pub struct LockerMockUpIds {
+            card_id: String,
+            external_id: String,
+            merchant_id: String,
+            customer_id: String,
+        }
+
+        fn create_locker_mock_up_new(locker_ids: LockerMockUpIds) -> storage::LockerMockUpNew {
+            storage::LockerMockUpNew {
+                card_id: locker_ids.card_id,
+                external_id: locker_ids.external_id,
+                card_fingerprint: "card_fingerprint".into(),
+                card_global_fingerprint: "card_global_fingerprint".into(),
+                merchant_id: locker_ids.merchant_id,
+                card_number: "1234123412341234".into(),
+                card_exp_year: "2023".into(),
+                card_exp_month: "06".into(),
+                name_on_card: Some("name_on_card".into()),
+                card_cvc: Some("123".into()),
+                payment_method_id: Some("payment_method_id".into()),
+                customer_id: Some(locker_ids.customer_id),
+            }
+        }
+
+        #[tokio::test]
+        async fn find_locker_by_card_id() {
+            let mockdb = MockDb::new(&Default::default()).await;
+
+            let created_locker = mockdb
+                .insert_locker_mock_up(create_locker_mock_up_new(LockerMockUpIds {
+                    card_id: "card_1".into(),
+                    external_id: "external_1".into(),
+                    merchant_id: "merchant_1".into(),
+                    customer_id: "customer_1".into(),
+                }))
+                .await
+                .unwrap();
+
+            let _ = mockdb
+                .insert_locker_mock_up(create_locker_mock_up_new(LockerMockUpIds {
+                    card_id: "card_2".into(),
+                    external_id: "external_1".into(),
+                    merchant_id: "merchant_1".into(),
+                    customer_id: "customer_1".into(),
+                }))
+                .await;
+
+            let found_locker = mockdb.find_locker_by_card_id("card_1").await.unwrap();
+
+            assert_eq!(created_locker, found_locker)
+        }
+
+        #[tokio::test]
+        async fn insert_locker_mock_up() {
+            let mockdb = MockDb::new(&Default::default()).await;
+
+            let created_locker = mockdb
+                .insert_locker_mock_up(create_locker_mock_up_new(LockerMockUpIds {
+                    card_id: "card_1".into(),
+                    external_id: "external_1".into(),
+                    merchant_id: "merchant_1".into(),
+                    customer_id: "customer_1".into(),
+                }))
+                .await
+                .unwrap();
+
+            let found_locker = mockdb
+                .lockers
+                .lock()
+                .await
+                .iter()
+                .find(|l| l.card_id == "card_1")
+                .cloned();
+
+            assert!(found_locker.is_some());
+
+            assert_eq!(created_locker, found_locker.unwrap())
+        }
+
+        #[tokio::test]
+        async fn delete_locker_mock_up() {
+            let mockdb = MockDb::new(&Default::default()).await;
+
+            let created_locker = mockdb
+                .insert_locker_mock_up(create_locker_mock_up_new(LockerMockUpIds {
+                    card_id: "card_1".into(),
+                    external_id: "external_1".into(),
+                    merchant_id: "merchant_1".into(),
+                    customer_id: "customer_1".into(),
+                }))
+                .await
+                .unwrap();
+
+            let deleted_locker = mockdb.delete_locker_mock_up("card_1").await.unwrap();
+
+            assert_eq!(created_locker, deleted_locker);
+
+            let exist = mockdb
+                .lockers
+                .lock()
+                .await
+                .iter()
+                .any(|l| l.card_id == "card_1");
+
+            assert!(!exist)
+        }
     }
 }

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

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

## Description
<!-- Describe your changes in detail -->


### 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
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless its an obvious bug or documentation fix
that will have little conversation).
-->
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 https://github.com/juspay/hyperswitch/issues/1257.

## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
I created a unit test for all implemented methods


## 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
- [x] I added unit tests for my changes where possible
- [ ] I added a [CHANGELOG](/CHANGELOG.md) entry if applicable
