Extend unit tests for ExchangeRateService

Extend the unit tests with more granular checks.
This commit is contained in:
cd2357 2020-05-09 11:36:19 +02:00
parent 1ad7169682
commit a2c9b18f6a
No known key found for this signature in database
GPG Key ID: F26C56748514D0D3
2 changed files with 136 additions and 26 deletions

View File

@ -0,0 +1,30 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.price;
import org.apache.commons.lang3.RandomStringUtils;
/**
* Provide utility methods for other tests
*/
public abstract class TestBase {
protected static String getRandomAlphaNumericString(int length) {
return RandomStringUtils.random(length, true, true);
}
}

View File

@ -17,46 +17,120 @@
package bisq.price.spot;
import bisq.price.TestBase;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Test;
public class ExchangeRateServiceTest {
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ExchangeRateServiceTest extends TestBase {
/**
* Logback version of the Slfj logger used by {@link ExchangeRateService}. This
* allows us to test if specific messages were logged <br/><br/>
*
* See https://stackoverflow.com/a/52229629
*/
private static Logger exchangeRateServiceLogger;
private static final String LIST_APPENDER_NAME = "testListAppender";
@BeforeAll
static void setup () {
// Get the logger object for logs in ExchangeRateService
exchangeRateServiceLogger = (Logger) LoggerFactory.getLogger(ExchangeRateService.class);
// Initiate and append a ListAppender, which allows us to programmatically inspect log messages
ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
listAppender.setName(LIST_APPENDER_NAME);
listAppender.start();
exchangeRateServiceLogger.addAppender(listAppender);
}
@Test
public void getAllMarketPrices_withNoExchangeRates_logs_Exception() {
ExchangeRateProvider dummyProvider = new ExchangeRateProvider("Dummy", "YUM", Duration.ofDays(1)) {
int numberOfCurrencyPairsOnExchange = 0;
ExchangeRateProvider dummyProvider = buildDummyExchangeRateProvider(numberOfCurrencyPairsOnExchange);
ExchangeRateService service = new ExchangeRateService(Collections.singletonList(dummyProvider));
@Override
public boolean isRunning() {
return true;
}
Map<String, Object> retrievedData = service.getAllMarketPrices();
@Override
protected Set<ExchangeRate> doGet() {
return Collections.emptySet();
}
};
doSanityChecksForRetrievedData(retrievedData, dummyProvider.getPrefix(), numberOfCurrencyPairsOnExchange);
dummyProvider.start();
dummyProvider.stop();
// No exchange rates provided by this exchange, two things should happen
// A) the timestamp should be set to 0
// B) an error should be logged
List<ExchangeRateProvider> providerList = new ArrayList<>(Collections.singleton(dummyProvider));
// A) Check that timestamp was set to 0
// This allows Bisq clients to eventually disregard data from this provider
assertEquals(0L, retrievedData.get(dummyProvider.getPrefix() + "Ts"));
ExchangeRateService service = new ExchangeRateService(providerList);
service.getAllMarketPrices();
// B) Check that an error is logged
// Log msg has the format: java.lang.IllegalStateException: No exchange rate data found for ExchangeName-JzfP1
List<ILoggingEvent> logsList = ((ListAppender) exchangeRateServiceLogger.getAppender(LIST_APPENDER_NAME)).list;
assertEquals(Level.ERROR, logsList.get(0).getLevel());
assertTrue(logsList.get(0).getMessage().endsWith("No exchange rate data found for " + dummyProvider.getName()));
}
@Test
public void getAllMarketPrices_withSingleExchangeRate() {
ExchangeRateProvider dummyProvider = new ExchangeRateProvider("Dummy", "YUM", Duration.ofDays(1)) {
int numberOfCurrencyPairsOnExchange = 1;
ExchangeRateProvider dummyProvider = buildDummyExchangeRateProvider(numberOfCurrencyPairsOnExchange);
ExchangeRateService service = new ExchangeRateService(Collections.singletonList(dummyProvider));
Map<String, Object> retrievedData = service.getAllMarketPrices();
doSanityChecksForRetrievedData(retrievedData, dummyProvider.getPrefix(), numberOfCurrencyPairsOnExchange);
// One rate was provided by this exchange, so the timestamp should not be 0
assertNotEquals(0L, retrievedData.get(dummyProvider.getPrefix() + "Ts"));
}
/**
* Performs generic sanity checks on the response format and contents
*
* @param retrievedData Response data retrieved from the {@link ExchangeRateService}
* @param providerPrefix {@link ExchangeRateProvider#getPrefix()}
* @param numberOfCurrencyPairsOnExchange Number of currency pairs this exchange was initiated with
*/
private void doSanityChecksForRetrievedData(Map<String, Object> retrievedData, String providerPrefix, int numberOfCurrencyPairsOnExchange) {
// Check that 3 entries were present in the service response:
// timestamp + count + price data (stored as a list under the key "data")
assertEquals(3, retrievedData.size());
assertNotNull(retrievedData.get(providerPrefix + "Ts"));
assertNotNull(retrievedData.get(providerPrefix + "Count"));
assertNotNull(retrievedData.get("data"));
// Check that the amount of provided exchange rates matches expected value
List<String> retrievedMarketPricesData = (List<String>) retrievedData.get("data");
assertEquals(numberOfCurrencyPairsOnExchange, retrievedMarketPricesData.size());
}
private ExchangeRateProvider buildDummyExchangeRateProvider(int numberOfRatesAvailable) {
ExchangeRateProvider dummyProvider = new ExchangeRateProvider(
"ExchangeName-" + getRandomAlphaNumericString(5),
"EXCH-" + getRandomAlphaNumericString(3),
Duration.ofDays(1)) {
@Override
public boolean isRunning() {
@ -66,18 +140,24 @@ public class ExchangeRateServiceTest {
@Override
protected Set<ExchangeRate> doGet() {
HashSet<ExchangeRate> exchangeRates = new HashSet<>();
exchangeRates.add(new ExchangeRate("DUM", 0, 0L, "Dummy"));
// Simulate the required amount of rates
for (int i = 0; i < numberOfRatesAvailable; i++) {
exchangeRates.add(new ExchangeRate(
"DUM-" + getRandomAlphaNumericString(3), // random symbol, avoid duplicates
0,
System.currentTimeMillis(),
getName())); // ExchangeRateProvider name
}
return exchangeRates;
}
};
// Initialize provider
dummyProvider.start();
dummyProvider.stop();
List<ExchangeRateProvider> providerList = new ArrayList<>(Collections.singleton(dummyProvider));
ExchangeRateService service = new ExchangeRateService(providerList);
service.getAllMarketPrices();
return dummyProvider;
}
}