diff --git a/pom.xml b/pom.xml
index 2ca15ec..905522d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,6 +7,43 @@
ch.engenius
accounts
1.0-SNAPSHOT
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 8
+ 8
+
+
+
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.24
+ provided
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.9.0-RC1
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 4.6.1
+ compile
+
+
+ org.mockito
+ mockito-core
+ 4.6.1
+ compile
+
+
\ 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
deleted file mode 100644
index b9979cb..0000000
--- a/src/main/java/ch/engenius/bank/Account.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package ch.engenius.bank;
-
-import java.math.BigDecimal;
-
-public class Account {
- private double money;
-
- public void withdraw(double amount) {
- if ((money - amount) < 0) {
- throw new IllegalStateException("not enough credits on account");
- }
- setMoney(money - amount);
-
- }
-
- public void deposit(double amount) {
- setMoney(money + amount);
- }
-
- public double getMoney() {
- return money;
- }
-
- public void setMoney(double money) {
- this.money = money;
- }
-
- public BigDecimal getMoneyAsBigDecimal() {
- return BigDecimal.valueOf(money);
- }
-}
diff --git a/src/main/java/ch/engenius/bank/Bank.java b/src/main/java/ch/engenius/bank/Bank.java
deleted file mode 100644
index 571ebc7..0000000
--- a/src/main/java/ch/engenius/bank/Bank.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package ch.engenius.bank;
-
-import java.util.HashMap;
-
-public class Bank {
- private HashMap accounts = new HashMap<>();
-
- public Account registerAccount(int accountNumber, int amount) {
- Account account = new Account();
- account.setMoney(amount);
- accounts.put(accountNumber, account);
- return account;
- }
-
- public Account getAccount( int number) {
- return accounts.get(number);
- }
-}
diff --git a/src/main/java/ch/engenius/bank/BankRunner.java b/src/main/java/ch/engenius/bank/BankRunner.java
index 10b30fd..61dc726 100644
--- a/src/main/java/ch/engenius/bank/BankRunner.java
+++ b/src/main/java/ch/engenius/bank/BankRunner.java
@@ -1,5 +1,13 @@
package ch.engenius.bank;
+import ch.engenius.bank.exception.AccountExistsException;
+import ch.engenius.bank.exception.AccountNotFoundException;
+import ch.engenius.bank.exception.IllegalAccountAmountException;
+import ch.engenius.bank.model.Account;
+import ch.engenius.bank.model.Bank;
+import ch.engenius.bank.service.AccountService;
+import ch.engenius.bank.service.BankService;
+
import java.math.BigDecimal;
import java.util.Random;
import java.util.concurrent.ExecutorService;
@@ -12,60 +20,70 @@ public class BankRunner {
private static final ExecutorService executor = Executors.newFixedThreadPool(8);
private final Random random = new Random(43);
- private final Bank bank = new Bank();
+ private Bank bank = new Bank();
+ private AccountService accountService = new AccountService();
+ private final BankService bankService = new BankService(bank, accountService);
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);
-
+ int accountsNumber = 100;
+ BigDecimal defaultDeposit = BigDecimal.valueOf(1000);
+ int iterations = 100000;
+ runner.registerAccounts(accountsNumber, defaultDeposit);
+ runner.sanityCheck(accountsNumber, BigDecimal.valueOf(accountsNumber).multiply(defaultDeposit));
+ runner.runBank(iterations, accountsNumber);
+ runner.sanityCheck(accountsNumber, BigDecimal.valueOf(accountsNumber).multiply(defaultDeposit));
}
private void runBank(int iterations, int maxAccount) {
- for (int i =0; i< iterations; i++ ) {
- executor.submit( ()-> runRandomOperation(maxAccount));
+ for (int i = 0; i < iterations; i++) {
+ executor.submit(() -> runRandomOperation(maxAccount));
}
try {
executor.shutdown();
- executor.awaitTermination(100,TimeUnit.SECONDS);
+ executor.awaitTermination(100, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void runRandomOperation(int maxAccount) {
- double transfer = random.nextDouble()*100.0;
+ BigDecimal transferAmount = BigDecimal.valueOf(random.nextDouble() * 100.0);
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);
+ try {
+ bankService.transferMoney(accountInNumber, accountOutNumber, transferAmount);
+ } catch (AccountNotFoundException accountNotFoundException) {
+ System.out.println("Account with account number:" + accountNotFoundException.getAccountNumber());
+ } catch (IllegalAccountAmountException illegalAccountAmountException) {
+ if (illegalAccountAmountException.getIllegalAmount().intValue() < 0) {
+ System.out.println("Amount for transfer can't be negative value");
+ } else {
+ System.out.println("insufficient funds on account with account number:" + accountOutNumber);
+ }
+ }
}
- private void registerAccounts(int number, int defaultMoney) {
- for ( int i = 0; i < number; i++) {
- bank.registerAccount(i, defaultMoney);
+ private void registerAccounts(int number, BigDecimal defaultMoney) {
+ for (int i = 0; i < number; i++) {
+ try {
+ bankService.registerAccount(i, defaultMoney);
+ } catch (AccountExistsException accountExistsException) {
+ System.out.println("Account with account number:" + accountExistsException.getAccountNumber() + " already exist");
+ }
}
}
- private void sanityCheck( int accountMaxNumber, int totalExpectedMoney) {
+ private void sanityCheck(int accountMaxNumber, BigDecimal totalExpectedMoney) {
BigDecimal sum = IntStream.range(0, accountMaxNumber)
- .mapToObj( bank::getAccount)
- .map ( Account::getMoneyAsBigDecimal)
- .reduce( BigDecimal.ZERO, BigDecimal::add);
+ .mapToObj(bankService::getAccount)
+ .map(Account::getMoney)
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
- if ( sum.intValue() != totalExpectedMoney) {
- throw new IllegalStateException("we got "+ sum + " != " + totalExpectedMoney +" (expected)");
+ if (sum.compareTo(totalExpectedMoney) != 0) {
+ throw new IllegalStateException("we got " + sum + " != " + totalExpectedMoney + " (expected)");
}
System.out.println("sanity check OK");
}
-
-
}
diff --git a/src/main/java/ch/engenius/bank/exception/AccountExistsException.java b/src/main/java/ch/engenius/bank/exception/AccountExistsException.java
new file mode 100644
index 0000000..5440ec8
--- /dev/null
+++ b/src/main/java/ch/engenius/bank/exception/AccountExistsException.java
@@ -0,0 +1,10 @@
+package ch.engenius.bank.exception;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public class AccountExistsException extends RuntimeException{
+ private int accountNumber;
+}
diff --git a/src/main/java/ch/engenius/bank/exception/AccountNotFoundException.java b/src/main/java/ch/engenius/bank/exception/AccountNotFoundException.java
new file mode 100644
index 0000000..7d16e26
--- /dev/null
+++ b/src/main/java/ch/engenius/bank/exception/AccountNotFoundException.java
@@ -0,0 +1,10 @@
+package ch.engenius.bank.exception;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public class AccountNotFoundException extends RuntimeException{
+ private int accountNumber;
+}
diff --git a/src/main/java/ch/engenius/bank/exception/IllegalAccountAmountException.java b/src/main/java/ch/engenius/bank/exception/IllegalAccountAmountException.java
new file mode 100644
index 0000000..e851965
--- /dev/null
+++ b/src/main/java/ch/engenius/bank/exception/IllegalAccountAmountException.java
@@ -0,0 +1,12 @@
+package ch.engenius.bank.exception;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.math.BigDecimal;
+
+@AllArgsConstructor
+@Getter
+public class IllegalAccountAmountException extends RuntimeException {
+ private BigDecimal illegalAmount;
+}
diff --git a/src/main/java/ch/engenius/bank/model/Account.java b/src/main/java/ch/engenius/bank/model/Account.java
new file mode 100644
index 0000000..7bc9866
--- /dev/null
+++ b/src/main/java/ch/engenius/bank/model/Account.java
@@ -0,0 +1,41 @@
+package ch.engenius.bank.model;
+
+import ch.engenius.bank.exception.IllegalAccountAmountException;
+import lombok.*;
+
+import java.math.BigDecimal;
+import java.util.concurrent.locks.ReentrantLock;
+
+@Getter
+@Setter
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Account {
+ private BigDecimal money;
+ private int accountNumber;
+ private ReentrantLock lock;
+
+ public void withdraw(BigDecimal amount) throws IllegalAccountAmountException {
+ validateWithdrawAmount(amount);
+ setMoney(money.subtract(amount));
+ }
+
+ public void lockAccount() {
+ this.lock.lock();
+ }
+
+ public void unlockAccount() {
+ this.lock.unlock();
+ }
+
+ public void deposit(BigDecimal amount) {
+ setMoney(money.add(amount));
+ }
+
+ private void validateWithdrawAmount(BigDecimal amount) {
+ if ((money.subtract(amount).compareTo(BigDecimal.ZERO)) < 0 || amount.compareTo(BigDecimal.ZERO) < 0) {
+ throw new IllegalAccountAmountException(amount);
+ }
+ }
+}
diff --git a/src/main/java/ch/engenius/bank/model/Bank.java b/src/main/java/ch/engenius/bank/model/Bank.java
new file mode 100644
index 0000000..8859976
--- /dev/null
+++ b/src/main/java/ch/engenius/bank/model/Bank.java
@@ -0,0 +1,20 @@
+package ch.engenius.bank.model;
+
+import lombok.Getter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Getter
+public class Bank {
+ private List accounts;
+
+ public Bank(){
+ this.accounts = new ArrayList<>();
+ }
+
+ public boolean addAccount(Account newAccount) {
+ return accounts.add(newAccount);
+ }
+
+}
diff --git a/src/main/java/ch/engenius/bank/note b/src/main/java/ch/engenius/bank/note
new file mode 100644
index 0000000..c7c9528
--- /dev/null
+++ b/src/main/java/ch/engenius/bank/note
@@ -0,0 +1,5 @@
+Another improvements that could be done is adding validation service layer, so for example we would have
+BankServiceImplementaion and BankServiceValidation. Main benefit of this would be that our implementaion
+service would we responsibility to only do business logic and not bothering with validation
+
+Another good improvement would be using one of the loggers(log4j probably) for logging certain events like creating accounts, transferring money etc.
diff --git a/src/main/java/ch/engenius/bank/service/AccountService.java b/src/main/java/ch/engenius/bank/service/AccountService.java
new file mode 100644
index 0000000..d436a81
--- /dev/null
+++ b/src/main/java/ch/engenius/bank/service/AccountService.java
@@ -0,0 +1,32 @@
+package ch.engenius.bank.service;
+
+import ch.engenius.bank.exception.IllegalAccountAmountException;
+import ch.engenius.bank.model.Account;
+
+import java.math.BigDecimal;
+
+public class AccountService {
+ //Idea of adding AccountService was not to expose directly Account model to BankService. This approach is also good for unit testing, because
+ //AccountService will just be mocked and verified that is called. it's not a good idea to put synchronization in model, so AccountService is also
+ //Proxy to Account with responsibility of synchronization
+
+ public void withdraw(Account account, BigDecimal amount) throws IllegalAccountAmountException {
+ try {
+ account.lockAccount();
+ account.withdraw(amount);
+ }
+ finally {
+ account.unlockAccount();
+ }
+ }
+
+ public void deposit(Account account, BigDecimal amount) {
+ try {
+ account.lockAccount();
+ account.deposit(amount);
+ }
+ finally {
+ account.unlockAccount();
+ }
+ }
+}
diff --git a/src/main/java/ch/engenius/bank/service/BankService.java b/src/main/java/ch/engenius/bank/service/BankService.java
new file mode 100644
index 0000000..7470d81
--- /dev/null
+++ b/src/main/java/ch/engenius/bank/service/BankService.java
@@ -0,0 +1,63 @@
+package ch.engenius.bank.service;
+
+import ch.engenius.bank.exception.AccountExistsException;
+import ch.engenius.bank.exception.AccountNotFoundException;
+import ch.engenius.bank.exception.IllegalAccountAmountException;
+import ch.engenius.bank.model.Account;
+import ch.engenius.bank.model.Bank;
+
+import java.math.BigDecimal;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class BankService {
+
+ private Bank bank;
+ private AccountService accountService;
+
+ public BankService(Bank bank, AccountService accountService) {
+ this.bank = bank;
+ this.accountService = accountService;
+ }
+
+ public void transferMoney(int accOutNumber, int accInNumber, BigDecimal amount) throws AccountNotFoundException, IllegalAccountAmountException {
+ Account accIn = getAccount(accInNumber); //I have decided to not go for a synchronization here in this case, instead I choose to put it in Account service.
+ Account accOut = getAccount(accOutNumber); //If somebody implements another service that need to use our account withdraw and deposit methods, developer
+ accountService.withdraw(accOut, amount); //of that service must also deal with synchronization. In this way we are avoiding boilerplate code
+ accountService.deposit(accIn, amount); //and also preventing potential new bugs
+ }
+
+ public Account getAccount(int accountNumber) throws AccountNotFoundException {
+ return bank.getAccounts().stream()
+ .filter(acc -> acc.getAccountNumber() == accountNumber)
+ .findFirst()
+ .orElseThrow(() -> new AccountNotFoundException(accountNumber));
+ }
+
+ public void registerAccount(int accountNumber, BigDecimal amount) throws AccountExistsException {
+ validateAccountExistence(accountNumber);
+ validateAmount(amount);
+ Account account = Account.builder()
+ .accountNumber(accountNumber)
+ .money(amount)
+ .lock(new ReentrantLock())
+ .build();
+ bank.addAccount(account);
+ }
+
+ private void validateAmount(BigDecimal amount) {
+ if (amount.compareTo(BigDecimal.ZERO) < 0) {
+ throw new IllegalAccountAmountException(amount);
+ }
+ }
+
+ private void validateAccountExistence(int accountNumber) {
+ if (accountExist(accountNumber)) {
+ throw new AccountExistsException(accountNumber);
+ }
+ }
+
+ private boolean accountExist(int accountNumber) {
+ return bank.getAccounts().stream()
+ .anyMatch(acc -> acc.getAccountNumber() == accountNumber);
+ }
+}
diff --git a/src/main/java/ch/engenius/test/account/AccountServiceTest.java b/src/main/java/ch/engenius/test/account/AccountServiceTest.java
new file mode 100644
index 0000000..6d4e311
--- /dev/null
+++ b/src/main/java/ch/engenius/test/account/AccountServiceTest.java
@@ -0,0 +1,41 @@
+package ch.engenius.test.account;
+
+import ch.engenius.bank.model.Account;
+import ch.engenius.bank.service.AccountService;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.math.BigDecimal;
+
+@ExtendWith(MockitoExtension.class)
+public class AccountServiceTest {
+ private static final BigDecimal ACCOUNT_AMOUNT = BigDecimal.ONE;
+
+ @Mock
+ private Account ACCOUNT;
+
+ @InjectMocks
+ private AccountService accountService;
+
+ @Test
+ public void withdraw_happyFlow() {
+ accountService.withdraw(ACCOUNT, ACCOUNT_AMOUNT);
+
+ Mockito.verify(ACCOUNT).lockAccount();
+ Mockito.verify(ACCOUNT).withdraw(ACCOUNT_AMOUNT);
+ Mockito.verify(ACCOUNT).unlockAccount();
+ }
+
+ @Test
+ public void deposit() {
+ accountService.deposit(ACCOUNT, ACCOUNT_AMOUNT);
+
+ Mockito.verify(ACCOUNT).lockAccount();
+ Mockito.verify(ACCOUNT).deposit(ACCOUNT_AMOUNT);
+ Mockito.verify(ACCOUNT).unlockAccount();
+ }
+}
diff --git a/src/main/java/ch/engenius/test/account/AccountTest.java b/src/main/java/ch/engenius/test/account/AccountTest.java
new file mode 100644
index 0000000..15877eb
--- /dev/null
+++ b/src/main/java/ch/engenius/test/account/AccountTest.java
@@ -0,0 +1,70 @@
+package ch.engenius.test.account;
+
+import ch.engenius.bank.exception.IllegalAccountAmountException;
+import ch.engenius.bank.model.Account;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.math.BigDecimal;
+import java.util.concurrent.locks.ReentrantLock;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@ExtendWith(MockitoExtension.class)
+public class AccountTest {
+
+ private static final BigDecimal ACCOUNT_AMOUNT = BigDecimal.ONE;
+ private static final int ACCOUNT_NUMBER = 1;
+
+ private Account account;
+
+ @BeforeEach
+ public void setUp() {
+ this.account = Account.builder()
+ .money(ACCOUNT_AMOUNT)
+ .accountNumber(ACCOUNT_NUMBER)
+ .lock(new ReentrantLock())
+ .build();
+ }
+
+ @Test
+ public void deposit_happyFlow() {
+ account.deposit(ACCOUNT_AMOUNT);
+
+ assertEquals(BigDecimal.valueOf(2), account.getMoney());
+ }
+
+ @Test
+ public void withdraw_happyFlow() {
+ account.withdraw(ACCOUNT_AMOUNT);
+
+ assertEquals(BigDecimal.ZERO, account.getMoney());
+ }
+
+ @Test
+ public void withdraw_notEnoughFunds_throwException() {
+ BigDecimal amountToWithdraw = BigDecimal.valueOf(2);
+
+ IllegalAccountAmountException actualException = assertThrows(IllegalAccountAmountException.class,
+ () -> account.withdraw(amountToWithdraw));
+
+ assertEquals(amountToWithdraw, actualException.getIllegalAmount());
+ }
+
+ @Test
+ public void lockAccount_happyFlow() {
+ account.lockAccount();
+
+ assertTrue(account.getLock().isLocked());
+ }
+
+ @Test
+ public void unlockAccount_happyFlow() {
+ account.lockAccount();
+ account.unlockAccount();
+
+ assertFalse(account.getLock().isLocked());
+ }
+}
diff --git a/src/main/java/ch/engenius/test/bank/BankServiceTest.java b/src/main/java/ch/engenius/test/bank/BankServiceTest.java
new file mode 100644
index 0000000..e665a32
--- /dev/null
+++ b/src/main/java/ch/engenius/test/bank/BankServiceTest.java
@@ -0,0 +1,131 @@
+package ch.engenius.test.bank;
+
+import ch.engenius.bank.exception.AccountExistsException;
+import ch.engenius.bank.exception.AccountNotFoundException;
+import ch.engenius.bank.exception.IllegalAccountAmountException;
+import ch.engenius.bank.model.Account;
+import ch.engenius.bank.model.Bank;
+import ch.engenius.bank.service.AccountService;
+import ch.engenius.bank.service.BankService;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.*;
+import org.mockito.junit.jupiter.*;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@ExtendWith(MockitoExtension.class)
+public class BankServiceTest {
+
+ private static final int AMOUNT_ACCOUNT_NUMBER = 1;
+ private static final int NO_AMOUNT_ACCOUNT_NUMBER = 2;
+ private static final int NOT_EXISTING_ACCOUNT_NUMBER = 3;
+ private static final BigDecimal AMOUNT_ONE = BigDecimal.ONE;
+ private static final BigDecimal AMOUNT_ZERO = BigDecimal.ZERO;
+ private static final Account AMOUNT_ACCOUNT = Account.builder()
+ .money(AMOUNT_ONE)
+ .accountNumber(AMOUNT_ACCOUNT_NUMBER)
+ .build();
+ private static final Account NO_AMOUNT_ACCOUNT = Account.builder()
+ .money(AMOUNT_ZERO)
+ .accountNumber(NO_AMOUNT_ACCOUNT_NUMBER)
+ .build();
+ @Captor
+ private ArgumentCaptor accountCaptor;
+
+ @Mock
+ private Bank bank;
+ @Mock
+ private AccountService accountService;
+
+ @InjectMocks
+ private BankService bankService = new BankService(bank, accountService);
+
+ @Test
+ public void transferMoney_happyFlow() {
+ Mockito.when(bank.getAccounts()).thenReturn(Arrays.asList(AMOUNT_ACCOUNT, NO_AMOUNT_ACCOUNT));
+
+ bankService.transferMoney(AMOUNT_ACCOUNT_NUMBER, NO_AMOUNT_ACCOUNT_NUMBER, AMOUNT_ONE);
+
+ Mockito.verify(accountService).withdraw(AMOUNT_ACCOUNT, AMOUNT_ONE);
+ Mockito.verify(accountService).deposit(NO_AMOUNT_ACCOUNT, AMOUNT_ONE);
+ }
+
+ @Test
+ public void transferMoney_accountDoesNotExist_throwsException(){
+ Mockito.when(bank.getAccounts()).thenReturn(Arrays.asList(AMOUNT_ACCOUNT, NO_AMOUNT_ACCOUNT));
+
+ AccountNotFoundException actualException = assertThrows(AccountNotFoundException.class,
+ ()-> bankService.transferMoney( NO_AMOUNT_ACCOUNT_NUMBER,NOT_EXISTING_ACCOUNT_NUMBER,AMOUNT_ONE));
+
+ assertEquals(NOT_EXISTING_ACCOUNT_NUMBER, actualException.getAccountNumber());
+ }
+
+ @Test
+ public void getAccount_happyFlow(){
+ Mockito.when(bank.getAccounts()).thenReturn(Arrays.asList(NO_AMOUNT_ACCOUNT, AMOUNT_ACCOUNT));
+
+ Account actualAccount = bankService.getAccount(AMOUNT_ACCOUNT_NUMBER);
+
+ assertEquals(AMOUNT_ACCOUNT_NUMBER, actualAccount.getAccountNumber());
+ assertEquals(AMOUNT_ONE, actualAccount.getMoney());
+ }
+
+ @Test
+ public void getAccount_accountDoesNotExist_throwsException(){
+ Mockito.when(bank.getAccounts()).thenReturn(Arrays.asList(NO_AMOUNT_ACCOUNT, AMOUNT_ACCOUNT));
+
+
+ AccountNotFoundException actualException = assertThrows(AccountNotFoundException.class,
+ () -> bankService.getAccount(NOT_EXISTING_ACCOUNT_NUMBER));
+
+ assertEquals(NOT_EXISTING_ACCOUNT_NUMBER, actualException.getAccountNumber());
+ }
+
+ @Test
+ public void registerAccount_happyFlow(){
+ List accounts = new ArrayList(Arrays.asList(NO_AMOUNT_ACCOUNT, AMOUNT_ACCOUNT));
+
+ Mockito.when(bank.getAccounts()).thenReturn(accounts);
+
+ bankService.registerAccount(NOT_EXISTING_ACCOUNT_NUMBER, AMOUNT_ZERO);
+
+ Mockito.verify(bank).addAccount(accountCaptor.capture());
+
+ Account capturedAccount = accountCaptor.getValue();
+ assertEquals(NOT_EXISTING_ACCOUNT_NUMBER, capturedAccount.getAccountNumber());
+ assertEquals(AMOUNT_ZERO, capturedAccount.getMoney());
+ }
+
+ @Test
+ public void registerAccount_accountAlreadyExist_throwException(){
+ List accounts = new ArrayList(Arrays.asList(NO_AMOUNT_ACCOUNT, AMOUNT_ACCOUNT));
+
+ Mockito.when(bank.getAccounts()).thenReturn(accounts);
+
+ AccountExistsException actualException = assertThrows(AccountExistsException.class,
+ () -> bankService.registerAccount(NO_AMOUNT_ACCOUNT_NUMBER, AMOUNT_ZERO));
+
+ assertEquals(NO_AMOUNT_ACCOUNT_NUMBER, actualException.getAccountNumber());
+ }
+
+ @Test
+ public void registerAccount_negativeAmountNewAccount_throwsException(){
+ List accounts = new ArrayList(Arrays.asList(NO_AMOUNT_ACCOUNT, AMOUNT_ACCOUNT));
+ BigDecimal negativeAmount = BigDecimal.valueOf(-1);
+
+ Mockito.when(bank.getAccounts()).thenReturn(accounts);
+
+ IllegalAccountAmountException actualException = assertThrows(IllegalAccountAmountException.class,
+ () -> bankService.registerAccount(NOT_EXISTING_ACCOUNT_NUMBER, negativeAmount));
+
+ assertEquals(negativeAmount, actualException.getIllegalAmount());
+ }
+
+}
diff --git a/src/main/java/ch/engenius/test/bank/BankTest.java b/src/main/java/ch/engenius/test/bank/BankTest.java
new file mode 100644
index 0000000..c9b9614
--- /dev/null
+++ b/src/main/java/ch/engenius/test/bank/BankTest.java
@@ -0,0 +1,30 @@
+package ch.engenius.test.bank;
+
+import ch.engenius.bank.model.Account;
+import ch.engenius.bank.model.Bank;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@ExtendWith(MockitoExtension.class)
+public class BankTest {
+
+ private Bank bank;
+
+ @BeforeEach
+ public void setUp() {
+ this.bank = new Bank();
+ }
+
+ @Test
+ public void addAccount_happyFlow() {
+ Account account = new Account();
+
+ bank.addAccount(account);
+
+ assertEquals(1, bank.getAccounts().size());
+ }
+}