diff --git a/lesson_17/bank/bank_app/build.gradle.kts b/lesson_17/bank/bank_app/build.gradle.kts index 5f768d84..9bd44a0b 100644 --- a/lesson_17/bank/bank_app/build.gradle.kts +++ b/lesson_17/bank/bank_app/build.gradle.kts @@ -28,6 +28,7 @@ dependencies { implementation("com.google.code.gson:gson:2.11.0") implementation("org.projectlombok:lombok:1.18.30") implementation("org.springframework.boot:spring-boot-starter") + } application { diff --git a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/AuditLog.java b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/AuditLog.java new file mode 100644 index 00000000..5f55e704 --- /dev/null +++ b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/AuditLog.java @@ -0,0 +1,50 @@ +package com.codedifferently.lesson17.bank; + +import java.util.ArrayList; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * The {@code AuditLog} class is responsible for logging important events in the banking system. It + * uses the Log4j logging framework to log messages at different levels. + */ +public class AuditLog { + + private static final Logger logger = LogManager.getLogger(AuditLog.class.getName()); + private final List logEntries = new ArrayList<>(); + + /** + * Logs a message at the INFO level and adds it to the internal list of log entries. + * + * @param message + */ + public void log(String message) { + logger.info(message); + logEntries.add(message); + } + + /** + * Logs an error message at the ERROR level and adds it to the internal list of log entries. + * + * @param message + */ + public void logError(String message) { + logger.error(message); + logEntries.add(message); + } + + /** + * Returns the list of log entries. + * + * @return A list of log entries + */ + public List getLogEntries() { + return logEntries; + } + + public void clear() { + logEntries.clear(); + logger.debug("Audit log cleared"); + } +} diff --git a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/BankAtm.java b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/BankAtm.java index 8cbcd3cc..92a9f042 100644 --- a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/BankAtm.java +++ b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/BankAtm.java @@ -11,6 +11,7 @@ public class BankAtm { private final Map customerById = new HashMap<>(); private final Map accountByNumber = new HashMap<>(); + private final AuditLog auditLog = new AuditLog(); /** * Adds a checking account to the bank. @@ -25,6 +26,7 @@ public void addAccount(CheckingAccount account) { owner -> { customerById.put(owner.getId(), owner); }); + auditLog.log("Added account " + account.getAccountNumber() + " to the bank"); } /** @@ -34,6 +36,7 @@ public void addAccount(CheckingAccount account) { * @return The unique set of accounts owned by the customer. */ public Set findAccountsByCustomerId(UUID customerId) { + auditLog.log("Finding accounts for customer " + customerId); return customerById.containsKey(customerId) ? customerById.get(customerId).getAccounts() : Set.of(); @@ -48,6 +51,7 @@ public Set findAccountsByCustomerId(UUID customerId) { public void depositFunds(String accountNumber, double amount) { CheckingAccount account = getAccountOrThrow(accountNumber); account.deposit(amount); + auditLog.log("Deposited " + amount + " into account " + accountNumber); } /** @@ -58,7 +62,9 @@ public void depositFunds(String accountNumber, double amount) { */ public void depositFunds(String accountNumber, Check check) { CheckingAccount account = getAccountOrThrow(accountNumber); + check.depositFunds(account); + auditLog.log("Deposited check into account " + accountNumber); } /** @@ -67,9 +73,11 @@ public void depositFunds(String accountNumber, Check check) { * @param accountNumber * @param amount */ - public void withdrawFunds(String accountNumber, double amount) { + public void withdrawFunds(String accountNumber, double amount, Check check) { CheckingAccount account = getAccountOrThrow(accountNumber); account.withdraw(amount); + + auditLog.log("Withdrew " + amount + " from account " + accountNumber); } /** @@ -83,6 +91,7 @@ private CheckingAccount getAccountOrThrow(String accountNumber) { if (account == null || account.isClosed()) { throw new AccountNotFoundException("Account not found"); } + auditLog.log("Retrieved account " + accountNumber); return account; } } diff --git a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/CheckingAccount.java b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/CheckingAccount.java index 5d8aeb74..45cf8675 100644 --- a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/CheckingAccount.java +++ b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/CheckingAccount.java @@ -68,9 +68,11 @@ public void withdraw(double amount) throws InsufficientFundsException { if (isClosed()) { throw new IllegalStateException("Cannot withdraw from a closed account"); } + if (amount <= 0) { throw new IllegalStateException("Withdrawal amount must be positive"); } + if (balance < amount) { throw new InsufficientFundsException("Account does not have enough funds for withdrawal"); } diff --git a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/SavingsAccount.java b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/SavingsAccount.java new file mode 100644 index 00000000..ac1f1152 --- /dev/null +++ b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/SavingsAccount.java @@ -0,0 +1,90 @@ +package com.codedifferently.lesson17.bank; + +import com.codedifferently.lesson17.bank.exceptions.InsufficientFundsException; +import java.util.Set; + +/** + * The {@code SavingsAccount} class represents a savings account in a banking system. It extends the + * {@code CheckingAccount} class and provides additional functionality specific to savings accounts. + */ +public class SavingsAccount extends CheckingAccount { + + /** + * Constructs a new SavingsAccount with the specified account number, owners, and balance. + * + * @param accountNumber + * @param owners + * @param balance + */ + public SavingsAccount(String accountNumber, Set owners, double balance) { + super(accountNumber, owners, balance); + } + + /** + * Gets the HashCode of the account number. + * + * @return The hash code of the account number. + */ + @Override + public int hashCode() { + return getAccountNumber().hashCode(); + } + + /** + * Checks if this SavingsAccount is equal to another object. + * + * @param obj The object to compare with. + * @return true if the objects are equal, false otherwise. + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof SavingsAccount other) { + return getAccountNumber().equals(other.getAccountNumber()) + && getOwners().equals(other.getOwners()) + && getBalance() == other.getBalance() + && isClosed() == other.isClosed(); + } + return false; + } + + /** + * Will throw and exception if the user attempt to make a withdrawl while under the instance of + * SavingsAccount. + * + *

And the makes a second withdrawl that will check if there is a check and throw an error if + * one comes in. + * + * @param amount The amount to deposit. + */ + @Override + public void withdraw(double amount) throws InsufficientFundsException { + throw new IllegalStateException("you cannot write a check from the savings account"); + } + + public void withdraw(double amount, Check check) { + if (check != null) { + throw new IllegalStateException("Cannot withdraw from a savings account using a check"); + } + withdraw(amount); // Call the original withdraw method + } + + /** + * Returns a string representation of the SavingsAccount. + * + * @return A string representation of the SavingsAccount. + */ + @Override + public String toString() { + return "SavingsAccount" + + "accountNumber='" + + getAccountNumber() + + '\'' + + ", owners=" + + getOwners() + + ", balance=" + + getBalance() + + ", isActive=" + + isClosed() + + '}'; + } +} diff --git a/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/AuditLogTest.java b/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/AuditLogTest.java new file mode 100644 index 00000000..475f2e14 --- /dev/null +++ b/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/AuditLogTest.java @@ -0,0 +1,38 @@ +package com.codedifferently.lesson17.bank; + +import org.junit.jupiter.api.Test; + +public class AuditLogTest { + @Test + public void testLog() { + AuditLog auditLog = new AuditLog(); + + String message = "Test log message"; + auditLog.log(message); + } + + @Test + public void testGetLogEntries() { + AuditLog auditLog = new AuditLog(); + + String message1 = "Test log message 1"; + String message2 = "Test log message 2"; + auditLog.log(message1); + auditLog.log(message2); + + assert auditLog.getLogEntries().size() == 2; + assert auditLog.getLogEntries().get(0).equals(message1); + assert auditLog.getLogEntries().get(1).equals(message2); + } + + @Test + public void testClear() { + AuditLog auditLog = new AuditLog(); + + String message = "Test log message"; + auditLog.log(message); + auditLog.clear(); + + assert auditLog.getLogEntries().isEmpty(); + } +} diff --git a/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/BankAtmTest.java b/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/BankAtmTest.java index fa4a913a..2fb08ed9 100644 --- a/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/BankAtmTest.java +++ b/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/BankAtmTest.java @@ -92,7 +92,7 @@ void testDepositFunds_DoesntDepositCheckTwice() { @Test void testWithdrawFunds() { // Act - classUnderTest.withdrawFunds(account2.getAccountNumber(), 50.0); + classUnderTest.withdrawFunds(account2.getAccountNumber(), 50.0, null); // Assert assertThat(account2.getBalance()).isEqualTo(150.0); @@ -104,7 +104,7 @@ void testWithdrawFunds_AccountNotFound() { // Act & Assert assertThatExceptionOfType(AccountNotFoundException.class) - .isThrownBy(() -> classUnderTest.withdrawFunds(nonExistingAccountNumber, 50.0)) + .isThrownBy(() -> classUnderTest.withdrawFunds(nonExistingAccountNumber, 50.0, null)) .withMessage("Account not found"); } } diff --git a/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/CheckingAccountTest.java b/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/CheckingAccountTest.java index f155d8e5..49332a11 100644 --- a/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/CheckingAccountTest.java +++ b/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/CheckingAccountTest.java @@ -48,7 +48,8 @@ void deposit_withNegativeAmount() { } @Test - void withdraw() { + void withdraw() { // Assuming Check has a constructor with id and amount + // Assuming Check has a constructor with id and amount classUnderTest.withdraw(50.0); assertEquals(50.0, classUnderTest.getBalance()); } diff --git a/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/SavingsAccountTest.java b/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/SavingsAccountTest.java new file mode 100644 index 00000000..b1eb801c --- /dev/null +++ b/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/SavingsAccountTest.java @@ -0,0 +1,52 @@ +package com.codedifferently.lesson17.bank; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.HashSet; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class SavingsAccountTest { + + private SavingsAccount classUnderTest; + private HashSet owners; + + @BeforeEach + void setUp() { + owners = new HashSet<>(); + owners.add(new Customer(UUID.randomUUID(), "John Doe")); + owners.add(new Customer(UUID.randomUUID(), "Jane Smith")); + classUnderTest = new SavingsAccount("123456789", owners, 100.0); + } + + @Test + void testHashCode() { + int expected = classUnderTest.getAccountNumber().hashCode(); + int actual = classUnderTest.hashCode(); + assertEquals(expected, actual); + } + + @Test + void testEquals() { + SavingsAccount other = new SavingsAccount("123456789", owners, 100.0); + boolean expected = true; + boolean actual = classUnderTest.equals(other); + assertEquals(expected, actual); + } + + @Test + void testCheckWithdrawl() { + Check check = new Check("123456789", 1000.0, classUnderTest); + + IllegalStateException exception = + assertThrows( + IllegalStateException.class, + () -> { + classUnderTest.withdraw(1000, check); + }); + + assertEquals("Cannot withdraw from a savings account using a check", exception.getMessage()); + } +}