Skip to content

Commit

Permalink
currency: refactoring
Browse files Browse the repository at this point in the history
Signed-off-by: Victor Login <batazor111@gmail.com>
  • Loading branch information
batazor committed Sep 28, 2024
1 parent cd6c010 commit 9dc71c1
Show file tree
Hide file tree
Showing 17 changed files with 137 additions and 77 deletions.
52 changes: 41 additions & 11 deletions boundaries/billing/currency/src/cache/test.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
#[cfg(test)]
mod tests {
use super::*;
use crate::cache::CacheService;
use crate::domain::exchange_rate::entities::{Currency, ExchangeRate};
use rust_decimal_macros::dec;
use crate::cache::CacheService;

#[tokio::test]
async fn test_set_and_get_rate() {
let cache = CacheService::new();
let rate = ExchangeRate::new(
Currency { code: "USD".to_string(), symbol: "$".to_string() },
Currency { code: "EUR".to_string(), symbol: "€".to_string() },
Currency {
code: "USD".to_string(),
symbol: "$".to_string(),
},
Currency {
code: "EUR".to_string(),
symbol: "€".to_string(),
},
dec!(0.85),
);

Expand All @@ -31,15 +37,27 @@ mod tests {
async fn test_update_rate() {
let cache = CacheService::new();
let rate1 = ExchangeRate::new(
Currency { code: "USD".to_string(), symbol: "$".to_string() },
Currency { code: "EUR".to_string(), symbol: "€".to_string() },
Currency {
code: "USD".to_string(),
symbol: "$".to_string(),
},
Currency {
code: "EUR".to_string(),
symbol: "€".to_string(),
},
dec!(0.85),
);
cache.set_rate(&rate1).await.unwrap();

let rate2 = ExchangeRate::new(
Currency { code: "USD".to_string(), symbol: "$".to_string() },
Currency { code: "EUR".to_string(), symbol: "€".to_string() },
Currency {
code: "USD".to_string(),
symbol: "$".to_string(),
},
Currency {
code: "EUR".to_string(),
symbol: "€".to_string(),
},
dec!(0.90),
);
cache.set_rate(&rate2).await.unwrap();
Expand All @@ -52,13 +70,25 @@ mod tests {
async fn test_multiple_rates() {
let cache = CacheService::new();
let rate_usd_eur = ExchangeRate::new(
Currency { code: "USD".to_string(), symbol: "$".to_string() },
Currency { code: "EUR".to_string(), symbol: "€".to_string() },
Currency {
code: "USD".to_string(),
symbol: "$".to_string(),
},
Currency {
code: "EUR".to_string(),
symbol: "€".to_string(),
},
dec!(0.85),
);
let rate_gbp_jpy = ExchangeRate::new(
Currency { code: "GBP".to_string(), symbol: "£".to_string() },
Currency { code: "JPY".to_string(), symbol: "¥".to_string() },
Currency {
code: "GBP".to_string(),
symbol: "£".to_string(),
},
Currency {
code: "JPY".to_string(),
symbol: "¥".to_string(),
},
dec!(150.0),
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::usecases::currency_conversion::converter::traits::ICurrencyConversionUseCase;
use crate::usecases::exchange_rate::fetcher::traits::IRateFetcherUseCase;
use crate::domain::exchange_rate::entities::{Currency, ExchangeRate};
use crate::usecases::currency_conversion::traits::ICurrencyConversionUseCase;
use crate::usecases::exchange_rate::traits::IRateFetcherUseCase;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
Expand Down
11 changes: 6 additions & 5 deletions boundaries/billing/currency/src/infrastructure/http/routes.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::handlers::{get_current_exchange_rate, get_historical_exchange_rate};
use crate::usecases::currency_conversion::converter::traits::ICurrencyConversionUseCase;
use crate::usecases::exchange_rate::fetcher::traits::IRateFetcherUseCase;
use crate::usecases::currency_conversion::traits::ICurrencyConversionUseCase;
use crate::usecases::exchange_rate::traits::IRateFetcherUseCase;
use std::sync::Arc;
use warp::Filter;

Expand Down Expand Up @@ -34,7 +34,8 @@ pub fn api(
/// * `rate_fetcher` - An `Arc` pointing to a trait object implementing `IRateFetcherUseCase`.
fn with_rate_fetcher(
rate_fetcher: Arc<dyn IRateFetcherUseCase>,
) -> impl Filter<Extract = (Arc<dyn IRateFetcherUseCase>,), Error = std::convert::Infallible> + Clone {
) -> impl Filter<Extract = (Arc<dyn IRateFetcherUseCase>,), Error = std::convert::Infallible> + Clone
{
warp::any().map(move || rate_fetcher.clone())
}

Expand All @@ -45,7 +46,7 @@ fn with_rate_fetcher(
/// * `conversion_service` - An `Arc` pointing to a trait object implementing `ICurrencyConversionUseCase`.
fn with_conversion_service(
conversion_service: Arc<dyn ICurrencyConversionUseCase>,
) -> impl Filter<Extract = (Arc<dyn ICurrencyConversionUseCase>,), Error = std::convert::Infallible> + Clone
{
) -> impl Filter<Extract = (Arc<dyn ICurrencyConversionUseCase>,), Error = std::convert::Infallible>
+ Clone {
warp::any().map(move || conversion_service.clone())
}
72 changes: 51 additions & 21 deletions boundaries/billing/currency/src/infrastructure/http/test.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
#[cfg(test)]
mod tests {
use crate::usecases::currency_conversion::converter::traits::ICurrencyConversionUseCase;
use crate::usecases::exchange_rate::fetcher::traits::IRateFetcherUseCase;
use crate::domain::exchange_rate::entities::{Currency, ExchangeRate};
use crate::infrastructure::http::routes::api;
use warp::http::StatusCode;
use warp::test::request;
use serde_json::json;
use crate::usecases::currency_conversion::traits::ICurrencyConversionUseCase;
use crate::usecases::exchange_rate::traits::IRateFetcherUseCase;
use rust_decimal::Decimal;
use serde_json::json;
use std::sync::Arc;
use warp::http::StatusCode;
use warp::test::request;
use warp::Filter;
use crate::domain::exchange_rate::entities::{Currency, ExchangeRate};

/// Mock implementation of `IRateFetcherUseCase`
struct MockRateFetcherUseCase;
Expand All @@ -20,8 +20,14 @@ mod tests {
async fn fetch_rate(&self, from: &str, to: &str) -> Option<ExchangeRate> {
if from.eq_ignore_ascii_case("USD") && to.eq_ignore_ascii_case("EUR") {
Some(ExchangeRate::new(
Currency { code: "USD".to_string(), symbol: "$".to_string() },
Currency { code: "EUR".to_string(), symbol: "€".to_string() },
Currency {
code: "USD".to_string(),
symbol: "$".to_string(),
},
Currency {
code: "EUR".to_string(),
symbol: "€".to_string(),
},
Decimal::new(85, 2), // 0.85
))
} else {
Expand All @@ -30,7 +36,10 @@ mod tests {
}

/// Mock save_rate does nothing
async fn save_rate(&self, _rate: ExchangeRate) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn save_rate(
&self,
_rate: ExchangeRate,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
Ok(())
}
}
Expand All @@ -48,16 +57,30 @@ mod tests {
start_date: &str,
end_date: &str,
) -> Option<Vec<ExchangeRate>> {
if base_currency.eq_ignore_ascii_case("USD") && target_currency.eq_ignore_ascii_case("EUR") {
if base_currency.eq_ignore_ascii_case("USD")
&& target_currency.eq_ignore_ascii_case("EUR")
{
Some(vec![
ExchangeRate::new(
Currency { code: "USD".to_string(), symbol: "$".to_string() },
Currency { code: "EUR".to_string(), symbol: "€".to_string() },
Currency {
code: "USD".to_string(),
symbol: "$".to_string(),
},
Currency {
code: "EUR".to_string(),
symbol: "€".to_string(),
},
Decimal::new(84, 2), // 0.84
),
ExchangeRate::new(
Currency { code: "USD".to_string(), symbol: "$".to_string() },
Currency { code: "EUR".to_string(), symbol: "€".to_string() },
Currency {
code: "USD".to_string(),
symbol: "$".to_string(),
},
Currency {
code: "EUR".to_string(),
symbol: "€".to_string(),
},
Decimal::new(85, 2), // 0.85
),
])
Expand All @@ -70,23 +93,27 @@ mod tests {
/// Helper function to inject `IRateFetcherUseCase` mock
fn with_rate_fetcher(
rate_fetcher: Arc<dyn IRateFetcherUseCase>,
) -> impl warp::Filter<Extract = (Arc<dyn IRateFetcherUseCase>,), Error = std::convert::Infallible> + Clone {
) -> impl warp::Filter<Extract = (Arc<dyn IRateFetcherUseCase>,), Error = std::convert::Infallible>
+ Clone {
warp::any().map(move || rate_fetcher.clone())
}

/// Helper function to inject `ICurrencyConversionUseCase` mock
fn with_conversion_service(
conversion_service: Arc<dyn ICurrencyConversionUseCase>,
) -> impl warp::Filter<Extract = (Arc<dyn ICurrencyConversionUseCase>,), Error = std::convert::Infallible> + Clone
{
) -> impl warp::Filter<
Extract = (Arc<dyn ICurrencyConversionUseCase>,),
Error = std::convert::Infallible,
> + Clone {
warp::any().map(move || conversion_service.clone())
}

#[tokio::test]
async fn test_get_current_exchange_rate_success() {
// Setup mocks
let mock_rate_fetcher = Arc::new(MockRateFetcherUseCase) as Arc<dyn IRateFetcherUseCase>;
let mock_conversion_service = Arc::new(MockCurrencyConversionUseCase) as Arc<dyn ICurrencyConversionUseCase>;
let mock_conversion_service =
Arc::new(MockCurrencyConversionUseCase) as Arc<dyn ICurrencyConversionUseCase>;

// Build API filter
let api_filter = api(mock_rate_fetcher.clone(), mock_conversion_service.clone());
Expand Down Expand Up @@ -114,7 +141,8 @@ mod tests {
async fn test_get_current_exchange_rate_not_found() {
// Setup mocks
let mock_rate_fetcher = Arc::new(MockRateFetcherUseCase) as Arc<dyn IRateFetcherUseCase>;
let mock_conversion_service = Arc::new(MockCurrencyConversionUseCase) as Arc<dyn ICurrencyConversionUseCase>;
let mock_conversion_service =
Arc::new(MockCurrencyConversionUseCase) as Arc<dyn ICurrencyConversionUseCase>;

// Build API filter
let api_filter = api(mock_rate_fetcher.clone(), mock_conversion_service.clone());
Expand All @@ -134,7 +162,8 @@ mod tests {
async fn test_get_historical_exchange_rate_success() {
// Setup mocks
let mock_rate_fetcher = Arc::new(MockRateFetcherUseCase) as Arc<dyn IRateFetcherUseCase>;
let mock_conversion_service = Arc::new(MockCurrencyConversionUseCase) as Arc<dyn ICurrencyConversionUseCase>;
let mock_conversion_service =
Arc::new(MockCurrencyConversionUseCase) as Arc<dyn ICurrencyConversionUseCase>;

// Build API filter
let api_filter = api(mock_rate_fetcher.clone(), mock_conversion_service.clone());
Expand Down Expand Up @@ -166,7 +195,8 @@ mod tests {
async fn test_invalid_currency_code() {
// Setup mocks
let mock_rate_fetcher = Arc::new(MockRateFetcherUseCase) as Arc<dyn IRateFetcherUseCase>;
let mock_conversion_service = Arc::new(MockCurrencyConversionUseCase) as Arc<dyn ICurrencyConversionUseCase>;
let mock_conversion_service =
Arc::new(MockCurrencyConversionUseCase) as Arc<dyn ICurrencyConversionUseCase>;

// Build API filter
let api_filter = api(mock_rate_fetcher.clone(), mock_conversion_service.clone());
Expand Down
10 changes: 5 additions & 5 deletions boundaries/billing/currency/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use std::sync::Arc;
use tracing::info;
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::EnvFilter;
use usecases::currency_conversion::converter::ICurrencyConversionUseCase;
use usecases::exchange_rate::fetcher::RateFetcherUseCase;
use usecases::currency_conversion::ICurrencyConversionUseCase;
use usecases::exchange_rate::RateFetcherUseCase;
use utoipa::OpenApi;
use warp::Filter;

Expand All @@ -23,9 +23,9 @@ use std::env;
use crate::cache::CacheService;
use crate::repository::exchange_rate::in_memory_repository::InMemoryExchangeRateRepository;
use crate::repository::exchange_rate::repository::ExchangeRateRepository;
use usecases::exchange_rate::fetcher::mock_bloomberg_provider::MockBloombergProvider;
use usecases::exchange_rate::fetcher::mock_yahoo_provider::MockYahooProvider;
use crate::usecases::currency_conversion::converter::converter::CurrencyConversionUseCase;
use crate::usecases::currency_conversion::converter::CurrencyConversionUseCase;
use usecases::exchange_rate::mock_bloomberg_provider::MockBloombergProvider;
use usecases::exchange_rate::mock_yahoo_provider::MockYahooProvider;

#[derive(OpenApi)]
#[openapi(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::domain::currency_conversion::entities::{Amount, ConvertedAmount};
use crate::usecases::exchange_rate::fetcher::RateFetcherUseCase;
use std::sync::Arc;
use async_trait::async_trait;
use crate::usecases::currency_conversion::converter::traits::ICurrencyConversionUseCase;
use crate::domain::exchange_rate::entities::ExchangeRate;
use crate::usecases::currency_conversion::traits::ICurrencyConversionUseCase;
use crate::usecases::exchange_rate::RateFetcherUseCase;
use async_trait::async_trait;
use std::sync::Arc;

pub struct CurrencyConversionUseCase {
pub rate_fetcher: Arc<RateFetcherUseCase>,
Expand Down Expand Up @@ -40,6 +40,7 @@ impl ICurrencyConversionUseCase for CurrencyConversionUseCase {
start_date: &str,
end_date: &str,
) -> Option<Vec<ExchangeRate>> {
self.get_historical_rates(base_currency, target_currency, start_date, end_date).await
self.get_historical_rates(base_currency, target_currency, start_date, end_date)
.await
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
pub mod converter;
pub mod converter;
pub mod traits;

// Re-export the trait for easier access
pub use traits::ICurrencyConversionUseCase;

This file was deleted.

13 changes: 12 additions & 1 deletion boundaries/billing/currency/src/usecases/exchange_rate/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
pub mod fetcher;
pub mod external_rate_provider;
pub mod mock_bloomberg_provider;
pub mod mock_yahoo_provider;
pub mod rate_fetcher_use_case;
pub mod traits;

// Re-export RateFetcherUseCase for easier access
pub use rate_fetcher_use_case::RateFetcherUseCase;
pub use traits::IRateFetcherUseCase;

#[cfg(test)]
pub mod tests;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::external_rate_provider::ExternalRateProvider;
use crate::cache::cache_service::CacheService;
use crate::domain::exchange_rate::entities::ExchangeRate;
use crate::repository::exchange_rate::repository::ExchangeRateRepository;
use crate::usecases::exchange_rate::fetcher::traits::IRateFetcherUseCase;
use crate::usecases::exchange_rate::traits::IRateFetcherUseCase;
use async_trait::async_trait;
use std::error::Error;
use std::sync::Arc;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ mod tests {
use crate::cache::CacheService;
use crate::domain::exchange_rate::entities::{Currency, ExchangeRate};
use crate::repository::exchange_rate::in_memory_repository::InMemoryExchangeRateRepository;
use crate::usecases::exchange_rate::fetcher::external_rate_provider::ExternalRateProvider;
use crate::usecases::exchange_rate::fetcher::mock_bloomberg_provider::MockBloombergProvider;
use crate::usecases::exchange_rate::fetcher::mock_yahoo_provider::MockYahooProvider;
use crate::repository::exchange_rate::repository::ExchangeRateRepository;
use crate::usecases::exchange_rate::external_rate_provider::ExternalRateProvider;
use crate::usecases::exchange_rate::mock_bloomberg_provider::MockBloombergProvider;
use crate::usecases::exchange_rate::mock_yahoo_provider::MockYahooProvider;
use crate::usecases::exchange_rate::RateFetcherUseCase;
use async_trait::async_trait;
use rust_decimal_macros::dec;
use std::error::Error;
use std::sync::Arc;
use async_trait::async_trait;
use tokio::sync::Mutex;
use crate::repository::exchange_rate::repository::ExchangeRateRepository;
use crate::usecases::exchange_rate::fetcher::RateFetcherUseCase;

/// A mock provider that always fails to fetch exchange rates.
struct FailingProvider;
Expand Down
Loading

0 comments on commit 9dc71c1

Please sign in to comment.