etopay_sdk/core/
postident.rs

1//! This module includes functions for interacting with postident operations.
2
3use super::Sdk;
4use crate::backend::postident::{get_case_details, get_new_case_id, update_case_status};
5use crate::error::Result;
6use crate::types::users::KycType;
7use api_types::api::postident::{CaseDetailsResponse, NewCaseIdResponse};
8use log::info;
9
10impl Sdk {
11    /// Start kyc verification for postident
12    ///
13    /// # Returns
14    ///
15    /// Returns a `Result` containing the `NewCaseIdResponse` if successful, or a [`crate::Error`] if an error occurs.
16    ///
17    /// # Errors
18    ///
19    /// - [`crate::Error::UserRepoNotInitialized`] if the repository fails to initialize.
20    /// - [`crate::Error::UserNotInitialized)`] if the user fails to initialize.
21    /// - [`crate::Error::UserAlreadyKycVerified`] if the user is already KYC verified.
22    pub async fn start_kyc_verification_for_postident(&mut self) -> Result<NewCaseIdResponse> {
23        info!("Starting PostIdent Verification for user");
24        let Some(repo) = &mut self.repo else {
25            return Err(crate::Error::UserRepoNotInitialized);
26        };
27        let Some(active_user) = &mut self.active_user else {
28            return Err(crate::Error::UserNotInitialized);
29        };
30        let user = repo.get(&active_user.username)?;
31        if user.is_kyc_verified {
32            return Err(crate::Error::UserAlreadyKycVerified);
33        }
34        if user.kyc_type == KycType::Undefined {
35            repo.set_kyc_type(&active_user.username, KycType::Postident)?;
36        } else {
37            return Err(crate::Error::UserAlreadyKycVerified);
38        }
39
40        let access_token = self
41            .access_token
42            .as_ref()
43            .ok_or(crate::error::Error::MissingAccessToken)?;
44        let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
45        let response = get_new_case_id(config, access_token).await?;
46
47        Ok(response)
48    }
49
50    /// Get case details for postident
51    ///
52    /// # Returns
53    ///
54    /// Returns a `Result` containing the `CaseDetailsResponse` if successful, or a [`crate::Error`] if an error occurs.
55    ///
56    /// # Errors
57    ///
58    /// - [`crate::Error::UserNotInitialized)`] if the user fails to initialize.
59    pub async fn get_kyc_details_for_postident(&self) -> Result<CaseDetailsResponse> {
60        info!("Fetching KYC details for postident");
61        let Some(_user) = &self.active_user else {
62            return Err(crate::Error::UserNotInitialized);
63        };
64        let access_token = self
65            .access_token
66            .as_ref()
67            .ok_or(crate::error::Error::MissingAccessToken)?;
68        let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
69        let case_details = get_case_details(config, access_token).await?;
70        Ok(case_details)
71    }
72
73    /// Update case status for postident
74    ///
75    /// # Arguments
76    ///
77    /// - `case_id`: The ID of the case to update.
78    ///
79    /// # Errors
80    ///
81    /// Returns a `Result` containing `()` if successful, or a [`crate::Error`] if an error occurs.
82    ///
83    /// # Returns
84    ///
85    /// Returns `Ok(())` if the case status is updated successfully.
86    pub async fn update_kyc_status_for_postident(&self, case_id: &str) -> Result<()> {
87        info!("updating KYC details for postident");
88        let Some(_user) = &self.active_user else {
89            return Err(crate::Error::UserNotInitialized);
90        };
91
92        let access_token = self
93            .access_token
94            .as_ref()
95            .ok_or(crate::error::Error::MissingAccessToken)?;
96
97        let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
98        update_case_status(config, access_token, case_id).await?;
99
100        Ok(())
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107    use crate::core::core_testing_utils::handle_error_test_cases;
108    use crate::testing_utils::{
109        AUTH_PROVIDER, CASE_ID, HEADER_X_APP_NAME, TOKEN, USERNAME, example_case_details, example_get_user,
110        example_new_case_id, set_config,
111    };
112    use crate::{core::Sdk, user::MockUserRepo, wallet_manager::MockWalletManager};
113    use api_types::api::postident::{NewCaseIdResponse, UpdateCaseStatusRequest};
114    use api_types::api::viviswap::detail::SwapPaymentDetailKey;
115    use mockito::Matcher;
116    use rstest::rstest;
117
118    #[rstest]
119    #[case::success(Ok(example_new_case_id()))]
120    #[case::repo_init_error(Err(crate::Error::UserRepoNotInitialized))]
121    #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
122    #[case::user_verified_erro(Err(crate::Error::UserAlreadyKycVerified))]
123    #[case::unauthorized(Err(crate::Error::MissingAccessToken))]
124    #[case::missing_config(Err(crate::Error::MissingConfig))]
125    #[tokio::test]
126    async fn test_start_kyc_verification_for_postident(#[case] expected: Result<NewCaseIdResponse>) {
127        // Arrange
128        let (mut srv, config, _cleanup) = set_config().await;
129        let mut sdk = Sdk::new(config).unwrap();
130        let mut mock_server = None;
131
132        match &expected {
133            Ok(_) => {
134                let mut mock_user_repo = example_get_user(SwapPaymentDetailKey::Iota, false, 1, KycType::Undefined);
135                mock_user_repo
136                    .expect_set_kyc_type()
137                    .times(1)
138                    .returning(move |username, typ| {
139                        assert_eq!(username, USERNAME);
140                        assert_eq!(typ, KycType::Postident);
141                        Ok(())
142                    });
143
144                sdk.repo = Some(Box::new(mock_user_repo));
145                sdk.active_user = Some(crate::types::users::ActiveUser {
146                    username: USERNAME.into(),
147                    wallet_manager: Box::new(MockWalletManager::new()),
148                    mnemonic_derivation_options: Default::default(),
149                });
150                sdk.access_token = Some(TOKEN.clone());
151
152                let mock_response = example_new_case_id();
153                let body = serde_json::to_string(&mock_response).unwrap();
154
155                mock_server = Some(
156                    srv.mock("GET", "/api/postident/get-new-case-id")
157                        .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
158                        .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
159                        .with_status(200)
160                        .with_header("content-type", "application/json")
161                        .with_body(&body)
162                        .expect(1)
163                        .create(),
164                );
165            }
166            Err(crate::Error::MissingAccessToken) => {
167                let mut mock_user_repo = example_get_user(SwapPaymentDetailKey::Iota, false, 1, KycType::Undefined);
168                mock_user_repo
169                    .expect_set_kyc_type()
170                    .times(1)
171                    .returning(move |username, typ| {
172                        assert_eq!(username, USERNAME);
173                        assert_eq!(typ, KycType::Postident);
174                        Ok(())
175                    });
176                sdk.repo = Some(Box::new(mock_user_repo));
177                sdk.active_user = Some(crate::types::users::ActiveUser {
178                    username: USERNAME.into(),
179                    wallet_manager: Box::new(MockWalletManager::new()),
180                    mnemonic_derivation_options: Default::default(),
181                });
182                sdk.access_token = None;
183            }
184            Err(crate::Error::MissingConfig) => {
185                let mut mock_user_repo = example_get_user(SwapPaymentDetailKey::Iota, false, 1, KycType::Undefined);
186                mock_user_repo
187                    .expect_set_kyc_type()
188                    .times(1)
189                    .returning(move |username, typ| {
190                        assert_eq!(username, USERNAME);
191                        assert_eq!(typ, KycType::Postident);
192                        Ok(())
193                    });
194                sdk.repo = Some(Box::new(mock_user_repo));
195                sdk.active_user = Some(crate::types::users::ActiveUser {
196                    username: USERNAME.into(),
197                    wallet_manager: Box::new(MockWalletManager::new()),
198                    mnemonic_derivation_options: Default::default(),
199                });
200                sdk.access_token = Some(TOKEN.clone());
201                sdk.config = None;
202            }
203            Err(crate::Error::UserAlreadyKycVerified) => {
204                let mock_user_repo = example_get_user(SwapPaymentDetailKey::Iota, true, 1, KycType::Postident);
205                sdk.repo = Some(Box::new(mock_user_repo));
206                sdk.active_user = Some(crate::types::users::ActiveUser {
207                    username: USERNAME.into(),
208                    wallet_manager: Box::new(MockWalletManager::new()),
209                    mnemonic_derivation_options: Default::default(),
210                });
211            }
212            Err(error) => {
213                handle_error_test_cases(error, &mut sdk, 1, 1).await;
214            }
215        }
216
217        // Act
218        let response = sdk.start_kyc_verification_for_postident().await;
219
220        // Assert
221        match expected {
222            Ok(resp) => {
223                assert_eq!(response.unwrap(), resp);
224            }
225            Err(ref expected_err) => {
226                assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
227            }
228        }
229        if let Some(m) = mock_server {
230            m.assert();
231        }
232    }
233
234    #[rstest]
235    #[case::success(Ok(example_case_details()))]
236    #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
237    #[case::unauthorized(Err(crate::Error::MissingAccessToken))]
238    #[case::missing_config(Err(crate::Error::MissingConfig))]
239    #[tokio::test]
240    async fn test_get_postident_kyc_details(#[case] expected: Result<CaseDetailsResponse>) {
241        // Arrange
242        let (mut srv, config, _cleanup) = set_config().await;
243        let mut sdk = Sdk::new(config).unwrap();
244        let mut mock_server = None;
245
246        match &expected {
247            Ok(_) => {
248                sdk.repo = Some(Box::new(MockUserRepo::new()));
249                sdk.active_user = Some(crate::types::users::ActiveUser {
250                    username: USERNAME.into(),
251                    wallet_manager: Box::new(MockWalletManager::new()),
252                    mnemonic_derivation_options: Default::default(),
253                });
254                sdk.access_token = Some(TOKEN.clone());
255
256                let mock_response = example_case_details();
257                let body = serde_json::to_string(&mock_response).unwrap();
258
259                mock_server = Some(
260                    srv.mock("GET", "/api/postident/get-case-details")
261                        .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
262                        .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
263                        .with_status(200)
264                        .with_header("content-type", "application/json")
265                        .with_body(&body)
266                        .expect(1)
267                        .create(),
268                );
269            }
270            Err(error) => {
271                handle_error_test_cases(error, &mut sdk, 0, 0).await;
272            }
273        }
274
275        // Act
276        let response = sdk.get_kyc_details_for_postident().await;
277
278        // Assert
279        match expected {
280            Ok(resp) => {
281                assert_eq!(response.unwrap(), resp);
282            }
283            Err(ref expected_err) => {
284                assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
285            }
286        }
287        if let Some(m) = mock_server {
288            m.assert();
289        }
290    }
291
292    #[rstest]
293    #[case::success(Ok(()))]
294    #[case::user_init_error(Err(crate::Error::UserNotInitialized))]
295    #[case::unauthorized(Err(crate::Error::MissingAccessToken))]
296    #[case::missing_config(Err(crate::Error::MissingConfig))]
297    #[tokio::test]
298    async fn test_update_postident_kyc_details(#[case] expected: Result<()>) {
299        // Arrange
300        let (mut srv, config, _cleanup) = set_config().await;
301        let mut sdk = Sdk::new(config).unwrap();
302        let mut mock_server = None;
303
304        match &expected {
305            Ok(_) => {
306                sdk.repo = Some(Box::new(MockUserRepo::new()));
307                sdk.active_user = Some(crate::types::users::ActiveUser {
308                    username: USERNAME.into(),
309                    wallet_manager: Box::new(MockWalletManager::new()),
310                    mnemonic_derivation_options: Default::default(),
311                });
312                sdk.access_token = Some(TOKEN.clone());
313
314                let req = UpdateCaseStatusRequest {
315                    case_id: CASE_ID.into(),
316                };
317                let req = serde_json::to_string(&req).unwrap();
318
319                mock_server = Some(
320                    srv.mock("POST", "/api/postident/update-case-status")
321                        .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
322                        .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
323                        .with_status(202)
324                        .with_header("content-type", "application/json")
325                        .match_body(Matcher::Exact(req))
326                        .expect(1)
327                        .create(),
328                );
329            }
330            Err(error) => {
331                handle_error_test_cases(error, &mut sdk, 0, 0).await;
332            }
333        }
334
335        // Act
336        let response = sdk.update_kyc_status_for_postident(CASE_ID).await;
337
338        // Assert
339        match expected {
340            Ok(_) => response.unwrap(),
341            Err(ref err) => {
342                assert_eq!(response.unwrap_err().to_string(), err.to_string());
343            }
344        }
345        if let Some(m) = mock_server {
346            m.assert();
347        }
348    }
349}