mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-21 18:58:04 +01:00
Introduce an alternative exchange rate data provider
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
package org.whispersystems.textsecuregcm.currency;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class CoinMarketCapClientTest {
|
||||
|
||||
private static final String RESPONSE_JSON = """
|
||||
{
|
||||
"status": {
|
||||
"timestamp": "2022-11-09T17:15:06.356Z",
|
||||
"error_code": 0,
|
||||
"error_message": null,
|
||||
"elapsed": 41,
|
||||
"credit_count": 1,
|
||||
"notice": null
|
||||
},
|
||||
"data": {
|
||||
"id": 7878,
|
||||
"symbol": "MOB",
|
||||
"name": "MobileCoin",
|
||||
"amount": 1,
|
||||
"last_updated": "2022-11-09T17:14:00.000Z",
|
||||
"quote": {
|
||||
"USD": {
|
||||
"price": 0.6625319895827952,
|
||||
"last_updated": "2022-11-09T17:14:00.000Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
@Test
|
||||
void parseResponse() throws JsonProcessingException {
|
||||
final CoinMarketCapClient.CoinMarketCapResponse parsedResponse = CoinMarketCapClient.parseResponse(RESPONSE_JSON);
|
||||
|
||||
assertEquals(7878, parsedResponse.priceConversionResponse().id());
|
||||
assertEquals("MOB", parsedResponse.priceConversionResponse().symbol());
|
||||
|
||||
final Map<String, CoinMarketCapClient.PriceConversionQuote> quote =
|
||||
parsedResponse.priceConversionResponse().quote();
|
||||
|
||||
assertEquals(1, quote.size());
|
||||
assertEquals(new BigDecimal("0.6625319895827952"), quote.get("USD").price());
|
||||
}
|
||||
|
||||
@Test
|
||||
void extractConversionRate() throws IOException {
|
||||
final CoinMarketCapClient.CoinMarketCapResponse parsedResponse = CoinMarketCapClient.parseResponse(RESPONSE_JSON);
|
||||
|
||||
assertEquals(new BigDecimal("0.6625319895827952"), CoinMarketCapClient.extractConversionRate(parsedResponse, "USD"));
|
||||
assertThrows(IOException.class, () -> CoinMarketCapClient.extractConversionRate(parsedResponse, "CAD"));
|
||||
}
|
||||
}
|
||||
@@ -7,27 +7,35 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.whispersystems.textsecuregcm.entities.CurrencyConversionEntityList;
|
||||
import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
|
||||
|
||||
class CurrencyConversionManagerTest {
|
||||
|
||||
@RegisterExtension
|
||||
static final RedisClusterExtension REDIS_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
|
||||
|
||||
@Test
|
||||
void testCurrencyCalculations() throws IOException {
|
||||
FixerClient fixerClient = mock(FixerClient.class);
|
||||
FtxClient ftxClient = mock(FtxClient.class);
|
||||
CoinMarketCapClient coinMarketCapClient = mock(CoinMarketCapClient.class);
|
||||
|
||||
when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("2.35"));
|
||||
when(coinMarketCapClient.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"));
|
||||
CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, coinMarketCapClient, REDIS_CLUSTER_EXTENSION.getRedisCluster(),
|
||||
List.of("FOO"), Clock.systemUTC());
|
||||
|
||||
manager.updateCacheIfNecessary();
|
||||
|
||||
@@ -45,9 +53,9 @@ class CurrencyConversionManagerTest {
|
||||
@Test
|
||||
void testCurrencyCalculations_noTrailingZeros() throws IOException {
|
||||
FixerClient fixerClient = mock(FixerClient.class);
|
||||
FtxClient ftxClient = mock(FtxClient.class);
|
||||
CoinMarketCapClient coinMarketCapClient = mock(CoinMarketCapClient.class);
|
||||
|
||||
when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("1.00000"));
|
||||
when(coinMarketCapClient.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"),
|
||||
@@ -55,7 +63,8 @@ class CurrencyConversionManagerTest {
|
||||
"CAD", new BigDecimal("700.000")
|
||||
));
|
||||
|
||||
CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, ftxClient, List.of("FOO"));
|
||||
CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, coinMarketCapClient, REDIS_CLUSTER_EXTENSION.getRedisCluster(),
|
||||
List.of("FOO"), Clock.systemUTC());
|
||||
|
||||
manager.updateCacheIfNecessary();
|
||||
|
||||
@@ -74,16 +83,17 @@ class CurrencyConversionManagerTest {
|
||||
@Test
|
||||
void testCurrencyCalculations_accuracy() throws IOException {
|
||||
FixerClient fixerClient = mock(FixerClient.class);
|
||||
FtxClient ftxClient = mock(FtxClient.class);
|
||||
CoinMarketCapClient coinMarketCapClient = mock(CoinMarketCapClient.class);
|
||||
|
||||
when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("0.999999"));
|
||||
when(coinMarketCapClient.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"));
|
||||
CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, coinMarketCapClient, REDIS_CLUSTER_EXTENSION.getRedisCluster(),
|
||||
List.of("FOO"), Clock.systemUTC());
|
||||
|
||||
manager.updateCacheIfNecessary();
|
||||
|
||||
@@ -102,20 +112,21 @@ class CurrencyConversionManagerTest {
|
||||
@Test
|
||||
void testCurrencyCalculationsTimeoutNoRun() throws IOException {
|
||||
FixerClient fixerClient = mock(FixerClient.class);
|
||||
FtxClient ftxClient = mock(FtxClient.class);
|
||||
CoinMarketCapClient coinMarketCapClient = mock(CoinMarketCapClient.class);
|
||||
|
||||
when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("2.35"));
|
||||
when(coinMarketCapClient.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"));
|
||||
CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, coinMarketCapClient, REDIS_CLUSTER_EXTENSION.getRedisCluster(),
|
||||
List.of("FOO"), Clock.systemUTC());
|
||||
|
||||
manager.updateCacheIfNecessary();
|
||||
|
||||
when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("3.50"));
|
||||
when(coinMarketCapClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("3.50"));
|
||||
|
||||
manager.updateCacheIfNecessary();
|
||||
|
||||
@@ -131,23 +142,26 @@ class CurrencyConversionManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCurrencyCalculationsFtxTimeoutWithRun() throws IOException {
|
||||
void testCurrencyCalculationsCoinMarketCapTimeoutWithRun() throws IOException {
|
||||
FixerClient fixerClient = mock(FixerClient.class);
|
||||
FtxClient ftxClient = mock(FtxClient.class);
|
||||
CoinMarketCapClient coinMarketCapClient = mock(CoinMarketCapClient.class);
|
||||
|
||||
when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("2.35"));
|
||||
when(coinMarketCapClient.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"));
|
||||
CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, coinMarketCapClient, REDIS_CLUSTER_EXTENSION.getRedisCluster(),
|
||||
List.of("FOO"), Clock.systemUTC());
|
||||
|
||||
manager.updateCacheIfNecessary();
|
||||
|
||||
when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("3.50"));
|
||||
manager.setFtxUpdatedTimestamp(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(2) - TimeUnit.SECONDS.toMillis(1));
|
||||
REDIS_CLUSTER_EXTENSION.getRedisCluster().useCluster(connection ->
|
||||
connection.sync().del(CurrencyConversionManager.COIN_MARKET_CAP_SHARED_CACHE_CURRENT_KEY));
|
||||
|
||||
when(coinMarketCapClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("3.50"));
|
||||
manager.updateCacheIfNecessary();
|
||||
|
||||
CurrencyConversionEntityList conversions = manager.getCurrencyConversions().orElseThrow();
|
||||
@@ -165,27 +179,37 @@ class CurrencyConversionManagerTest {
|
||||
@Test
|
||||
void testCurrencyCalculationsFixerTimeoutWithRun() throws IOException {
|
||||
FixerClient fixerClient = mock(FixerClient.class);
|
||||
FtxClient ftxClient = mock(FtxClient.class);
|
||||
CoinMarketCapClient coinMarketCapClient = mock(CoinMarketCapClient.class);
|
||||
|
||||
when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("2.35"));
|
||||
when(coinMarketCapClient.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"));
|
||||
final Instant currentTime = Instant.now().truncatedTo(ChronoUnit.MILLIS);
|
||||
|
||||
final Clock clock = mock(Clock.class);
|
||||
when(clock.instant()).thenReturn(currentTime);
|
||||
when(clock.millis()).thenReturn(currentTime.toEpochMilli());
|
||||
|
||||
CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, coinMarketCapClient, REDIS_CLUSTER_EXTENSION.getRedisCluster(),
|
||||
List.of("FOO"), clock);
|
||||
|
||||
manager.updateCacheIfNecessary();
|
||||
|
||||
when(ftxClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("3.50"));
|
||||
when(coinMarketCapClient.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));
|
||||
final Instant afterFixerExpiration = currentTime.plus(CurrencyConversionManager.FIXER_REFRESH_INTERVAL).plusMillis(1);
|
||||
when(clock.instant()).thenReturn(afterFixerExpiration);
|
||||
when(clock.millis()).thenReturn(afterFixerExpiration.toEpochMilli());
|
||||
|
||||
manager.updateCacheIfNecessary();
|
||||
|
||||
CurrencyConversionEntityList conversions = manager.getCurrencyConversions().orElseThrow();
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
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<String> 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"));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user