From feb59deb285242d310e9d795d75cdb77022f6bed Mon Sep 17 00:00:00 2001 From: Sophiah Ho Date: Fri, 10 Sep 2021 17:15:57 -0400 Subject: [PATCH] Use BigDecimal instead of Double for currency rate calculations (#134) use BigDecimal instead of double for accuracy --- .../currency/CurrencyConversionManager.java | 26 ++- .../textsecuregcm/currency/FixerClient.java | 5 +- .../textsecuregcm/currency/FtxClient.java | 5 +- .../entities/CurrencyConversionEntity.java | 7 +- .../CurrencyConversionManagerTest.java | 149 +++++++++++++----- .../currency/FixerClientTest.java | 36 +++++ .../textsecuregcm/currency/FtxClientTest.java | 33 ++++ .../controllers/PaymentsControllerTest.java | 27 +++- .../test/resources/fixtures/fixer.res.json | 15 ++ .../src/test/resources/fixtures/ftx.res.json | 1 + 10 files changed, 251 insertions(+), 53 deletions(-) create mode 100644 service/src/test/java/org/whispersystems/textsecuregcm/currency/FixerClientTest.java create mode 100644 service/src/test/java/org/whispersystems/textsecuregcm/currency/FtxClientTest.java create mode 100644 service/src/test/resources/fixtures/fixer.res.json create mode 100644 service/src/test/resources/fixtures/ftx.res.json diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/currency/CurrencyConversionManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/currency/CurrencyConversionManager.java index 1e14a7b73..e9213f4e5 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/currency/CurrencyConversionManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/currency/CurrencyConversionManager.java @@ -8,6 +8,7 @@ import org.whispersystems.textsecuregcm.entities.CurrencyConversionEntityList; import org.whispersystems.textsecuregcm.util.Util; import java.io.IOException; +import java.math.BigDecimal; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -34,8 +35,8 @@ public class CurrencyConversionManager implements Managed { private long fixerUpdatedTimestamp; private long ftxUpdatedTimestamp; - private Map cachedFixerValues; - private Map cachedFtxValues; + private Map cachedFixerValues; + private Map cachedFtxValues; public CurrencyConversionManager(FixerClient fixerClient, FtxClient ftxClient, List currencies) { this.fixerClient = fixerClient; @@ -75,7 +76,7 @@ public class CurrencyConversionManager implements Managed { } if (System.currentTimeMillis() - ftxUpdatedTimestamp > FTX_INTERVAL || cachedFtxValues == null) { - Map cachedFtxValues = new HashMap<>(); + Map cachedFtxValues = new HashMap<>(); for (String currency : currencies) { cachedFtxValues.put(currency, ftxClient.getSpotPrice(currency, "USD")); @@ -87,14 +88,14 @@ public class CurrencyConversionManager implements Managed { List entities = new LinkedList<>(); - for (Map.Entry currency : cachedFtxValues.entrySet()) { - double usdValue = currency.getValue(); + for (Map.Entry currency : cachedFtxValues.entrySet()) { + BigDecimal usdValue = stripTrailingZerosAfterDecimal(currency.getValue()); - Map values = new HashMap<>(); + Map values = new HashMap<>(); values.put("USD", usdValue); - for (Map.Entry conversion : cachedFixerValues.entrySet()) { - values.put(conversion.getKey(), conversion.getValue() * usdValue); + for (Map.Entry conversion : cachedFixerValues.entrySet()) { + values.put(conversion.getKey(), stripTrailingZerosAfterDecimal(conversion.getValue().multiply(usdValue))); } entities.add(new CurrencyConversionEntity(currency.getKey(), values)); @@ -104,6 +105,15 @@ public class CurrencyConversionManager implements Managed { this.cached.set(new CurrencyConversionEntityList(entities, ftxUpdatedTimestamp)); } + private BigDecimal stripTrailingZerosAfterDecimal(BigDecimal bigDecimal) { + BigDecimal n = bigDecimal.stripTrailingZeros(); + if (n.scale() < 0) { + return n.setScale(0); + } else { + return n; + } + } + @VisibleForTesting void setFixerUpdatedTimestamp(long timestamp) { this.fixerUpdatedTimestamp = timestamp; diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/currency/FixerClient.java b/service/src/main/java/org/whispersystems/textsecuregcm/currency/FixerClient.java index 577628631..185b46aad 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/currency/FixerClient.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/currency/FixerClient.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.whispersystems.textsecuregcm.util.SystemMapper; import java.io.IOException; +import java.math.BigDecimal; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -20,7 +21,7 @@ public class FixerClient { this.client = client; } - public Map getConversionsForBase(String base) throws FixerException { + public Map getConversionsForBase(String base) throws FixerException { try { URI uri = URI.create("https://data.fixer.io/api/latest?access_key=" + apiKey + "&base=" + base); @@ -58,7 +59,7 @@ public class FixerClient { private String date; @JsonProperty - private Map rates; + private Map rates; } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/currency/FtxClient.java b/service/src/main/java/org/whispersystems/textsecuregcm/currency/FtxClient.java index edf9bedd1..ed5d4e84e 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/currency/FtxClient.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/currency/FtxClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.whispersystems.textsecuregcm.util.SystemMapper; import java.io.IOException; +import java.math.BigDecimal; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -18,7 +19,7 @@ public class FtxClient { this.client = client; } - public double getSpotPrice(String currency, String base) throws FtxException{ + public BigDecimal getSpotPrice(String currency, String base) throws FtxException{ try { URI uri = URI.create("https://ftx.com/api/markets/" + currency + "/" + base); @@ -51,7 +52,7 @@ public class FtxClient { private static class FtxResult { @JsonProperty - private double price; + private BigDecimal price; } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/CurrencyConversionEntity.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/CurrencyConversionEntity.java index 300f39e9c..c937b16fc 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/CurrencyConversionEntity.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/CurrencyConversionEntity.java @@ -2,6 +2,7 @@ package org.whispersystems.textsecuregcm.entities; import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; import java.util.Map; public class CurrencyConversionEntity { @@ -10,9 +11,9 @@ public class CurrencyConversionEntity { private String base; @JsonProperty - private Map conversions; + private Map conversions; - public CurrencyConversionEntity(String base, Map conversions) { + public CurrencyConversionEntity(String base, Map conversions) { this.base = base; this.conversions = conversions; } @@ -23,7 +24,7 @@ public class CurrencyConversionEntity { return base; } - public Map getConversions() { + public Map getConversions() { return conversions; } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/currency/CurrencyConversionManagerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/currency/CurrencyConversionManagerTest.java index cef12d74e..06dfe7757 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/currency/CurrencyConversionManagerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/currency/CurrencyConversionManagerTest.java @@ -1,18 +1,18 @@ package org.whispersystems.textsecuregcm.currency; -import org.junit.Test; -import org.whispersystems.textsecuregcm.entities.CurrencyConversionEntityList; - -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; - import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import org.junit.Test; +import org.whispersystems.textsecuregcm.entities.CurrencyConversionEntityList; + public class CurrencyConversionManagerTest { @Test @@ -20,8 +20,12 @@ public class CurrencyConversionManagerTest { FixerClient fixerClient = mock(FixerClient.class); FtxClient ftxClient = mock(FtxClient.class); - when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(2.35); - when(fixerClient.getConversionsForBase(eq("USD"))).thenReturn(Map.of("EUR", 0.822876, "FJD", 2.0577,"FKP", 0.743446)); + when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("2.35")); + when(fixerClient.getConversionsForBase(eq("USD"))).thenReturn(Map.of( + "EUR", new BigDecimal("0.822876"), + "FJD", new BigDecimal("2.0577"), + "FKP", new BigDecimal("0.743446") + )); CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, ftxClient, List.of("FOO")); @@ -32,10 +36,67 @@ public class CurrencyConversionManagerTest { assertThat(conversions.getCurrencies().size()).isEqualTo(1); assertThat(conversions.getCurrencies().get(0).getBase()).isEqualTo("FOO"); assertThat(conversions.getCurrencies().get(0).getConversions().size()).isEqualTo(4); - assertThat(conversions.getCurrencies().get(0).getConversions().get("USD")).isEqualTo(2.35); - assertThat(conversions.getCurrencies().get(0).getConversions().get("EUR")).isEqualTo(1.9337586000000002); - assertThat(conversions.getCurrencies().get(0).getConversions().get("FJD")).isEqualTo(4.8355950000000005); - assertThat(conversions.getCurrencies().get(0).getConversions().get("FKP")).isEqualTo(1.7470981); + assertThat(conversions.getCurrencies().get(0).getConversions().get("USD")).isEqualTo(new BigDecimal("2.35")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("EUR")).isEqualTo(new BigDecimal("1.9337586")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("FJD")).isEqualTo(new BigDecimal("4.835595")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("FKP")).isEqualTo(new BigDecimal("1.7470981")); + } + + @Test + public void testCurrencyCalculations_noTrailingZeros() throws IOException { + FixerClient fixerClient = mock(FixerClient.class); + FtxClient ftxClient = mock(FtxClient.class); + + when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("1.00000")); + when(fixerClient.getConversionsForBase(eq("USD"))).thenReturn(Map.of( + "EUR", new BigDecimal("0.200000"), + "FJD", new BigDecimal("3.00000"), + "FKP", new BigDecimal("50.0000"), + "CAD", new BigDecimal("700.000") + )); + + CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, ftxClient, List.of("FOO")); + + manager.updateCacheIfNecessary(); + + CurrencyConversionEntityList conversions = manager.getCurrencyConversions().orElseThrow(); + + assertThat(conversions.getCurrencies().size()).isEqualTo(1); + assertThat(conversions.getCurrencies().get(0).getBase()).isEqualTo("FOO"); + assertThat(conversions.getCurrencies().get(0).getConversions().size()).isEqualTo(5); + assertThat(conversions.getCurrencies().get(0).getConversions().get("USD")).isEqualTo(new BigDecimal("1")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("EUR")).isEqualTo(new BigDecimal("0.2")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("FJD")).isEqualTo(new BigDecimal("3")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("FKP")).isEqualTo(new BigDecimal("50")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("CAD")).isEqualTo(new BigDecimal("700")); + } + + @Test + public void testCurrencyCalculations_accuracy() throws IOException { + FixerClient fixerClient = mock(FixerClient.class); + FtxClient ftxClient = mock(FtxClient.class); + + when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("0.999999")); + when(fixerClient.getConversionsForBase(eq("USD"))).thenReturn(Map.of( + "EUR", new BigDecimal("1.000001"), + "FJD", new BigDecimal("0.000001"), + "FKP", new BigDecimal("1") + )); + + CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, ftxClient, List.of("FOO")); + + manager.updateCacheIfNecessary(); + + CurrencyConversionEntityList conversions = manager.getCurrencyConversions().orElseThrow(); + + assertThat(conversions.getCurrencies().size()).isEqualTo(1); + assertThat(conversions.getCurrencies().get(0).getBase()).isEqualTo("FOO"); + assertThat(conversions.getCurrencies().get(0).getConversions().size()).isEqualTo(4); + assertThat(conversions.getCurrencies().get(0).getConversions().get("USD")).isEqualTo(new BigDecimal("0.999999")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("EUR")).isEqualTo(new BigDecimal("0.999999999999")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("FJD")).isEqualTo(new BigDecimal("0.000000999999")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("FKP")).isEqualTo(new BigDecimal("0.999999")); + } @Test @@ -43,14 +104,18 @@ public class CurrencyConversionManagerTest { FixerClient fixerClient = mock(FixerClient.class); FtxClient ftxClient = mock(FtxClient.class); - when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(2.35); - when(fixerClient.getConversionsForBase(eq("USD"))).thenReturn(Map.of("EUR", 0.822876, "FJD", 2.0577,"FKP", 0.743446)); + when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("2.35")); + when(fixerClient.getConversionsForBase(eq("USD"))).thenReturn(Map.of( + "EUR", new BigDecimal("0.822876"), + "FJD", new BigDecimal("2.0577"), + "FKP", new BigDecimal("0.743446") + )); CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, ftxClient, List.of("FOO")); manager.updateCacheIfNecessary(); - when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(3.50); + when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("3.50")); manager.updateCacheIfNecessary(); @@ -59,10 +124,10 @@ public class CurrencyConversionManagerTest { assertThat(conversions.getCurrencies().size()).isEqualTo(1); assertThat(conversions.getCurrencies().get(0).getBase()).isEqualTo("FOO"); assertThat(conversions.getCurrencies().get(0).getConversions().size()).isEqualTo(4); - assertThat(conversions.getCurrencies().get(0).getConversions().get("USD")).isEqualTo(2.35); - assertThat(conversions.getCurrencies().get(0).getConversions().get("EUR")).isEqualTo(1.9337586000000002); - assertThat(conversions.getCurrencies().get(0).getConversions().get("FJD")).isEqualTo(4.8355950000000005); - assertThat(conversions.getCurrencies().get(0).getConversions().get("FKP")).isEqualTo(1.7470981); + assertThat(conversions.getCurrencies().get(0).getConversions().get("USD")).isEqualTo(new BigDecimal("2.35")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("EUR")).isEqualTo(new BigDecimal("1.9337586")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("FJD")).isEqualTo(new BigDecimal("4.835595")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("FKP")).isEqualTo(new BigDecimal("1.7470981")); } @Test @@ -70,14 +135,18 @@ public class CurrencyConversionManagerTest { FixerClient fixerClient = mock(FixerClient.class); FtxClient ftxClient = mock(FtxClient.class); - when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(2.35); - when(fixerClient.getConversionsForBase(eq("USD"))).thenReturn(Map.of("EUR", 0.822876, "FJD", 2.0577,"FKP", 0.743446)); + when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("2.35")); + when(fixerClient.getConversionsForBase(eq("USD"))).thenReturn(Map.of( + "EUR", new BigDecimal("0.822876"), + "FJD", new BigDecimal("2.0577"), + "FKP", new BigDecimal("0.743446") + )); CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, ftxClient, List.of("FOO")); manager.updateCacheIfNecessary(); - when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(3.50); + when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("3.50")); manager.setFtxUpdatedTimestamp(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(2) - TimeUnit.SECONDS.toMillis(1)); manager.updateCacheIfNecessary(); @@ -86,10 +155,10 @@ public class CurrencyConversionManagerTest { assertThat(conversions.getCurrencies().size()).isEqualTo(1); assertThat(conversions.getCurrencies().get(0).getBase()).isEqualTo("FOO"); assertThat(conversions.getCurrencies().get(0).getConversions().size()).isEqualTo(4); - assertThat(conversions.getCurrencies().get(0).getConversions().get("USD")).isEqualTo(3.5); - assertThat(conversions.getCurrencies().get(0).getConversions().get("EUR")).isEqualTo(2.8800660000000002); - assertThat(conversions.getCurrencies().get(0).getConversions().get("FJD")).isEqualTo(7.20195); - assertThat(conversions.getCurrencies().get(0).getConversions().get("FKP")).isEqualTo(2.602061); + assertThat(conversions.getCurrencies().get(0).getConversions().get("USD")).isEqualTo(new BigDecimal("3.5")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("EUR")).isEqualTo(new BigDecimal("2.880066")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("FJD")).isEqualTo(new BigDecimal("7.20195")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("FKP")).isEqualTo(new BigDecimal("2.602061")); } @@ -98,15 +167,23 @@ public class CurrencyConversionManagerTest { FixerClient fixerClient = mock(FixerClient.class); FtxClient ftxClient = mock(FtxClient.class); - when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(2.35); - when(fixerClient.getConversionsForBase(eq("USD"))).thenReturn(Map.of("EUR", 0.822876, "FJD", 2.0577,"FKP", 0.743446)); + when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("2.35")); + when(fixerClient.getConversionsForBase(eq("USD"))).thenReturn(Map.of( + "EUR", new BigDecimal("0.822876"), + "FJD", new BigDecimal("2.0577"), + "FKP", new BigDecimal("0.743446") + )); CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, ftxClient, List.of("FOO")); manager.updateCacheIfNecessary(); - when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(3.50); - when(fixerClient.getConversionsForBase(eq("USD"))).thenReturn(Map.of("EUR", 0.922876, "FJD", 2.0577,"FKP", 0.743446)); + when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("3.50")); + when(fixerClient.getConversionsForBase(eq("USD"))).thenReturn(Map.of( + "EUR", new BigDecimal("0.922876"), + "FJD", new BigDecimal("2.0577"), + "FKP", new BigDecimal("0.743446") + )); manager.setFixerUpdatedTimestamp(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(2) - TimeUnit.SECONDS.toMillis(1)); manager.updateCacheIfNecessary(); @@ -116,10 +193,10 @@ public class CurrencyConversionManagerTest { assertThat(conversions.getCurrencies().size()).isEqualTo(1); assertThat(conversions.getCurrencies().get(0).getBase()).isEqualTo("FOO"); assertThat(conversions.getCurrencies().get(0).getConversions().size()).isEqualTo(4); - assertThat(conversions.getCurrencies().get(0).getConversions().get("USD")).isEqualTo(2.35); - assertThat(conversions.getCurrencies().get(0).getConversions().get("EUR")).isEqualTo(2.1687586000000003); - assertThat(conversions.getCurrencies().get(0).getConversions().get("FJD")).isEqualTo(4.8355950000000005); - assertThat(conversions.getCurrencies().get(0).getConversions().get("FKP")).isEqualTo(1.7470981); + assertThat(conversions.getCurrencies().get(0).getConversions().get("USD")).isEqualTo(new BigDecimal("2.35")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("EUR")).isEqualTo(new BigDecimal("2.1687586")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("FJD")).isEqualTo(new BigDecimal("4.835595")); + assertThat(conversions.getCurrencies().get(0).getConversions().get("FKP")).isEqualTo(new BigDecimal("1.7470981")); } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/currency/FixerClientTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/currency/FixerClientTest.java new file mode 100644 index 000000000..6a31bcd3d --- /dev/null +++ b/service/src/test/java/org/whispersystems/textsecuregcm/currency/FixerClientTest.java @@ -0,0 +1,36 @@ +package org.whispersystems.textsecuregcm.currency; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.math.BigDecimal; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandler; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.whispersystems.textsecuregcm.tests.util.JsonHelpers.jsonFixture; + +public class FixerClientTest { + + @Test + public void testGetConversionsForBase() throws IOException, InterruptedException { + HttpResponse httpResponse = mock(HttpResponse.class); + when(httpResponse.statusCode()).thenReturn(200); + when(httpResponse.body()).thenReturn(jsonFixture("fixtures/fixer.res.json")); + + HttpClient httpClient = mock(HttpClient.class); + when(httpClient.send(any(HttpRequest.class), any(BodyHandler.class))).thenReturn(httpResponse); + + FixerClient fixerClient = new FixerClient(httpClient, "foo"); + Map conversions = fixerClient.getConversionsForBase("EUR"); + assertThat(conversions.get("CAD")).isEqualTo(new BigDecimal("1.560132")); + } + +} diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/currency/FtxClientTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/currency/FtxClientTest.java new file mode 100644 index 000000000..489763153 --- /dev/null +++ b/service/src/test/java/org/whispersystems/textsecuregcm/currency/FtxClientTest.java @@ -0,0 +1,33 @@ +package org.whispersystems.textsecuregcm.currency; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.whispersystems.textsecuregcm.tests.util.JsonHelpers.jsonFixture; + +import java.io.IOException; +import java.math.BigDecimal; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandler; +import org.junit.jupiter.api.Test; + +public class FtxClientTest { + + @Test + public void testGetSpotPrice() throws IOException, InterruptedException { + HttpResponse httpResponse = mock(HttpResponse.class); + when(httpResponse.statusCode()).thenReturn(200); + when(httpResponse.body()).thenReturn(jsonFixture("fixtures/ftx.res.json")); + + HttpClient httpClient = mock(HttpClient.class); + when(httpClient.send(any(HttpRequest.class), any(BodyHandler.class))).thenReturn(httpResponse); + + FtxClient ftxClient = new FtxClient(httpClient); + BigDecimal spotPrice = ftxClient.getSpotPrice("FOO", "BAR"); + assertThat(spotPrice).isEqualTo(new BigDecimal("0.8017")); + } + +} diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/PaymentsControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/PaymentsControllerTest.java index 48d0d6155..25d872c3d 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/PaymentsControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/PaymentsControllerTest.java @@ -14,6 +14,7 @@ import com.google.common.collect.ImmutableSet; import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.ResourceExtension; +import java.math.BigDecimal; import java.util.List; import java.util.Map; import java.util.Optional; @@ -52,7 +53,17 @@ class PaymentsControllerTest { @BeforeEach void setup() { when(paymentsCredentialGenerator.generateFor(eq(AuthHelper.VALID_UUID.toString()))).thenReturn(validCredentials); - when(currencyManager.getCurrencyConversions()).thenReturn(Optional.of(new CurrencyConversionEntityList(List.of(new CurrencyConversionEntity("FOO", Map.of("USD", 2.35, "EUR", 1.89)), new CurrencyConversionEntity("BAR", Map.of("USD", 1.50, "EUR", 0.98))), System.currentTimeMillis()))); + when(currencyManager.getCurrencyConversions()).thenReturn(Optional.of( + new CurrencyConversionEntityList(List.of( + new CurrencyConversionEntity("FOO", Map.of( + "USD", new BigDecimal("2.35"), + "EUR", new BigDecimal("1.89") + )), + new CurrencyConversionEntity("BAR", Map.of( + "USD", new BigDecimal("1.50"), + "EUR", new BigDecimal("0.98") + )) + ), System.currentTimeMillis()))); } @Test @@ -103,7 +114,19 @@ class PaymentsControllerTest { assertThat(conversions.getCurrencies().size()).isEqualTo(2); assertThat(conversions.getCurrencies().get(0).getBase()).isEqualTo("FOO"); - assertThat(conversions.getCurrencies().get(0).getConversions().get("USD")).isEqualTo(2.35); + assertThat(conversions.getCurrencies().get(0).getConversions().get("USD")).isEqualTo(new BigDecimal("2.35")); + } + + @Test + void testGetCurrencyConversions_Json() { + String json = + resources.getJerseyTest() + .target("/v1/payments/conversions") + .request() + .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD)) + .get(String.class); + + assertThat(json.contains("{\"USD\":2.35,\"EUR\":1.89}")); } } diff --git a/service/src/test/resources/fixtures/fixer.res.json b/service/src/test/resources/fixtures/fixer.res.json new file mode 100644 index 000000000..b388c2e7c --- /dev/null +++ b/service/src/test/resources/fixtures/fixer.res.json @@ -0,0 +1,15 @@ +{ + "success": true, + "timestamp": 1519296206, + "base": "EUR", + "date": "2021-08-01", + "rates": { + "AUD": 1.566015, + "CAD": 1.560132, + "CHF": 1.154727, + "CNY": 7.827874, + "GBP": 0.882047, + "JPY": 132.360679, + "USD": 1.23396 + } +} diff --git a/service/src/test/resources/fixtures/ftx.res.json b/service/src/test/resources/fixtures/ftx.res.json new file mode 100644 index 000000000..e7c27ff44 --- /dev/null +++ b/service/src/test/resources/fixtures/ftx.res.json @@ -0,0 +1 @@ +{"success":true,"result":{"name":"CAD/USD","enabled":true,"postOnly":false,"priceIncrement":0.0001,"sizeIncrement":1.0,"minProvideSize":1.0,"last":0.8024,"bid":0.8014,"ask":0.8017,"price":0.8017,"type":"spot","baseCurrency":"CAD","quoteCurrency":"USD","underlying":null,"restricted":false,"highLeverageFeeExempt":true,"change1h":-0.000872382851445663,"change24h":-0.0007478499314470896,"changeBod":-0.0007478499314470896,"quoteVolume24h":116.3474,"volumeUsd24h":116.3474}}