diff --git a/src/main/java/com/example/bankapp/controller/BankController.java b/src/main/java/com/example/bankapp/controller/BankController.java index 19fcded7..62724436 100644 --- a/src/main/java/com/example/bankapp/controller/BankController.java +++ b/src/main/java/com/example/bankapp/controller/BankController.java @@ -48,10 +48,18 @@ public String login() { } @PostMapping("/deposit") - public String deposit(@RequestParam BigDecimal amount) { + public String deposit(@RequestParam BigDecimal amount, Model model) { String username = SecurityContextHolder.getContext().getAuthentication().getName(); Account account = accountService.findAccountByUsername(username); - accountService.deposit(account, amount); + + try { + accountService.deposit(account, amount); + } catch (RuntimeException e) { + model.addAttribute("error", e.getMessage()); + model.addAttribute("account", account); + return "dashboard"; + } + return "redirect:/dashboard"; } diff --git a/src/main/java/com/example/bankapp/service/AccountService.java b/src/main/java/com/example/bankapp/service/AccountService.java index 5d7d90ec..806a7fb4 100644 --- a/src/main/java/com/example/bankapp/service/AccountService.java +++ b/src/main/java/com/example/bankapp/service/AccountService.java @@ -49,6 +49,7 @@ public Account registerAccount(String username, String password) { public void deposit(Account account, BigDecimal amount) { + validateAmount(amount); account.setBalance(account.getBalance().add(amount)); accountRepository.save(account); @@ -62,6 +63,7 @@ public void deposit(Account account, BigDecimal amount) { } public void withdraw(Account account, BigDecimal amount) { + validateAmount(amount); if (account.getBalance().compareTo(amount) < 0) { throw new RuntimeException("Insufficient funds"); } @@ -101,6 +103,7 @@ public Collection authorities() { } public void transferAmount(Account fromAccount, String toUsername, BigDecimal amount) { + validateAmount(amount); if (fromAccount.getBalance().compareTo(amount) < 0) { throw new RuntimeException("Insufficient funds"); } @@ -134,4 +137,10 @@ public void transferAmount(Account fromAccount, String toUsername, BigDecimal am transactionRepository.save(creditTransaction); } + private static void validateAmount(BigDecimal amount) { + if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) { + throw new RuntimeException("Amount must be greater than zero"); + } + } + } diff --git a/src/test/java/com/example/bankapp/service/AccountServiceTest.java b/src/test/java/com/example/bankapp/service/AccountServiceTest.java new file mode 100644 index 00000000..3429fb7a --- /dev/null +++ b/src/test/java/com/example/bankapp/service/AccountServiceTest.java @@ -0,0 +1,117 @@ +package com.example.bankapp.service; + +import com.example.bankapp.model.Account; +import com.example.bankapp.repository.AccountRepository; +import com.example.bankapp.repository.TransactionRepository; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.crypto.password.PasswordEncoder; + +import java.math.BigDecimal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + +@ExtendWith(MockitoExtension.class) +class AccountServiceTest { + + @Mock + AccountRepository accountRepository; + + @Mock + TransactionRepository transactionRepository; + + @Mock + PasswordEncoder passwordEncoder; + + @InjectMocks + AccountService accountService; + + @Test + void withdrawRejectsNegativeAmountWithoutChangingBalance() { + Account account = accountWithBalance("100"); + + assertThrows(RuntimeException.class, + () -> accountService.withdraw(account, new BigDecimal("-1000000"))); + + assertEquals(new BigDecimal("100"), account.getBalance()); + verifyNoInteractions(accountRepository, transactionRepository); + } + + @Test + void withdrawRejectsZeroAmount() { + Account account = accountWithBalance("100"); + + assertThrows(RuntimeException.class, + () -> accountService.withdraw(account, BigDecimal.ZERO)); + + assertEquals(new BigDecimal("100"), account.getBalance()); + verifyNoInteractions(accountRepository, transactionRepository); + } + + @Test + void withdrawRejectsNullAmount() { + Account account = accountWithBalance("100"); + + assertThrows(RuntimeException.class, + () -> accountService.withdraw(account, null)); + + assertEquals(new BigDecimal("100"), account.getBalance()); + verifyNoInteractions(accountRepository, transactionRepository); + } + + @Test + void withdrawUpdatesBalanceAndSavesTransaction() { + Account account = accountWithBalance("100"); + + accountService.withdraw(account, new BigDecimal("40")); + + assertEquals(new BigDecimal("60"), account.getBalance()); + verify(accountRepository).save(account); + verify(transactionRepository).save(org.mockito.ArgumentMatchers.any()); + } + + @Test + void depositRejectsNegativeAmountWithoutChangingBalance() { + Account account = accountWithBalance("100"); + + assertThrows(RuntimeException.class, + () -> accountService.deposit(account, new BigDecimal("-40"))); + + assertEquals(new BigDecimal("100"), account.getBalance()); + verifyNoInteractions(accountRepository, transactionRepository); + } + + @Test + void depositUpdatesBalanceAndSavesTransaction() { + Account account = accountWithBalance("100"); + + accountService.deposit(account, new BigDecimal("40")); + + assertEquals(new BigDecimal("140"), account.getBalance()); + verify(accountRepository).save(account); + verify(transactionRepository).save(org.mockito.ArgumentMatchers.any()); + } + + @Test + void transferRejectsNegativeAmountBeforeLookingUpRecipient() { + Account account = accountWithBalance("100"); + + assertThrows(RuntimeException.class, + () -> accountService.transferAmount(account, "recipient", new BigDecimal("-40"))); + + verifyNoInteractions(accountRepository, transactionRepository); + } + + private Account accountWithBalance(String balance) { + Account account = new Account(); + account.setUsername("account"); + account.setBalance(new BigDecimal(balance)); + return account; + } +}