From 71109174f7dc5dcfbf5723d1967302c325fbfbfc Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Fri, 24 May 2024 17:19:30 +0200 Subject: [PATCH] fix: exchange method fixes --- .../ztsh/wymiana/service/CurrencyService.java | 30 +++++++--- .../wymiana/service/CurrencyServiceTest.java | 56 +++++++++++++++++-- 2 files changed, 73 insertions(+), 13 deletions(-) diff --git a/src/main/java/eu/ztsh/wymiana/service/CurrencyService.java b/src/main/java/eu/ztsh/wymiana/service/CurrencyService.java index a26b0bc..f43d6b6 100644 --- a/src/main/java/eu/ztsh/wymiana/service/CurrencyService.java +++ b/src/main/java/eu/ztsh/wymiana/service/CurrencyService.java @@ -9,6 +9,8 @@ import eu.ztsh.wymiana.web.model.CurrencyExchangeRequest; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; @@ -35,8 +37,13 @@ public class CurrencyService { } // End: unlock other currencies - var exchanged = performExchange(user.currencies().get(request.from().toUpperCase()), - user.currencies().get(request.to().toUpperCase()), + var from = user.currencies().get(request.from().toUpperCase()); + if (from == null) { + // There is no currency 'from' opened so no need to check if user has funds to exchange + throw new InsufficientFundsException(); + } + var exchanged = performExchange(from, + Optional.ofNullable(user.currencies().get(request.to().toUpperCase())).orElse(create(request.to())), Optional.ofNullable(request.toSell()).orElse(0D), Optional.ofNullable(request.toBuy()).orElse(0D)); user.currencies().putAll(exchanged); @@ -45,18 +52,23 @@ public class CurrencyService { .orElseThrow(ExchangeFailedException::new); } + private Currency create(String symbol) { + // TODO: check if supported + return new Currency(symbol.toUpperCase(), 0D); + } + private Map performExchange(Currency from, Currency to, double toSell, double toBuy) { double exchangeRate; double neededFromAmount; double requestedToAmount; if (from.symbol().equalsIgnoreCase("PLN")) { exchangeRate = nbpService.getSellRate(to.symbol()); - neededFromAmount = toBuy != 0 ? toBuy * exchangeRate : toSell; - requestedToAmount = toBuy != 0 ? toBuy : toSell / exchangeRate; + neededFromAmount = round(toBuy != 0 ? toBuy * exchangeRate : toSell); + requestedToAmount = round(toBuy != 0 ? toBuy : toSell / exchangeRate); } else { - exchangeRate = nbpService.getSellRate(from.symbol()); - neededFromAmount = toBuy != 0 ? toBuy / exchangeRate : toSell; - requestedToAmount = toBuy != 0 ? toBuy : toSell * exchangeRate; + exchangeRate = nbpService.getBuyRate(from.symbol()); + neededFromAmount = round(toBuy != 0 ? toBuy / exchangeRate : toSell); + requestedToAmount = round(toBuy != 0 ? toBuy : toSell * exchangeRate); } if (neededFromAmount > from.amount()) { throw new InsufficientFundsException(); @@ -66,4 +78,8 @@ public class CurrencyService { return Stream.of(newFrom, newTo).collect(Collectors.toMap(Currency::symbol, currency -> currency)); } + private double round(double input) { + return BigDecimal.valueOf(input).setScale(2, RoundingMode.HALF_UP).doubleValue(); + } + } diff --git a/src/test/java/eu/ztsh/wymiana/service/CurrencyServiceTest.java b/src/test/java/eu/ztsh/wymiana/service/CurrencyServiceTest.java index 13ae4ec..cc640a0 100644 --- a/src/test/java/eu/ztsh/wymiana/service/CurrencyServiceTest.java +++ b/src/test/java/eu/ztsh/wymiana/service/CurrencyServiceTest.java @@ -29,8 +29,8 @@ class CurrencyServiceTest extends RepositoryBasedTest { public CurrencyServiceTest(UserRepository userRepository, InstanceValidator instanceValidator) { super(userRepository); var nbp = Mockito.mock(NbpService.class); - Mockito.when(nbp.getSellRate("USD")).thenReturn(USD_SELL); - Mockito.when(nbp.getBuyRate("USD")).thenReturn(USD_BUY); + Mockito.when(nbp.getSellRate("USD")).thenReturn(SELL_RATE); + Mockito.when(nbp.getBuyRate("USD")).thenReturn(BUY_RATE); currencyService = new CurrencyService(new UserService(userRepository, instanceValidator), nbp, instanceValidator); } @@ -65,10 +65,11 @@ class CurrencyServiceTest extends RepositoryBasedTest { var expected = EntityCreator.user().pln(0).usd(USD_BUY).build(); expect(expected); } + @Transactional @Test void usdToPlnToSellSuccessTest() { - var entity = EntityCreator.user().build(); + var entity = EntityCreator.user().pln(0).usd(USD_SELL).build(); userRepository.save(entity); var result = currencyService.exchange(EntityCreator.exchangeRequest() .from(USD_SYMBOL) @@ -84,7 +85,7 @@ class CurrencyServiceTest extends RepositoryBasedTest { @Transactional @Test void usdToPlnToBuySuccessTest() { - var entity = EntityCreator.user().build(); + var entity = EntityCreator.user().pln(0).usd(USD_SELL).build(); userRepository.save(entity); var result = currencyService.exchange(EntityCreator.exchangeRequest() .from(USD_SYMBOL) @@ -97,14 +98,57 @@ class CurrencyServiceTest extends RepositoryBasedTest { expect(expected); } + @Transactional + @Test + void usdToPlnNoUsdCurrencyTest() { + var entity = EntityCreator.user().build(); + userRepository.save(entity); + var request = EntityCreator.exchangeRequest() + .from(USD_SYMBOL) + .to(PLN_SYMBOL) + .toBuy(PLN) + .build(); + assertThatThrownBy(() -> currencyService.exchange(request)) + .isInstanceOf(InsufficientFundsException.class); + } + + @Transactional + @Test + void doubleExchangeTest() { + var initialValue = 100; + var entity = EntityCreator.user().pln(initialValue).build(); + userRepository.save(entity); + var result1 = currencyService.exchange(EntityCreator.exchangeRequest() + .from(PLN_SYMBOL) + .to(USD_SYMBOL) + .toBuy(USD_BUY) + .build()); + var result2 = currencyService.exchange(EntityCreator.exchangeRequest() + .from(USD_SYMBOL) + .to(PLN_SYMBOL) + .toSell(USD_BUY) + .build()); + var resultOptional = userRepository.findById(entity.getPesel()); + assertThat(resultOptional) + .isNotEmpty(); + var resultEntity = resultOptional.get(); + assertThat(resultEntity.getCurrencies() + .stream() + .filter(c -> c.getSymbol().equalsIgnoreCase("PLN")) + .findFirst()).isNotEmpty().get().matches(currencyEntity -> currencyEntity.getAmount() < initialValue); + } + + @Transactional @Test void insufficientFundsTest() { - var entity = EntityCreator.exchangeRequest() + var entity = EntityCreator.user().build(); + userRepository.save(entity); + var request = EntityCreator.exchangeRequest() .from(PLN_SYMBOL) .to(USD_SYMBOL) .toBuy(PLN) .build(); - assertThatThrownBy(() -> currencyService.exchange(entity)) + assertThatThrownBy(() -> currencyService.exchange(request)) .isInstanceOf(InsufficientFundsException.class); }