etopay_sdk/core/
exchange.rs

1use super::Sdk;
2use crate::{backend::dlt::get_exchange_rate, error::Result};
3use log::info;
4use rust_decimal::Decimal;
5
6impl Sdk {
7    /// Return the current exchange rate.
8    ///
9    /// # Returns
10    ///
11    /// Returns a `Result` containing the exchange rate as a `Decimal` type if successful, or a [`crate::Error`] if an error occurs.
12    // MARK10:get_exchange_rate
13    pub async fn get_exchange_rate(&self) -> Result<Decimal> {
14        info!("Fetching exchange rate from viviswap");
15        let _user = self.get_user().await?;
16
17        let access_token = self
18            .access_token
19            .as_ref()
20            .ok_or(crate::error::Error::MissingAccessToken)?;
21        let config = self.config.as_ref().ok_or(crate::Error::MissingConfig)?;
22        let network = self.active_network.clone().ok_or(crate::Error::MissingNetwork)?;
23        let exchange_rate = get_exchange_rate(config, access_token, network.key).await?;
24        Ok(exchange_rate)
25    }
26}
27
28#[cfg(test)]
29mod tests {
30    use crate::core::core_testing_utils::handle_error_test_cases;
31    use crate::testing_utils::{IOTA_NETWORK_KEY, example_api_networks};
32    use crate::types::users::ActiveUser;
33    use crate::{
34        core::Sdk,
35        error::Result,
36        testing_utils::{
37            AUTH_PROVIDER, HEADER_X_APP_NAME, TOKEN, USERNAME, example_exchange_rate_response, example_get_user,
38            set_config,
39        },
40        types::users::KycType,
41        wallet_manager::MockWalletManager,
42    };
43    use api_types::api::viviswap::detail::SwapPaymentDetailKey;
44    use mockito::Matcher;
45    use rstest::rstest;
46    use rust_decimal::Decimal;
47
48    #[rstest]
49    #[case::success(Ok(example_exchange_rate_response().course.course.0))]
50    #[case::repo_init_error(Err(crate::Error::UserRepoNotInitialized))]
51    #[case::unauthorized(Err(crate::Error::MissingAccessToken))]
52    #[case::missing_config(Err(crate::Error::MissingConfig))]
53    #[tokio::test]
54    async fn test_get_exchange_rate(#[case] expected: Result<Decimal>) {
55        // Arrange
56        let (mut srv, config, _cleanup) = set_config().await;
57        let mut sdk = Sdk::new(config).unwrap();
58        let mut mock_server = None;
59
60        match &expected {
61            Ok(_) => {
62                let mock_user_repo = example_get_user(SwapPaymentDetailKey::Iota, false, 1, KycType::Undefined);
63                sdk.repo = Some(Box::new(mock_user_repo));
64                sdk.active_user = Some(crate::types::users::ActiveUser {
65                    username: USERNAME.into(),
66                    wallet_manager: Box::new(MockWalletManager::new()),
67                });
68                sdk.access_token = Some(TOKEN.clone());
69                sdk.set_networks(example_api_networks());
70                sdk.set_network(IOTA_NETWORK_KEY.to_string()).await.unwrap();
71
72                let body = serde_json::to_string(&example_exchange_rate_response()).unwrap();
73                mock_server = Some(
74                    srv.mock("GET", "/api/courses")
75                        .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
76                        .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
77                        .match_query(Matcher::Exact("network_key=IOTA".to_string()))
78                        .with_status(200)
79                        .with_body(&body)
80                        .expect(1)
81                        .create(),
82                );
83            }
84            Err(error) => {
85                handle_error_test_cases(error, &mut sdk, 1, 1).await;
86            }
87        }
88
89        // Act
90        let response = sdk.get_exchange_rate().await;
91
92        // Assert
93        match expected {
94            Ok(resp) => {
95                assert_eq!(response.unwrap(), resp);
96            }
97            Err(ref expected_err) => {
98                assert_eq!(response.err().unwrap().to_string(), expected_err.to_string());
99            }
100        }
101        if let Some(m) = mock_server {
102            m.assert();
103        }
104    }
105
106    #[tokio::test]
107    async fn it_should_get_exchange_rate() {
108        // Arrange
109        let (mut srv, config, _cleanup) = set_config().await;
110        let mut sdk = Sdk::new(config).unwrap();
111        sdk.set_networks(example_api_networks());
112        sdk.set_network(IOTA_NETWORK_KEY.to_string()).await.unwrap();
113
114        sdk.repo = Some(Box::new(example_get_user(
115            SwapPaymentDetailKey::Iota,
116            false,
117            1,
118            KycType::Undefined, // changed to undefined because it was requiring the viviswap features
119        )));
120        sdk.active_user = Some(get_active_user());
121        sdk.access_token = Some(TOKEN.clone());
122
123        // Get exchange rate
124        let exchange_rate_mock_response = example_exchange_rate_response();
125        let body = serde_json::to_string(&exchange_rate_mock_response).unwrap();
126        let get_exchange_rate = srv
127            .mock("GET", "/api/courses?network_key=IOTA")
128            .match_header(HEADER_X_APP_NAME, AUTH_PROVIDER)
129            .match_header("authorization", format!("Bearer {}", TOKEN.as_str()).as_str())
130            .with_status(200)
131            .with_body(&body)
132            .with_header("content-type", "application/json")
133            .with_body(&body)
134            .create();
135
136        // Call function you want to test
137        let result = sdk.get_exchange_rate().await;
138
139        // Assert
140        result.unwrap();
141        get_exchange_rate.assert();
142    }
143
144    /// Create an active user
145    fn get_active_user() -> ActiveUser {
146        ActiveUser {
147            username: USERNAME.into(),
148            wallet_manager: Box::new(MockWalletManager::new()),
149        }
150    }
151}