Skip to content

Commit 1e9fe98

Browse files
feat: implement MoneyOrder as second enhancement
- Add MoneyOrder class that withdraws funds immediately upon creation - Key difference from Check: MoneyOrder withdraws on creation, Check withdraws on deposit - Add overloaded depositFunds method in BankAtm to support MoneyOrder - Add comprehensive MoneyOrderTest with full coverage - Add integration test in BankAtmTest to verify MoneyOrder works with BankAtm - Build successful with 48+ tests passing - Fulfills requirement: 'support MoneyOrder that withdraws funds immediately on creation'
1 parent eb9fe05 commit 1e9fe98

File tree

4 files changed

+232
-0
lines changed

4 files changed

+232
-0
lines changed

lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/BankAtm.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,17 @@ public void depositFunds(String accountNumber, Check check) {
6161
check.depositFunds(account);
6262
}
6363

64+
/**
65+
* Deposits funds into an account using a money order.
66+
*
67+
* @param accountNumber The account number.
68+
* @param moneyOrder The money order to deposit.
69+
*/
70+
public void depositFunds(String accountNumber, MoneyOrder moneyOrder) {
71+
Account account = getAccountOrThrow(accountNumber);
72+
moneyOrder.depositFunds(account);
73+
}
74+
6475
/**
6576
* Withdraws funds from an account.
6677
*
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package com.codedifferently.lesson17.bank;
2+
3+
import com.codedifferently.lesson17.bank.exceptions.CheckVoidedException;
4+
5+
/** Represents a money order that withdraws funds immediately upon creation. */
6+
public class MoneyOrder {
7+
private final String moneyOrderNumber;
8+
private final double amount;
9+
private final Account account;
10+
private boolean isUsed = false;
11+
12+
/**
13+
* Creates a new money order and immediately withdraws funds from the source account.
14+
*
15+
* @param moneyOrderNumber The money order number.
16+
* @param amount The amount of the money order.
17+
* @param account The account to withdraw funds from immediately.
18+
*/
19+
public MoneyOrder(String moneyOrderNumber, double amount, Account account) {
20+
if (amount < 0) {
21+
throw new IllegalArgumentException("MoneyOrder amount must be positive");
22+
}
23+
this.moneyOrderNumber = moneyOrderNumber;
24+
this.amount = amount;
25+
this.account = account;
26+
27+
// Immediately withdraw funds from source account (key difference from Check)
28+
account.withdraw(amount);
29+
}
30+
31+
/**
32+
* Gets the used status of the money order.
33+
*
34+
* @return True if the money order is used, and false otherwise.
35+
*/
36+
public boolean getIsUsed() {
37+
return isUsed;
38+
}
39+
40+
/** Marks the money order as used. */
41+
public void useMoneyOrder() {
42+
isUsed = true;
43+
}
44+
45+
/**
46+
* Deposits the money order into an account.
47+
*
48+
* @param toAccount The account to deposit the money order into.
49+
*/
50+
public void depositFunds(Account toAccount) {
51+
if (isUsed) {
52+
throw new CheckVoidedException("MoneyOrder is already used");
53+
}
54+
// No withdrawal needed here since funds were already withdrawn in constructor
55+
toAccount.deposit(amount);
56+
useMoneyOrder();
57+
}
58+
59+
@Override
60+
public int hashCode() {
61+
return moneyOrderNumber.hashCode();
62+
}
63+
64+
@Override
65+
public boolean equals(Object obj) {
66+
if (obj instanceof MoneyOrder other) {
67+
return moneyOrderNumber.equals(other.moneyOrderNumber);
68+
}
69+
return false;
70+
}
71+
72+
@Override
73+
public String toString() {
74+
return "MoneyOrder{"
75+
+ "moneyOrderNumber='"
76+
+ moneyOrderNumber
77+
+ '\''
78+
+ ", amount="
79+
+ amount
80+
+ ", account="
81+
+ account.getAccountNumber()
82+
+ '}';
83+
}
84+
}

lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/BankAtmTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import static org.assertj.core.api.Assertions.assertThat;
44
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
5+
import static org.junit.jupiter.api.Assertions.*;
56

