diff --git a/tests/tests_unit/test_exchange_rates.py b/tests/tests_unit/test_exchange_rates.py new file mode 100644 index 0000000..c10566a --- /dev/null +++ b/tests/tests_unit/test_exchange_rates.py @@ -0,0 +1,172 @@ +import datetime +from decimal import Decimal + +from money.currency import Currency +from money.money import Money + +from xexe.exchange_rates import ExchangeRate, ExchangeRates, add_equal_rates + + +def test_exchange_rate_creation_works(): + + a_rate = ExchangeRate( + from_currency=Currency.USD, + to_currency=Currency.EUR, + rate=Decimal("1.2"), + rate_date=datetime.date.today(), + ) + + assert a_rate.from_currency.value == "USD" + assert a_rate.to_currency.value == "EUR" + assert a_rate.rate.amount == Decimal("1.2") + assert a_rate.rate_date == datetime.date.today() + + +def test_descriptor_builds_properly(): + + a_rate = ExchangeRate( + from_currency=Currency.USD, + to_currency=Currency.EUR, + rate=Decimal("1.2"), + rate_date=datetime.date(2020, 3, 10), + ) + + assert a_rate.descriptor == "USDEUR2020-03-10" + + +def test_exchange_rates_builds_index_properly(): + + a_rate = ExchangeRate( + from_currency=Currency.USD, + to_currency=Currency.EUR, + rate=Decimal("1.2"), + rate_date=datetime.date(2020, 3, 10), + ) + another_rate = ExchangeRate( + from_currency=Currency.GBP, + to_currency=Currency.JPY, + rate=Decimal("10"), + rate_date=datetime.date(2019, 10, 3), + ) + a_third_rate = ExchangeRate( + from_currency=Currency.NZD, + to_currency=Currency.PLN, + rate=Decimal("1.2"), + rate_date=datetime.date(2025, 1, 10), + ) + + rates = ExchangeRates({a_rate, another_rate, a_third_rate}) + + assert len(rates._rate_index) == 3 + assert "USDEUR2020-03-10" in rates._rate_index + assert "GBPJPY2019-10-03" in rates._rate_index + assert "NZDPLN2025-01-10" in rates._rate_index + + +def test_present_currencies_works_fine(): + a_rate = ExchangeRate( + from_currency=Currency.USD, + to_currency=Currency.EUR, + rate=Decimal("1.2"), + rate_date=datetime.date(2020, 3, 10), + ) + another_rate = ExchangeRate( + from_currency=Currency.GBP, + to_currency=Currency.JPY, + rate=Decimal("10"), + rate_date=datetime.date(2019, 10, 3), + ) + a_third_rate = ExchangeRate( + from_currency=Currency.NZD, + to_currency=Currency.PLN, + rate=Decimal("1.2"), + rate_date=datetime.date(2025, 1, 10), + ) + + rates = ExchangeRates({a_rate, another_rate, a_third_rate}) + + assert rates.present_currencies == { + Currency.USD, + Currency.EUR, + Currency.JPY, + Currency.GBP, + Currency.NZD, + Currency.PLN, + } + + +def test_present_dates_works_fine(): + a_rate = ExchangeRate( + from_currency=Currency.USD, + to_currency=Currency.EUR, + rate=Decimal("1.2"), + rate_date=datetime.date(2020, 3, 10), + ) + another_rate = ExchangeRate( + from_currency=Currency.GBP, + to_currency=Currency.JPY, + rate=Decimal("10"), + rate_date=datetime.date(2019, 10, 3), + ) + a_third_rate = ExchangeRate( + from_currency=Currency.NZD, + to_currency=Currency.PLN, + rate=Decimal("1.2"), + rate_date=datetime.date(2025, 1, 10), + ) + + rates = ExchangeRates({a_rate, another_rate, a_third_rate}) + + assert rates.present_dates == { + datetime.date(2025, 1, 10), + datetime.date(2019, 10, 3), + datetime.date(2020, 3, 10), + } + + +def test_add_equal_rates_returns_expected_values(): + a_rate = ExchangeRate( + from_currency=Currency.USD, + to_currency=Currency.EUR, + rate=Decimal("1.2"), + rate_date=datetime.date(2020, 3, 10), + ) + another_rate = ExchangeRate( + from_currency=Currency.GBP, + to_currency=Currency.JPY, + rate=Decimal("10"), + rate_date=datetime.date(2019, 10, 3), + ) + a_third_rate = ExchangeRate( + from_currency=Currency.NZD, + to_currency=Currency.PLN, + rate=Decimal("1.2"), + rate_date=datetime.date(2025, 1, 10), + ) + + rates = ExchangeRates({a_rate, another_rate, a_third_rate}) + + assert len(rates) == 3 + + rates = add_equal_rates(rates) + + equal_rate_entry_count = len(list(rates.present_currencies)) * len( + rates.present_dates + ) + assert len(rates) == equal_rate_entry_count + 3 + assert ( + len([rate for rate in rates if rate.from_currency == rate.to_currency]) + == equal_rate_entry_count + ) + for rate in rates: + if rate.from_currency == rate.to_currency: + assert rate.amount == Decimal(1) + + +def test_add_equal_rates_runs_on_empty_rates(): + + empty_rates = ExchangeRates() + + empty_rates = add_equal_rates(empty_rates) + + assert True diff --git a/xexe/exchange_rates.py b/xexe/exchange_rates.py index c6c27ee..6276016 100644 --- a/xexe/exchange_rates.py +++ b/xexe/exchange_rates.py @@ -1,5 +1,7 @@ import datetime -from typing import Iterable, Union +from decimal import Decimal +from numbers import Number +from typing import Iterable, Set, Union from money.currency import Currency from money.money import Money @@ -11,11 +13,13 @@ class ExchangeRate: self, from_currency: Currency, to_currency: Currency, - rate: Money, + rate: Union[Money, Number], rate_date: datetime.date, ) -> None: self.from_currency = from_currency self.to_currency = to_currency + if not isinstance(rate, Money): + rate = Money(rate, to_currency) self.rate = rate self.rate_date = rate_date @@ -27,6 +31,10 @@ class ExchangeRate: + str(self.rate_date.strftime("%Y-%m-%d")) ) + @property + def amount(self) -> Decimal: + return self.rate.amount + class ExchangeRates: @@ -40,8 +48,44 @@ class ExchangeRates: raise TypeError("ExchangeRates can only hold Rates.") self._rate_index[rate.descriptor] = rate + @property + def present_currencies(self) -> Set[Currency]: + present_currencies = set() + + for rate in self: + present_currencies.add(rate.from_currency) + present_currencies.add(rate.to_currency) + + return present_currencies + + @property + def present_dates(self) -> Set[datetime.date]: + return {rate.rate_date for rate in self} + def add_rate(self, new_rate: ExchangeRate) -> None: self._rate_index[new_rate.descriptor] = new_rate def __iter__(self): return iter(self._rate_index.values()) + + def __len__(self): + return len(self._rate_index) + + +def add_equal_rates(rates: ExchangeRates) -> ExchangeRates: + + present_currencies = rates.present_currencies + present_dates = rates.present_dates + + for date in present_dates: + for currency in present_currencies: + rates.add_rate( + ExchangeRate( + from_currency=currency, + to_currency=currency, + rate=Money(1, currency), + rate_date=date, + ) + ) + + return rates