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 000000000..4cfaf5aa8 --- /dev/null +++ b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/AuditLog.java @@ -0,0 +1,47 @@ +package com.codedifferently.lesson17.bank; + +import java.util.ArrayList; +import java.util.List; + +/** + * Records audit logs for account-related actions. This class maintains a list of log entries for + * tracking account additions. + */ +public class AuditLog { + + // List to hold log entries + private final List logEntries = new ArrayList<>(); + + /** + * Logs the addition of a new account by recording its account number. + * + * @param accountNumber The account number of the newly added account. + */ + public void logAccountAddition(String accountNumber) { + logEntries.add("Account added: " + accountNumber); + } + + /** + * Retrieves all log entries recorded in the audit log. + * + * @return A new list containing all log entries. + */ + public List getLogEntries() { + // Return a copy of the log entries to prevent external modification + return new ArrayList<>(logEntries); + } + + /** + * Retrieves the last log entry recorded in the audit log. + * + * @return The most recent log entry as a String, or null if there are no entries. + */ + public String getLastEntry() { + // Check if the log is empty and return null if so + if (logEntries.isEmpty()) { + return null; // Or throw an exception if preferred + } + // Return the last log entry + return logEntries.get(logEntries.size() - 1); + } +} 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 8cbcd3cc0..40227dd68 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 @@ -1,88 +1,158 @@ package com.codedifferently.lesson17.bank; import com.codedifferently.lesson17.bank.exceptions.AccountNotFoundException; +import java.util.Currency; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; -/** Represents a bank ATM. */ +/** + * Represents a bank ATM that manages customer accounts and transactions. This class handles account + * management, deposits, withdrawals, and logging actions. + */ public class BankAtm { + // Map to store customers by their unique ID private final Map customerById = new HashMap<>(); + + // Map to store checking accounts by their account number private final Map accountByNumber = new HashMap<>(); + // Audit log to record account-related actions + private final AuditLog auditLog = new AuditLog(); + /** - * Adds a checking account to the bank. + * Adds a checking account to the bank's records. * - * @param account The account to add. + * @param account The CheckingAccount to add. */ public void addAccount(CheckingAccount account) { + // Add the account to the account map using its account number accountByNumber.put(account.getAccountNumber(), account); + + // Register each owner of the account in the customer map account .getOwners() .forEach( owner -> { customerById.put(owner.getId(), owner); }); + + // Log the account addition in the audit log + auditLog.logAccountAddition(account.getAccountNumber()); } /** - * Finds all accounts owned by a customer. + * Finds all accounts owned by a specific customer. * - * @param customerId The ID of the customer. - * @return The unique set of accounts owned by the customer. + * @param customerId The ID of the customer whose accounts are to be found. + * @return A unique set of CheckingAccounts owned by the customer, or an empty set if none are + * found. */ public Set findAccountsByCustomerId(UUID customerId) { return customerById.containsKey(customerId) ? customerById.get(customerId).getAccounts() - : Set.of(); + : Set.of(); // Return an empty set if the customer does not exist } /** - * Deposits funds into an account. + * Deposits a specified amount of funds into an account. * - * @param accountNumber The account number. - * @param amount The amount to deposit. + * @param accountNumber The account number to deposit into. + * @param amount The amount of money to deposit. */ public void depositFunds(String accountNumber, double amount) { + // Retrieve the account or throw an exception if not found CheckingAccount account = getAccountOrThrow(accountNumber); - account.deposit(amount); + account.deposit(amount); // Deposit the amount into the account } /** * Deposits funds into an account using a check. * - * @param accountNumber The account number. - * @param check The check to deposit. + * @param accountNumber The account number to deposit into. + * @param check The Check object representing the check to be deposited. */ public void depositFunds(String accountNumber, Check check) { + // Retrieve the account or throw an exception if not found CheckingAccount account = getAccountOrThrow(accountNumber); - check.depositFunds(account); + check.depositFunds(account); // Process the check and deposit funds into the account + } + + /** + * Deposits funds into an account while converting from a specified currency. + * + * @param accountNumber The account number to deposit into. + * @param amount The amount to deposit. + * @param currency The currency of the deposit amount. + */ + public void depositFunds(String accountNumber, double amount, Currency currency) { + // Retrieve the account or throw an exception if not found + CheckingAccount account = getAccountOrThrow(accountNumber); + + // Validate the provided currency + if (currency == null || !isValidCurrency(currency)) { + throw new IllegalArgumentException("Invalid currency provided"); + } + + // Convert the amount to the account's currency (assuming USD for this example) + Currency accountCurrency = + Currency.getInstance("USD"); // Replace with actual account currency if necessary + double convertedAmount = CurrencyConverter.convert(amount, currency, accountCurrency); + + account.deposit(convertedAmount); // Deposit the converted amount into the account + } + + /** + * Checks if the provided currency is valid. + * + * @param currency The currency to validate. + * @return true if the currency is valid; false otherwise. + */ + private boolean isValidCurrency(Currency currency) { + // Define valid currencies + return currency.getCurrencyCode().equals("USD") + || currency.getCurrencyCode().equals("GBP") + || currency.getCurrencyCode().equals("CAD") + || currency.getCurrencyCode().equals("EUR"); + } + + /** + * Retrieves all entries from the audit log. + * + * @return A list of log entries recorded in the audit log. + */ + public List getAuditLogEntries() { + return auditLog.getLogEntries(); // Return the audit log entries } /** - * Withdraws funds from an account. + * Withdraws a specified amount of funds from an account. * - * @param accountNumber - * @param amount + * @param accountNumber The account number to withdraw from. + * @param amount The amount of money to withdraw. */ public void withdrawFunds(String accountNumber, double amount) { + // Retrieve the account or throw an exception if not found CheckingAccount account = getAccountOrThrow(accountNumber); - account.withdraw(amount); + account.withdraw(amount); // Withdraw the specified amount from the account } /** - * Gets an account by its number or throws an exception if not found. + * Retrieves an account by its account number, throwing an exception if not found. * - * @param accountNumber The account number. - * @return The account. + * @param accountNumber The account number to search for. + * @return The CheckingAccount associated with the given account number. + * @throws AccountNotFoundException if the account is not found or is closed. */ private CheckingAccount getAccountOrThrow(String accountNumber) { CheckingAccount account = accountByNumber.get(accountNumber); + // Check if the account is found and is not closed if (account == null || account.isClosed()) { throw new AccountNotFoundException("Account not found"); } - return account; + return account; // Return the found account } } diff --git a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/BusinessCheckingAccount.java b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/BusinessCheckingAccount.java new file mode 100644 index 000000000..be23916af --- /dev/null +++ b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/BusinessCheckingAccount.java @@ -0,0 +1,32 @@ +package com.codedifferently.lesson17.bank; + +import java.util.Set; + +/** + * Represents a business checking account, which is a specialized type of checking account. This + * account must have at least one owner designated as a business. + */ +public class BusinessCheckingAccount extends CheckingAccount { + + /** + * Creates a new BusinessCheckingAccount with the specified account number, owners, and initial + * balance. + * + * @param accountNumber The account number for the business checking account. + * @param owners A set of Customer objects representing the owners of the account. + * @param initialBalance The initial balance to set for the account. + * @throws IllegalArgumentException if no owner is designated as a business. + */ + public BusinessCheckingAccount( + String accountNumber, Set owners, double initialBalance) { + super( + accountNumber, + owners, + initialBalance); // Call the superclass constructor to initialize common fields + + // Ensure that at least one owner is a business customer + if (!owners.stream().anyMatch(Customer::isBusiness)) { + throw new IllegalArgumentException("At least one owner must be a business"); + } + } +} diff --git a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/Check.java b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/Check.java index 061fa4a5c..1ca4b2fc8 100644 --- a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/Check.java +++ b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/Check.java @@ -2,67 +2,80 @@ import com.codedifferently.lesson17.bank.exceptions.CheckVoidedException; -/** Represents a check. */ +/** + * Represents a check that can be deposited into a checking account. A check has a number, an + * amount, and is associated with a checking account. + */ public class Check { + // The unique identifier for the check private final String checkNumber; + + // The amount specified on the check private final double amount; + + // The checking account that the check is drawn from private final CheckingAccount account; + + // Indicates whether the check has been voided private boolean isVoided = false; /** - * Creates a new check. + * Creates a new check with the specified check number, amount, and associated account. * * @param checkNumber The check number. - * @param amount The amount of the check. - * @param account The account the check is drawn on. + * @param amount The amount of the check (must be positive). + * @param account The checking account the check is drawn on. + * @throws IllegalArgumentException if the amount is negative. */ public Check(String checkNumber, double amount, CheckingAccount account) { if (amount < 0) { - throw new IllegalArgumentException("Check amount must be positive"); + throw new IllegalArgumentException( + "Check amount must be positive"); // Validate the check amount } this.checkNumber = checkNumber; this.amount = amount; - this.account = account; + this.account = account; // Assign the associated checking account } /** * Gets the voided status of the check. * - * @return True if the check is voided, and false otherwise. + * @return True if the check is voided; false otherwise. */ public boolean getIsVoided() { - return isVoided; + return isVoided; // Return the voided status } - /** Voids the check. */ + /** Voids the check, preventing it from being deposited. */ public void voidCheck() { - isVoided = true; + isVoided = true; // Set the voided status to true } /** - * Deposits the check into an account. + * Deposits the check into the specified account. * * @param toAccount The account to deposit the check into. + * @throws CheckVoidedException if the check is voided. */ public void depositFunds(CheckingAccount toAccount) { if (isVoided) { - throw new CheckVoidedException("Check is voided"); + throw new CheckVoidedException("Check is voided"); // Check if the check is voided } - account.withdraw(amount); - toAccount.deposit(amount); - voidCheck(); + account.withdraw(amount); // Withdraw the amount from the original account + toAccount.deposit(amount); // Deposit the amount into the specified account + voidCheck(); // Void the check after successful deposit } @Override public int hashCode() { - return checkNumber.hashCode(); + return checkNumber.hashCode(); // Generate hash code based on the check number } @Override public boolean equals(Object obj) { if (obj instanceof Check other) { - return checkNumber.equals(other.checkNumber); + return checkNumber.equals(other.checkNumber); // Check equality based on the check number } return false; } @@ -77,6 +90,6 @@ public String toString() { + amount + ", account=" + account.getAccountNumber() - + '}'; + + '}'; // Return a string representation of the check } } diff --git a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/CurrencyConverter.java b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/CurrencyConverter.java new file mode 100644 index 000000000..9553d4509 --- /dev/null +++ b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/CurrencyConverter.java @@ -0,0 +1,57 @@ +package com.codedifferently.lesson17.bank; + +import java.util.Currency; + +public class CurrencyConverter { + public static double convert(double amount, Currency fromCurrency, Currency toCurrency) { + if (fromCurrency.equals(toCurrency)) { + return amount; // No conversion needed + } + + double exchangeRate = getExchangeRate(fromCurrency, toCurrency); + return amount * exchangeRate; + } + + private static double getExchangeRate(Currency fromCurrency, Currency toCurrency) { + // Example exchange rates + if (fromCurrency.getCurrencyCode().equals("USD") + && toCurrency.getCurrencyCode().equals("EUR")) { + return 0.85; // USD to EUR + } else if (fromCurrency.getCurrencyCode().equals("EUR") + && toCurrency.getCurrencyCode().equals("USD")) { + return 1.18; // EUR to USD + } else if (fromCurrency.getCurrencyCode().equals("USD") + && toCurrency.getCurrencyCode().equals("GBP")) { + return 0.75; // USD to GBP + } else if (fromCurrency.getCurrencyCode().equals("GBP") + && toCurrency.getCurrencyCode().equals("USD")) { + return 1.33; // GBP to USD + } else if (fromCurrency.getCurrencyCode().equals("EUR") + && toCurrency.getCurrencyCode().equals("GBP")) { + return 0.88; // EUR to GBP + } else if (fromCurrency.getCurrencyCode().equals("GBP") + && toCurrency.getCurrencyCode().equals("EUR")) { + return 1.14; // GBP to EUR + } else if (fromCurrency.getCurrencyCode().equals("USD") + && toCurrency.getCurrencyCode().equals("CAD")) { + return 1.25; // USD to CAD + } else if (fromCurrency.getCurrencyCode().equals("CAD") + && toCurrency.getCurrencyCode().equals("USD")) { + return 0.80; // CAD to USD + } else if (fromCurrency.getCurrencyCode().equals("EUR") + && toCurrency.getCurrencyCode().equals("CAD")) { + return 1.50; // EUR to CAD + } else if (fromCurrency.getCurrencyCode().equals("CAD") + && toCurrency.getCurrencyCode().equals("EUR")) { + return 0.67; // CAD to EUR + } else if (fromCurrency.getCurrencyCode().equals("GBP") + && toCurrency.getCurrencyCode().equals("CAD")) { + return 1.75; // GBP to CAD + } else if (fromCurrency.getCurrencyCode().equals("CAD") + && toCurrency.getCurrencyCode().equals("GBP")) { + return 0.57; // CAD to GBP + } + // Add other currency pairs as needed + return 1.0; // Default to 1.0 if no conversion is defined + } +} diff --git a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/Customer.java b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/Customer.java index af0847134..7998274ce 100644 --- a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/Customer.java +++ b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/Customer.java @@ -10,16 +10,19 @@ public class Customer { private final UUID id; private final String name; private final Set accounts = new HashSet<>(); + private boolean isBusiness; /** * Creates a new customer. * * @param id The ID of the customer. * @param name The name of the customer. + * @param isBusiness Indicates whether the customer is a business (true) or an individual (false). */ - public Customer(UUID id, String name) { + public Customer(UUID id, String name, boolean isBusiness) { this.id = id; this.name = name; + this.isBusiness = isBusiness; } /** @@ -40,6 +43,15 @@ public String getName() { return name; } + /** + * Checks if the customer is a business. + * + * @return true if the customer is a business; false otherwise. + */ + public boolean isBusiness() { + return isBusiness; + } + /** * Adds a checking account to the customer. * @@ -60,11 +72,13 @@ public Set getAccounts() { @Override public int hashCode() { + // Generate a hash code based on the customer's ID for proper set operations return id.hashCode(); } @Override public boolean equals(Object obj) { + // Check if this customer is equal to another customer based on their ID if (obj instanceof Customer other) { return id.equals(other.id); } @@ -73,6 +87,7 @@ public boolean equals(Object obj) { @Override public String toString() { + // Provide a string representation of the customer including ID and name return "Customer{" + "id=" + id + ", name='" + name + '\'' + '}'; } } diff --git a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/MoneyOrder.java b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/MoneyOrder.java new file mode 100644 index 000000000..5e7ec67fa --- /dev/null +++ b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/MoneyOrder.java @@ -0,0 +1,43 @@ +package com.codedifferently.lesson17.bank; + +/** + * Represents a money order, which includes an amount and a source account number from which the + * funds are drawn. + */ +public class MoneyOrder { + + // The amount of money represented by the money order + private final double amount; + + // The account number from which the money order is issued + private final String sourceAccountNumber; + + /** + * Creates a new MoneyOrder with the specified amount and source account number. + * + * @param amount The amount of the money order. + * @param sourceAccountNumber The account number from which the funds are drawn. + */ + public MoneyOrder(double amount, String sourceAccountNumber) { + this.amount = amount; // Initialize the amount + this.sourceAccountNumber = sourceAccountNumber; // Initialize the source account number + } + + /** + * Gets the amount of the money order. + * + * @return The amount of the money order. + */ + public double getAmount() { + return amount; // Return the amount + } + + /** + * Gets the source account number for the money order. + * + * @return The source account number. + */ + public String getSourceAccountNumber() { + return sourceAccountNumber; // Return the source account number + } +} 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 000000000..93f794bf6 --- /dev/null +++ b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/SavingsAccount.java @@ -0,0 +1,35 @@ +package com.codedifferently.lesson17.bank; + +import java.util.Set; + +/** + * Represents a savings account, which is a specialized type of checking account. Inherits the + * behavior and properties of the CheckingAccount class. + */ +public class SavingsAccount extends CheckingAccount { + + /** + * Creates a new SavingsAccount with the specified account number, owners, and initial balance. + * + * @param accountNumber The account number for the savings account. + * @param owners A set of Customer objects representing the owners of the account. + * @param initialBalance The initial balance to set for the account. + */ + public SavingsAccount(String accountNumber, Set owners, double initialBalance) { + super( + accountNumber, + owners, + initialBalance); // Call the superclass constructor to initialize common fields + } + + /** + * Deposits funds into the savings account. This method overrides the deposit method in + * CheckingAccount, but currently it behaves the same way as the parent class. + * + * @param amount The amount to deposit into the savings account. + */ + @Override + public void deposit(double amount) { + super.deposit(amount); // Call the deposit method from CheckingAccount + } +} 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 fa4a913a2..b792d9d6e 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 @@ -5,6 +5,9 @@ import com.codedifferently.lesson17.bank.exceptions.AccountNotFoundException; import com.codedifferently.lesson17.bank.exceptions.CheckVoidedException; +import com.codedifferently.lesson17.bank.exceptions.InsufficientFundsException; +import java.util.Currency; +import java.util.List; import java.util.Set; import java.util.UUID; import org.junit.jupiter.api.BeforeEach; @@ -21,8 +24,8 @@ class BankAtmTest { @BeforeEach void setUp() { classUnderTest = new BankAtm(); - customer1 = new Customer(UUID.randomUUID(), "John Doe"); - customer2 = new Customer(UUID.randomUUID(), "Jane Smith"); + customer1 = new Customer(UUID.randomUUID(), "John Doe", false); + customer2 = new Customer(UUID.randomUUID(), "Jane Smith", false); account1 = new CheckingAccount("123456789", Set.of(customer1), 100.0); account2 = new CheckingAccount("987654321", Set.of(customer1, customer2), 200.0); customer1.addAccount(account1); @@ -35,7 +38,7 @@ void setUp() { @Test void testAddAccount() { // Arrange - Customer customer3 = new Customer(UUID.randomUUID(), "Alice Johnson"); + Customer customer3 = new Customer(UUID.randomUUID(), "Tech Labs", true); CheckingAccount account3 = new CheckingAccount("555555555", Set.of(customer3), 300.0); customer3.addAccount(account3); @@ -80,10 +83,13 @@ void testDepositFunds_Check() { @Test void testDepositFunds_DoesntDepositCheckTwice() { + // Arrange Check check = new Check("987654321", 100.0, account1); + // Act classUnderTest.depositFunds("987654321", check); + // Assert assertThatExceptionOfType(CheckVoidedException.class) .isThrownBy(() -> classUnderTest.depositFunds("987654321", check)) .withMessage("Check is voided"); @@ -100,6 +106,7 @@ void testWithdrawFunds() { @Test void testWithdrawFunds_AccountNotFound() { + // Arrange String nonExistingAccountNumber = "999999999"; // Act & Assert @@ -107,4 +114,78 @@ void testWithdrawFunds_AccountNotFound() { .isThrownBy(() -> classUnderTest.withdrawFunds(nonExistingAccountNumber, 50.0)) .withMessage("Account not found"); } + + @Test + void testDepositFunds_WithCurrency_GBP() { + // Arrange + Currency gbp = Currency.getInstance("GBP"); + account1.deposit(100.0); // Reset balance for testing + double initialBalance = account1.getBalance(); + + // Act + classUnderTest.depositFunds(account1.getAccountNumber(), 100.0, gbp); + + // Assert + double expectedBalance = + initialBalance + CurrencyConverter.convert(100.0, gbp, Currency.getInstance("USD")); + assertThat(account1.getBalance()).isEqualTo(expectedBalance); + } + + @Test + void testDepositFunds_WithCurrency_CAD() { + // Arrange + Currency cad = Currency.getInstance("CAD"); + account2.deposit(200.0); + double initialBalance = account2.getBalance(); + + // Act + classUnderTest.depositFunds(account2.getAccountNumber(), 100.0, cad); + + // Assert + double expectedBalance = + initialBalance + CurrencyConverter.convert(100.0, cad, Currency.getInstance("USD")); + assertThat(account2.getBalance()).isEqualTo(expectedBalance); + } + + @Test + void testDepositFunds_AccountNotFound() { + // Arrange + String nonExistingAccountNumber = "999999999"; + + // Act & Assert + assertThatExceptionOfType(AccountNotFoundException.class) + .isThrownBy( + () -> + classUnderTest.depositFunds( + nonExistingAccountNumber, 50.0, Currency.getInstance("USD"))) + .withMessage("Account not found"); + } + + @Test + void testWithdrawFunds_InsufficientFunds() { + // Arrange + double amountToWithdraw = 200.0; // More than the balance + + // Act & Assert + assertThatExceptionOfType(InsufficientFundsException.class) + .isThrownBy( + () -> classUnderTest.withdrawFunds(account1.getAccountNumber(), amountToWithdraw)) + .withMessage("Account does not have enough funds for withdrawal"); + } + + @Test + void testAddAccountLogsCorrectly() { + // Arrange + String accountNumber = "555555555"; + Customer customer = new Customer(UUID.randomUUID(), "New Customer", true); + CheckingAccount newAccount = new CheckingAccount(accountNumber, Set.of(customer), 300.0); + customer.addAccount(newAccount); + + // Act + classUnderTest.addAccount(newAccount); + + // Assert + List logEntries = classUnderTest.getAuditLogEntries(); + assertThat(logEntries).contains("Account added: " + accountNumber); + } } diff --git a/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/BusinessCheckingAccountTest.java b/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/BusinessCheckingAccountTest.java new file mode 100644 index 000000000..d112f3b3f --- /dev/null +++ b/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/BusinessCheckingAccountTest.java @@ -0,0 +1,37 @@ +package com.codedifferently.lesson17.bank; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Set; +import java.util.UUID; +import org.junit.jupiter.api.Test; + +class BusinessCheckingAccountTest { + + @Test + void testConstructor_ThrowsException_NoBusinessOwner() { + // Arrange + Customer individualOwner = new Customer(UUID.randomUUID(), "John Doe", false); + Set owners = Set.of(individualOwner); + + // Act & Assert + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> new BusinessCheckingAccount("123456789", owners, 100.0)) + .withMessage("At least one owner must be a business"); + } + + @Test + void testConstructor_Success_WithBusinessOwner() { + // Arrange + Customer businessOwner = new Customer(UUID.randomUUID(), "Tech Labs", true); + Set owners = Set.of(businessOwner); + + // Act + BusinessCheckingAccount account = new BusinessCheckingAccount("123456789", owners, 100.0); + + // Assert + assertThat(account).isNotNull(); + assertThat(account.getBalance()).isEqualTo(100.0); + } +} 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 f155d8e5b..78f0b37cf 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 @@ -12,95 +12,128 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +/** + * Unit tests for the CheckingAccount class. This class verifies the functionality and behavior of + * CheckingAccount methods. + */ class CheckingAccountTest { + // The CheckingAccount instance being tested private CheckingAccount classUnderTest; + + // Set of owners for the CheckingAccount private Set owners; + /** + * Set up method to initialize test environment before each test case. Creates a set of owners and + * initializes a CheckingAccount with a default balance. + */ @BeforeEach void setUp() { owners = new HashSet<>(); - owners.add(new Customer(UUID.randomUUID(), "John Doe")); - owners.add(new Customer(UUID.randomUUID(), "Jane Smith")); - classUnderTest = new CheckingAccount("123456789", owners, 100.0); + owners.add(new Customer(UUID.randomUUID(), "John Doe", false)); // Individual owner + owners.add(new Customer(UUID.randomUUID(), "Tech Corp", true)); // Business owner + classUnderTest = new CheckingAccount("123456789", owners, 100.0); // Initialize account } + /** Tests the retrieval of the account number. */ @Test void getAccountNumber() { - assertEquals("123456789", classUnderTest.getAccountNumber()); + assertEquals("123456789", classUnderTest.getAccountNumber()); // Verify account number } + /** Tests the retrieval of the account owners. */ @Test void getOwners() { - assertEquals(owners, classUnderTest.getOwners()); + assertEquals(owners, classUnderTest.getOwners()); // Verify owners set } + /** Tests the deposit functionality by adding funds to the account. */ @Test void deposit() { - classUnderTest.deposit(50.0); - assertEquals(150.0, classUnderTest.getBalance()); + classUnderTest.deposit(50.0); // Deposit funds + assertEquals(150.0, classUnderTest.getBalance()); // Verify new balance } + /** Tests depositing a negative amount, expecting an IllegalArgumentException. */ @Test void deposit_withNegativeAmount() { assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> classUnderTest.deposit(-50.0)); + .isThrownBy(() -> classUnderTest.deposit(-50.0)); // Verify exception is thrown } + /** Tests the withdrawal functionality by taking funds from the account. */ @Test void withdraw() { - classUnderTest.withdraw(50.0); - assertEquals(50.0, classUnderTest.getBalance()); + classUnderTest.withdraw(50.0); // Withdraw funds + assertEquals(50.0, classUnderTest.getBalance()); // Verify new balance } + /** Tests withdrawing a negative amount, expecting an IllegalStateException. */ @Test void withdraw_withNegativeAmount() { assertThatExceptionOfType(IllegalStateException.class) .isThrownBy(() -> classUnderTest.withdraw(-50.0)) - .withMessage("Withdrawal amount must be positive"); + .withMessage("Withdrawal amount must be positive"); // Verify exception message } + /** + * Tests withdrawal when there are insufficient funds, expecting an InsufficientFundsException. + */ @Test void withdraw_withInsufficientBalance() { assertThatExceptionOfType(InsufficientFundsException.class) .isThrownBy(() -> classUnderTest.withdraw(150.0)) - .withMessage("Account does not have enough funds for withdrawal"); + .withMessage( + "Account does not have enough funds for withdrawal"); // Verify exception message } + /** Tests the retrieval of the current account balance. */ @Test void getBalance() { - assertEquals(100.0, classUnderTest.getBalance()); + assertEquals(100.0, classUnderTest.getBalance()); // Verify initial balance } + /** + * Tests closing an account that still has a positive balance, expecting an IllegalStateException. + */ @Test void closeAccount_withPositiveBalance() { assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> classUnderTest.closeAccount()); + .isThrownBy(() -> classUnderTest.closeAccount()); // Verify exception is thrown } + /** Tests whether the account is closed. */ @Test void isClosed() { - assertFalse(classUnderTest.isClosed()); - classUnderTest.withdraw(100); - classUnderTest.closeAccount(); - assertTrue(classUnderTest.isClosed()); + assertFalse(classUnderTest.isClosed()); // Verify account is initially open + classUnderTest.withdraw(100); // Withdraw all funds + classUnderTest.closeAccount(); // Close the account + assertTrue(classUnderTest.isClosed()); // Verify account is closed } + /** Tests the equality of two CheckingAccount instances. */ @Test void equals() { CheckingAccount otherAccount = new CheckingAccount("123456789", owners, 200.0); - assertEquals(classUnderTest, otherAccount); + assertEquals( + classUnderTest, + otherAccount); // Verify accounts are equal based on account number and owners } + /** Tests the hash code of a CheckingAccount instance. */ @Test void hashCodeTest() { CheckingAccount otherAccount = new CheckingAccount("123456789", owners, 200.0); - assertEquals(classUnderTest.hashCode(), otherAccount.hashCode()); + assertEquals(classUnderTest.hashCode(), otherAccount.hashCode()); // Verify hash codes are equal } + /** Tests the string representation of the CheckingAccount. */ @Test void toStringTest() { String expected = "CheckingAccount{accountNumber='123456789', balance=100.0, isActive=true}"; - assertEquals(expected, classUnderTest.toString()); + assertEquals( + expected, + classUnderTest.toString()); // Verify string representation matches expected format } } diff --git a/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/CurrencyConverterTest.java b/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/CurrencyConverterTest.java new file mode 100644 index 000000000..c5d999e2d --- /dev/null +++ b/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/CurrencyConverterTest.java @@ -0,0 +1,39 @@ +package com.codedifferently.lesson17.bank; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Currency; +import org.junit.jupiter.api.Test; + +class CurrencyConverterTest { + + @Test + void testConvert_EuroToCAD() { + // Arrange + double amountInEUR = 100.0; + Currency euro = Currency.getInstance("EUR"); + Currency cad = Currency.getInstance("CAD"); + + // Act + double convertedAmount = CurrencyConverter.convert(amountInEUR, euro, cad); + + // Assert + double expectedAmount = amountInEUR * 1.50; + assertThat(convertedAmount).isEqualTo(expectedAmount); + } + + @Test + void testConvert_GBPToCAD() { + // Arrange + double amountInGBP = 100.0; + Currency gbp = Currency.getInstance("GBP"); + Currency cad = Currency.getInstance("CAD"); + + // Act + double convertedAmount = CurrencyConverter.convert(amountInGBP, gbp, cad); + + // Assert + double expectedAmount = amountInGBP * 1.75; // GBP to CAD exchange rate + assertThat(convertedAmount).isEqualTo(expectedAmount); + } +} diff --git a/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/MoneyOrderTest.java b/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/MoneyOrderTest.java new file mode 100644 index 000000000..78192e3f6 --- /dev/null +++ b/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/MoneyOrderTest.java @@ -0,0 +1,21 @@ +package com.codedifferently.lesson17.bank; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +public class MoneyOrderTest { + @Test + void testMoneyOrderInitialization() { + // Arrange + double amount = 50.0; + String sourceAccountNumber = "123456789"; + + // Act + MoneyOrder moneyOrder = new MoneyOrder(amount, sourceAccountNumber); + + // Assert + assertThat(moneyOrder.getAmount()).isEqualTo(amount); + assertThat(moneyOrder.getSourceAccountNumber()).isEqualTo(sourceAccountNumber); + } +} 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 000000000..172f854bc --- /dev/null +++ b/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/SavingsAccountTest.java @@ -0,0 +1,17 @@ +package com.codedifferently.lesson17.bank; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Set; +import org.junit.jupiter.api.Test; + +class SavingsAccountTest { + + @Test + void testSavingsAccountDeposit() { + SavingsAccount savingsAccount = new SavingsAccount("987654321", Set.of(), 200.0); + double amountToDeposit = 100.0; + savingsAccount.deposit(amountToDeposit); + assertThat(savingsAccount.getBalance()).isEqualTo(300.0); + } +}