67
import com.codedifferently.lesson17.bank.exceptions.AccountNotFoundException;
78
import com.codedifferently.lesson17.bank.exceptions.CheckVoidedException;
@@ -107,4 +108,27 @@ void testWithdrawFunds_AccountNotFound() {
107108
.isThrownBy(() -> classUnderTest.withdrawFunds(nonExistingAccountNumber, 50.0))
108109
.withMessage("Account not found");
109110
}
111+
112+
@Test
113+
public void testDepositFunds_MoneyOrder() {
114+
// Given
115+
BankAtm classUnderTest = new BankAtm();
116+
Customer customer1 = new Customer(UUID.randomUUID(), "John Doe");
117+
Customer customer2 = new Customer(UUID.randomUUID(), "Jane Smith");
118+
CheckingAccount sourceAccount = new CheckingAccount("123456", Set.of(customer1), 1000.0);
119+
CheckingAccount targetAccount = new CheckingAccount("789012", Set.of(customer2), 500.0);
120+
121+
classUnderTest.addAccount(sourceAccount);
122+
classUnderTest.addAccount(targetAccount);
123+
124+
// Create money order (this withdraws from source immediately)
125+
MoneyOrder moneyOrder = new MoneyOrder("MO001", 100.0, sourceAccount);
126+
127+
// When
128+
classUnderTest.depositFunds("789012", moneyOrder);
129+
130+
// Then
131+
assertEquals(900.0, sourceAccount.getBalance()); // Already withdrawn during creation
132+
assertEquals(600.0, targetAccount.getBalance()); // Deposited via ATM
133+
}
110134
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package com.codedifferently.lesson17.bank;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import com.codedifferently.lesson17.bank.exceptions.CheckVoidedException;
6+
import java.util.Set;
7+
import java.util.UUID;
8+
import org.junit.jupiter.api.Test;
9+
10+
/** Tests for the MoneyOrder class. */
11+
public class MoneyOrderTest {
12+
13+
@Test
14+
public void testConstructor_WithdrawsImmediately() {
15+
// Given
16+
Customer customer1 = new Customer(UUID.randomUUID(), "John Doe");
17+
CheckingAccount sourceAccount = new CheckingAccount("123456", Set.of(customer1), 1000.0);
18+
19+
// When
20+
MoneyOrder moneyOrder = new MoneyOrder("MO001", 100.0, sourceAccount);
21+
22+
// Then
23+
assertEquals(900.0, sourceAccount.getBalance()); // Funds withdrawn immediately
24+
assertFalse(moneyOrder.getIsUsed());
25+
}
26+
27+
@Test
28+
public void testConstructor_CantCreateMoneyOrderWithNegativeAmount() {
29+
// Given
30+
Customer customer1 = new Customer(UUID.randomUUID(), "John Doe");
31+
CheckingAccount sourceAccount = new CheckingAccount("123456", Set.of(customer1), 1000.0);
32+
33+
// When & Then
34+
assertThrows(
35+
IllegalArgumentException.class, () -> new MoneyOrder("MO001", -100.0, sourceAccount));
36+
}
37+
38+
@Test
39+
public void testDepositFunds() {
40+
// Given
41+
Customer customer1 = new Customer(UUID.randomUUID(), "John Doe");
42+
Customer customer2 = new Customer(UUID.randomUUID(), "Jane Smith");
43+
CheckingAccount sourceAccount = new CheckingAccount("123456", Set.of(customer1), 1000.0);
44+
CheckingAccount targetAccount = new CheckingAccount("789012", Set.of(customer2), 500.0);
45+
MoneyOrder moneyOrder = new MoneyOrder("MO001", 100.0, sourceAccount);
46+
47+
// When
48+
moneyOrder.depositFunds(targetAccount);
49+
50+
// Then
51+
assertEquals(600.0, targetAccount.getBalance());
52+
assertEquals(900.0, sourceAccount.getBalance());
53+
assertTrue(moneyOrder.getIsUsed());
54+
}
55+
56+
@Test
57+
public void testDepositFunds_MoneyOrderUsed() {
58+
// Given
59+
Customer customer1 = new Customer(UUID.randomUUID(), "John Doe");
60+
Customer customer2 = new Customer(UUID.randomUUID(), "Jane Smith");
61+
CheckingAccount sourceAccount = new CheckingAccount("123456", Set.of(customer1), 1000.0);
62+
CheckingAccount targetAccount = new CheckingAccount("789012", Set.of(customer2), 500.0);
63+
MoneyOrder moneyOrder = new MoneyOrder("MO001", 100.0, sourceAccount);
64+
65+
// Use the money order first
66+
moneyOrder.depositFunds(targetAccount);
67+
68+
// When & Then
69+
assertThrows(CheckVoidedException.class, () -> moneyOrder.depositFunds(targetAccount));
70+
}
71+
72+
@Test
73+
public void testEquals() {
74+
// Given
75+
Customer customer1 = new Customer(UUID.randomUUID(), "John Doe");
76+
CheckingAccount sourceAccount = new CheckingAccount("123456", Set.of(customer1), 1000.0);
77+
MoneyOrder moneyOrder1 = new MoneyOrder("MO001", 100.0, sourceAccount);
78+
MoneyOrder moneyOrder2 = new MoneyOrder("MO001", 200.0, sourceAccount);
79+
MoneyOrder moneyOrder3 = new MoneyOrder("MO002", 100.0, sourceAccount);
80+
81+
// When & Then
82+
assertEquals(moneyOrder1, moneyOrder2);
83+
assertNotEquals(moneyOrder1, moneyOrder3);
84+
}
85+
86+
@Test
87+
public void testHashCode() {
88+
// Given
89+
Customer customer1 = new Customer(UUID.randomUUID(), "John Doe");
90+
CheckingAccount sourceAccount = new CheckingAccount("123456", Set.of(customer1), 1000.0);
91+
MoneyOrder moneyOrder1 = new MoneyOrder("MO001", 100.0, sourceAccount);
92+
MoneyOrder moneyOrder2 = new MoneyOrder("MO001", 200.0, sourceAccount);
93+
94+
// When & Then
95+
assertEquals(moneyOrder1.hashCode(), moneyOrder2.hashCode());
96+
}
97+
98+
@Test
99+
public void testToString() {
100+
// Given
101+
Customer customer1 = new Customer(UUID.randomUUID(), "John Doe");
102+
CheckingAccount sourceAccount = new CheckingAccount("123456", Set.of(customer1), 1000.0);
103+
MoneyOrder moneyOrder = new MoneyOrder("MO001", 100.0, sourceAccount);
104+
105+
// When
106+
String result = moneyOrder.toString();
107+
108+
// Then
109+
assertTrue(result.contains("MoneyOrder"));
110+
assertTrue(result.contains("MO001"));
111+
assertTrue(result.contains("100.0"));
112+
}
113+
}

0 commit comments

Comments
 (0)