diff --git a/SaneEconomyCore/src/main/java/org/appledash/saneeconomy/economy/EconomyManager.java b/SaneEconomyCore/src/main/java/org/appledash/saneeconomy/economy/EconomyManager.java index 268db19..4143dbb 100644 --- a/SaneEconomyCore/src/main/java/org/appledash/saneeconomy/economy/EconomyManager.java +++ b/SaneEconomyCore/src/main/java/org/appledash/saneeconomy/economy/EconomyManager.java @@ -29,6 +29,8 @@ public class EconomyManager { private final Currency currency; private final EconomyStorageBackend backend; private final String serverAccountName; + + private static final BigDecimal REQUIRED_BALANCE_ACCURACY = new BigDecimal("0.0001"); public EconomyManager(ISaneEconomy saneEconomy, Currency currency, EconomyStorageBackend backend, String serverAccountName) { this.saneEconomy = saneEconomy; @@ -84,8 +86,25 @@ public class EconomyManager { * @return True if they have requiredBalance or more, false otherwise */ public boolean hasBalance(Economable targetPlayer, BigDecimal requiredBalance) { - return (EconomableConsole.isConsole(targetPlayer)) || (this.getBalance(targetPlayer).compareTo(requiredBalance) >= 0); - + return (EconomableConsole.isConsole(targetPlayer)) || (hasBalance(this.getBalance(targetPlayer), requiredBalance)); + } + + /** + * Compare account balance and required balance to a reasonable degree of accuracy.
+ * Visible for testing + * + * @param accountBalance account balance + * @param requiredBalance required balance + * @return true if the account has the required balance to some degree of accuracy, false otherwise + */ + public boolean hasBalance(BigDecimal accountBalance, BigDecimal requiredBalance) { + if (accountBalance.compareTo(requiredBalance) >= 0) { + return true; + } + // Must compare to degree of accuracy + // See https://github.com/AppleDash/SaneEconomy/issues/100 + BigDecimal difference = requiredBalance.subtract(accountBalance); + return difference.compareTo(REQUIRED_BALANCE_ACCURACY) < 0; // difference < PRECISION } /** diff --git a/SaneEconomyCore/src/test/java/org/appledash/saneeconomy/test/EconomyManagerTest.java b/SaneEconomyCore/src/test/java/org/appledash/saneeconomy/test/EconomyManagerTest.java index feb2a75..ceffa34 100644 --- a/SaneEconomyCore/src/test/java/org/appledash/saneeconomy/test/EconomyManagerTest.java +++ b/SaneEconomyCore/src/test/java/org/appledash/saneeconomy/test/EconomyManagerTest.java @@ -18,6 +18,7 @@ import org.junit.Test; import java.math.BigDecimal; import java.text.DecimalFormat; import java.util.*; +import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; /** @@ -117,4 +118,25 @@ public class EconomyManagerTest { return true; } + + @Test + public void testHasRequiredBalance() { + for (int n = 0; n < 20; n++) { // in the absence of Junit 5's @RepeatedTest + + BigDecimal bigDecimal = randomBigDecimal(); + // We MUST modify the BigDecimal in some way otherwise the test will always succeed + // See https://github.com/AppleDash/SaneEconomy/issues/100 + for (int m = 0; m < 20; m++) { + bigDecimal = bigDecimal.add(randomBigDecimal()).subtract(randomBigDecimal()); + } + // + + Assert.assertTrue("Account must have required balance despite loss of precision (repeat " + n + ")", + economyManager.hasBalance(bigDecimal, new BigDecimal(bigDecimal.doubleValue()))); + } + } + + private static BigDecimal randomBigDecimal() { + return new BigDecimal(ThreadLocalRandom.current().nextDouble()); + } }