From 46b597607d897e0c21d010f7aae827b2326895ab Mon Sep 17 00:00:00 2001 From: Milica Visekruna Date: Wed, 10 May 2023 15:25:18 +0200 Subject: [PATCH] Fix and refactor Bad Bank assignment --- .gitignore | 3 + ClassDiagram.JPG | Bin 0 -> 97122 bytes README.md | 35 +++++++ pom.xml | 26 +++++ src/main/java/ch/engenius/bank/Account.java | 68 ++++++++++--- src/main/java/ch/engenius/bank/Bank.java | 61 ++++++++++-- .../java/ch/engenius/bank/BankRunner.java | 86 ++++++++++------ .../java/ch/engenius/bank/AccountTest.java | 45 +++++++++ src/test/java/ch/engenius/bank/BankTest.java | 94 ++++++++++++++++++ 9 files changed, 368 insertions(+), 50 deletions(-) create mode 100644 .gitignore create mode 100644 ClassDiagram.JPG create mode 100644 src/test/java/ch/engenius/bank/AccountTest.java create mode 100644 src/test/java/ch/engenius/bank/BankTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6322372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/* +target/* + diff --git a/ClassDiagram.JPG b/ClassDiagram.JPG new file mode 100644 index 0000000000000000000000000000000000000000..163a8833c61308ffe4819f59dc512f7283b9f83d GIT binary patch literal 97122 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%0Du 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Á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.engenius accounts 1.0-SNAPSHOT + + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + 8 + + + + + + + org.junit.jupiter + junit-jupiter-engine + 5.8.1 + test + + + org.junit.jupiter + junit-jupiter + 5.8.1 + test + + \ No newline at end of file diff --git a/src/main/java/ch/engenius/bank/Account.java b/src/main/java/ch/engenius/bank/Account.java index b9979cb..6b891ff 100644 --- a/src/main/java/ch/engenius/bank/Account.java +++ b/src/main/java/ch/engenius/bank/Account.java @@ -1,31 +1,71 @@ package ch.engenius.bank; import java.math.BigDecimal; +import java.util.concurrent.locks.ReentrantLock; public class Account { - private double money; + private BigDecimal balance = BigDecimal.ZERO; + private final ReentrantLock lock = new ReentrantLock(); - public void withdraw(double amount) { - if ((money - amount) < 0) { - throw new IllegalStateException("not enough credits on account"); + /** + * Withdraws the specified amount from this account. + * @param amount the amount to withdraw + * @throws IllegalArgumentException if the amount is null or non-positive + * @throws IllegalStateException if there is not enough balance to cover the withdrawal + */ + public void withdraw(BigDecimal amount) { + validateAmount(amount); + lock.lock(); + try { + if (!hasEnoughBalance(amount)) { + throw new IllegalStateException("Not enough credits on account!"); + } + balance = balance.subtract(amount); + } finally { + lock.unlock(); } - setMoney(money - amount); - } - public void deposit(double amount) { - setMoney(money + amount); + /** + * Deposits the specified amount into this account. + * @param amount the amount to deposit + * @throws IllegalArgumentException if the amount is null or non-positive + */ + public void deposit(BigDecimal amount) { + validateAmount(amount); + lock.lock(); + try { + balance = balance.add(amount); + } finally { + lock.unlock(); + } } - public double getMoney() { - return money; + /** + * Returns the current balance of this account. + * @return the current balance + */ + public BigDecimal getBalance() { + return balance; } - public void setMoney(double money) { - this.money = money; + /** + * Validates the specified amount. + * @param amount the amount to validate + * @throws IllegalArgumentException if the amount is null or non-positive + */ + private void validateAmount(BigDecimal amount) { + if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) { + throw new IllegalArgumentException("Amount must be positive and not null."); + } } - public BigDecimal getMoneyAsBigDecimal() { - return BigDecimal.valueOf(money); + /** + * Checks if this account has enough balance to cover the specified amount. + * @param amount the amount to check + * @return true if there is enough balance, false otherwise + */ + private boolean hasEnoughBalance(BigDecimal amount) { + return balance.compareTo(amount) >= 0; } } diff --git a/src/main/java/ch/engenius/bank/Bank.java b/src/main/java/ch/engenius/bank/Bank.java index 571ebc7..1b8e460 100644 --- a/src/main/java/ch/engenius/bank/Bank.java +++ b/src/main/java/ch/engenius/bank/Bank.java @@ -1,18 +1,67 @@ package ch.engenius.bank; -import java.util.HashMap; +import java.math.BigDecimal; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantLock; public class Bank { - private HashMap accounts = new HashMap<>(); + private final ConcurrentHashMap accounts = new ConcurrentHashMap<>(); + private final ConcurrentHashMap locks = new ConcurrentHashMap<>(); + + /** + * Registers a new account with the bank. + * + * @param accountNumber the account number to register. + * @param amount the initial deposit amount. + * @return the newly created account. + * @throws IllegalArgumentException if the account number is already registered or if the initial deposit amount is negative. + */ + public Account registerAccount(int accountNumber, BigDecimal amount) { + if (accounts.containsKey(accountNumber)) { + throw new IllegalArgumentException("Account number " + accountNumber + " is already registered."); + } - public Account registerAccount(int accountNumber, int amount) { Account account = new Account(); - account.setMoney(amount); + account.deposit(amount); accounts.put(accountNumber, account); return account; } - public Account getAccount( int number) { - return accounts.get(number); + /** + * Retrieves an account by its number. + * + * @param number the account number to retrieve. + * @return the account with the given number. + * @throws IllegalArgumentException if no account with the given number exists. + */ + public Account getAccount(int number) { + Account account = accounts.get(number); + if (account == null) { + throw new IllegalArgumentException("Account number " + number + " does not exist."); + } + + return account; + } + + /** + * Transfers an amount of money from one account to another. + * + * @param fromAccount the number of the account to withdraw money from. + * @param toAccount the number of the account to deposit money into. + * @param amount the amount of money to transfer. + * @throws IllegalArgumentException if the source and destination accounts are the same, or if either account does not exist, + * or if the source account has insufficient funds. + */ + public void transfer(int fromAccount, int toAccount, BigDecimal amount) { + if (fromAccount == toAccount) { + throw new IllegalArgumentException("The source and destination accounts cannot be the same."); + } + + Account accOut = getAccount(fromAccount); + Account accIn = getAccount(toAccount); + + accOut.withdraw(amount); + accIn.deposit(amount); } + } diff --git a/src/main/java/ch/engenius/bank/BankRunner.java b/src/main/java/ch/engenius/bank/BankRunner.java index 10b30fd..acdb008 100644 --- a/src/main/java/ch/engenius/bank/BankRunner.java +++ b/src/main/java/ch/engenius/bank/BankRunner.java @@ -1,15 +1,23 @@ package ch.engenius.bank; import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; import java.util.Random; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; +import java.util.logging.Level; import java.util.stream.IntStream; +import java.util.logging.Logger; public class BankRunner { - private static final ExecutorService executor = Executors.newFixedThreadPool(8); + private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(8); + private static final Logger LOGGER = Logger.getLogger(BankRunner.class.getName()); + private static final int NUM_ACCOUNTS = 100; + private static final BigDecimal INITIAL_DEPOSIT = BigDecimal.valueOf(1000); + private static final int NUM_ITERATIONS = 10000; private final Random random = new Random(43); private final Bank bank = new Bank(); @@ -17,55 +25,73 @@ public class BankRunner { public static void main(String[] args) { BankRunner runner = new BankRunner(); - int accounts = 100; - int defaultDeposit = 1000; - int iterations = 10000; - runner.registerAccounts(accounts, defaultDeposit); - runner.sanityCheck(accounts, accounts*defaultDeposit); - runner.runBank(iterations, accounts); - runner.sanityCheck(accounts, accounts*defaultDeposit); + runner.registerAccounts(NUM_ACCOUNTS, INITIAL_DEPOSIT); + runner.verifyTotalBalance(NUM_ACCOUNTS, INITIAL_DEPOSIT.multiply(BigDecimal.valueOf(NUM_ACCOUNTS))); + runner.runBank(NUM_ITERATIONS, NUM_ACCOUNTS); + runner.verifyTotalBalance(NUM_ACCOUNTS, INITIAL_DEPOSIT.multiply(BigDecimal.valueOf(NUM_ACCOUNTS))); } + /** + * Executes a number of iterations of random transfers between accounts in the bank. + * @param iterations The number of iterations to run + * @param maxAccount The maximum number of accounts in the bank + */ private void runBank(int iterations, int maxAccount) { - for (int i =0; i< iterations; i++ ) { - executor.submit( ()-> runRandomOperation(maxAccount)); + List> transferTasks = new ArrayList<>(); + for (int i = 0; i < iterations; i++) { + transferTasks.add(() -> { + performRandomTransfer(maxAccount); + return null; + }); } try { - executor.shutdown(); - executor.awaitTermination(100,TimeUnit.SECONDS); + EXECUTOR.invokeAll(transferTasks); + EXECUTOR.shutdown(); } catch (InterruptedException e) { - e.printStackTrace(); + LOGGER.log(Level.SEVERE, "Execution was interrupted", e); + Thread.currentThread().interrupt(); + } finally { + EXECUTOR.shutdownNow(); } } - private void runRandomOperation(int maxAccount) { - double transfer = random.nextDouble()*100.0; + /** + * Performs a random transfer between two random accounts in the bank. + * @param maxAccount The maximum number of accounts in the bank + */ + private void performRandomTransfer(int maxAccount) { + BigDecimal transferAmount = BigDecimal.valueOf(random.nextDouble()).multiply(BigDecimal.valueOf(100)); int accountInNumber = random.nextInt(maxAccount); int accountOutNumber = random.nextInt(maxAccount); - Account accIn =bank.getAccount(accountInNumber); - Account accOut =bank.getAccount(accountOutNumber); - accIn.deposit(transfer); - accOut.withdraw(transfer); + bank.transfer(accountOutNumber, accountInNumber, transferAmount); } - private void registerAccounts(int number, int defaultMoney) { - for ( int i = 0; i < number; i++) { + /** + * Registers a specified number of accounts in the bank, each with a default amount of money. + * @param number The number of accounts to register + * @param defaultMoney The default amount of money for each account + */ + private void registerAccounts(int number, BigDecimal defaultMoney) { + for (int i = 0; i < number; i++) { bank.registerAccount(i, defaultMoney); } } - private void sanityCheck( int accountMaxNumber, int totalExpectedMoney) { + /** + * Verifies that the total balance of all accounts in the bank matches an expected value. + * @param accountMaxNumber The maximum number of accounts in the bank + * @param totalExpectedMoney The expected total balance of all accounts + */ + private void verifyTotalBalance(int accountMaxNumber, BigDecimal totalExpectedMoney) { BigDecimal sum = IntStream.range(0, accountMaxNumber) - .mapToObj( bank::getAccount) - .map ( Account::getMoneyAsBigDecimal) - .reduce( BigDecimal.ZERO, BigDecimal::add); + .mapToObj(bank::getAccount) + .map(Account::getBalance) + .reduce(BigDecimal.ZERO, BigDecimal::add); - if ( sum.intValue() != totalExpectedMoney) { + if (sum.compareTo(totalExpectedMoney) != 0) { throw new IllegalStateException("we got "+ sum + " != " + totalExpectedMoney +" (expected)"); } - System.out.println("sanity check OK"); + LOGGER.info("Total balance is OK."); } - - } diff --git a/src/test/java/ch/engenius/bank/AccountTest.java b/src/test/java/ch/engenius/bank/AccountTest.java new file mode 100644 index 0000000..543be2a --- /dev/null +++ b/src/test/java/ch/engenius/bank/AccountTest.java @@ -0,0 +1,45 @@ +package ch.engenius.bank; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +import java.math.BigDecimal; + +class AccountTest { + + private Account account; + + @BeforeEach + void setUp() { + account = new Account(); + account.deposit(new BigDecimal("1000")); + } + + @Test + void testWithdraw() { + account.withdraw(new BigDecimal("500")); + assertEquals(new BigDecimal("500"), account.getBalance()); + } + + @Test + void testWithdrawInvalidAmount() { + assertThrows(IllegalArgumentException.class, () -> account.withdraw(new BigDecimal("-100"))); + } + + @Test + void testWithdrawInsufficientBalance() { + assertThrows(IllegalStateException.class, () -> account.withdraw(new BigDecimal("1500"))); + } + + @Test + void testDeposit() { + account.deposit(new BigDecimal("500")); + assertEquals(new BigDecimal("1500"), account.getBalance()); + } + + @Test + void testDepositInvalidAmount() { + assertThrows(IllegalArgumentException.class, () -> account.deposit(new BigDecimal("-100"))); + } +} diff --git a/src/test/java/ch/engenius/bank/BankTest.java b/src/test/java/ch/engenius/bank/BankTest.java new file mode 100644 index 0000000..4faedf2 --- /dev/null +++ b/src/test/java/ch/engenius/bank/BankTest.java @@ -0,0 +1,94 @@ +package ch.engenius.bank; + +import static org.junit.jupiter.api.Assertions.*; + +import java.math.BigDecimal; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class BankTest { + + private Bank bank; + + @BeforeEach + void setUp() { + bank = new Bank(); + } + + @Test + void testRegisterAccount() { + Account account = bank.registerAccount(1, new BigDecimal("1000")); + assertEquals(new BigDecimal("1000"), account.getBalance()); + } + + @Test + void testRegisterDuplicateAccount() { + bank.registerAccount(1, new BigDecimal("1000")); + assertThrows(IllegalArgumentException.class, () -> bank.registerAccount(1, new BigDecimal("500"))); + } + + @Test + void testRegisterNegativeAmount() { + assertThrows(IllegalArgumentException.class, () -> bank.registerAccount(1, new BigDecimal("-500"))); + } + + @Test + void testGetAccount() { + Account account = bank.registerAccount(1, new BigDecimal("1000")); + assertEquals(account, bank.getAccount(1)); + } + + @Test + void testGetNonExistentAccount() { + assertThrows(IllegalArgumentException.class, () -> bank.getAccount(1)); + } + + @Test + void testTransfer() { + bank.registerAccount(1, new BigDecimal("1000")); + bank.registerAccount(2, new BigDecimal("500")); + bank.transfer(1, 2, new BigDecimal("500")); + assertEquals(new BigDecimal("500"), bank.getAccount(1).getBalance()); + assertEquals(new BigDecimal("1000"), bank.getAccount(2).getBalance()); + } + + @Test + void testTransferSameAccount() { + bank.registerAccount(1, new BigDecimal("1000")); + assertThrows(IllegalArgumentException.class, () -> bank.transfer(1, 1, new BigDecimal("500"))); + } + + @Test + void testTransferNonExistentAccounts() { + assertThrows(IllegalArgumentException.class, () -> bank.transfer(1, 2, new BigDecimal("500"))); + } + + @Test + void testTransferInsufficientFunds() { + bank.registerAccount(1, new BigDecimal("500")); + bank.registerAccount(2, new BigDecimal("500")); + assertThrows(IllegalStateException.class, () -> bank.transfer(1, 2, new BigDecimal("1000"))); + } + + @Test + void testTransferLocking() throws InterruptedException { + Bank bank = new Bank(); + Account account = bank.registerAccount(1, new BigDecimal("1000.00")); + + Thread thread1 = new Thread(() -> { + account.withdraw(new BigDecimal("500.00")); + }); + + Thread thread2 = new Thread(() -> { + assertThrows(IllegalStateException.class, () -> account.withdraw(new BigDecimal("800.00"))); + }); + + thread1.start(); + thread2.start(); + thread1.join(); + thread2.join(); + + assertEquals(new BigDecimal("500.00"), account.getBalance()); + } +}