package com.atm.controller; import com.atm.model.Account; import com.atm.model.Customer; import com.atm.service.AccountService; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import java.math.BigDecimal; import java.util.Arrays; import java.util.List; import java.util.Optional; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @WebMvcTest(AccountController.class) public class AccountControllerTest { @Autowired private MockMvc mockMvc; @MockBean private AccountService accountService; @Autowired private ObjectMapper objectMapper; private Customer testCustomer; private Account testAccount; private Account testAccount2; @BeforeEach public void setUp() { testCustomer = new Customer(); testCustomer.setCid(12345L); testCustomer.setCname("张三"); testCustomer.setCpin("123456"); testCustomer.setCbalance(new BigDecimal("1000.00")); testCustomer.setCstatus("active"); testCustomer.setCtype("regular"); testAccount = new Account(); testAccount.setAid(1L); testAccount.setCustomer(testCustomer); testAccount.setAtype("savings"); testAccount.setAbalance(new BigDecimal("5000.00")); testAccount.setAstatus("active"); testAccount2 = new Account(); testAccount2.setAid(2L); testAccount.setCustomer(testCustomer); testAccount2.setAtype("checking"); testAccount2.setAbalance(new BigDecimal("2000.00")); testAccount2.setAstatus("active"); } @Test public void whenGetAccountById_thenReturnAccount() throws Exception { // given when(accountService.findByAid(1L)).thenReturn(Optional.of(testAccount)); // when & then mockMvc.perform(get("/api/accounts/1")) .andExpect(status().isOk()) .andExpect(jsonPath("$.aid").value(1)) .andExpect(jsonPath("$.atype").value("savings")) .andExpect(jsonPath("$.abalance").value(5000.00)); } @Test public void whenGetNonExistingAccountById_thenReturnNotFound() throws Exception { // given when(accountService.findByAid(999L)).thenReturn(Optional.empty()); // when & then mockMvc.perform(get("/api/accounts/999")) .andExpect(status().isNotFound()); } @Test public void whenGetAccountsByCustomerId_thenReturnAccounts() throws Exception { // given List accounts = Arrays.asList(testAccount, testAccount2); when(accountService.findByCid(12345L)).thenReturn(accounts); // when & then mockMvc.perform(get("/api/accounts/customer/12345")) .andExpect(status().isOk()) .andExpect(jsonPath("$").isArray()) .andExpect(jsonPath("$.length()").value(2)); } @Test public void whenCreateAccount_thenReturnCreatedAccount() throws Exception { // given Account newAccount = new Account(); newAccount.setCustomer(testCustomer); newAccount.setAtype("investment"); newAccount.setAbalance(new BigDecimal("10000.00")); newAccount.setAstatus("active"); when(accountService.createAccount(any(Account.class))).thenReturn(newAccount); // when & then mockMvc.perform(post("/api/accounts") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(newAccount))) .andExpect(status().isCreated()) .andExpect(jsonPath("$.atype").value("investment")) .andExpect(jsonPath("$.abalance").value(10000.00)); } @Test public void whenDeposit_thenReturnUpdatedAccount() throws Exception { // given BigDecimal depositAmount = new BigDecimal("1000.00"); Account updatedAccount = new Account(); updatedAccount.setAid(1L); updatedAccount.setCustomer(testCustomer); updatedAccount.setAtype("savings"); updatedAccount.setAbalance(new BigDecimal("6000.00")); updatedAccount.setAstatus("active"); when(accountService.deposit(1L, depositAmount)).thenReturn(Optional.of(updatedAccount)); // when & then mockMvc.perform(post("/api/accounts/1/deposit") .contentType(MediaType.APPLICATION_JSON) .content("{\"amount\":1000.00}")) .andExpect(status().isOk()) .andExpect(jsonPath("$.aid").value(1)) .andExpect(jsonPath("$.abalance").value(6000.00)); } @Test public void whenDepositToNonExistingAccount_thenReturnNotFound() throws Exception { // given BigDecimal depositAmount = new BigDecimal("1000.00"); when(accountService.deposit(999L, depositAmount)).thenReturn(Optional.empty()); // when & then mockMvc.perform(post("/api/accounts/999/deposit") .contentType(MediaType.APPLICATION_JSON) .content("{\"amount\":1000.00}")) .andExpect(status().isNotFound()); } @Test public void whenWithdraw_thenReturnUpdatedAccount() throws Exception { // given BigDecimal withdrawAmount = new BigDecimal("1000.00"); Account updatedAccount = new Account(); updatedAccount.setAid(1L); updatedAccount.setCustomer(testCustomer); updatedAccount.setAtype("savings"); updatedAccount.setAbalance(new BigDecimal("4000.00")); updatedAccount.setAstatus("active"); when(accountService.withdraw(1L, withdrawAmount)).thenReturn(Optional.of(updatedAccount)); // when & then mockMvc.perform(post("/api/accounts/1/withdraw") .contentType(MediaType.APPLICATION_JSON) .content("{\"amount\":1000.00}")) .andExpect(status().isOk()) .andExpect(jsonPath("$.aid").value(1)) .andExpect(jsonPath("$.abalance").value(4000.00)); } @Test public void whenWithdrawFromNonExistingAccount_thenReturnNotFound() throws Exception { // given BigDecimal withdrawAmount = new BigDecimal("1000.00"); when(accountService.withdraw(999L, withdrawAmount)).thenReturn(Optional.empty()); // when & then mockMvc.perform(post("/api/accounts/999/withdraw") .contentType(MediaType.APPLICATION_JSON) .content("{\"amount\":1000.00}")) .andExpect(status().isNotFound()); } @Test public void whenWithdrawWithInsufficientBalance_thenReturnBadRequest() throws Exception { // given BigDecimal withdrawAmount = new BigDecimal("10000.00"); when(accountService.withdraw(1L, withdrawAmount)).thenReturn(Optional.empty()); // when & then mockMvc.perform(post("/api/accounts/1/withdraw") .contentType(MediaType.APPLICATION_JSON) .content("{\"amount\":10000.00}")) .andExpect(status().isBadRequest()); } @Test public void whenTransfer_thenReturnSuccess() throws Exception { // given BigDecimal transferAmount = new BigDecimal("1000.00"); when(accountService.transfer(1L, 2L, transferAmount)).thenReturn(true); // when & then mockMvc.perform(post("/api/accounts/1/transfer") .contentType(MediaType.APPLICATION_JSON) .content("{\"toAccountId\":2,\"amount\":1000.00}")) .andExpect(status().isOk()) .andExpect(jsonPath("$.success").value(true)) .andExpect(jsonPath("$.message").value("转账成功")); } @Test public void whenTransferWithInsufficientBalance_thenReturnBadRequest() throws Exception { // given BigDecimal transferAmount = new BigDecimal("10000.00"); when(accountService.transfer(1L, 2L, transferAmount)).thenReturn(false); // when & then mockMvc.perform(post("/api/accounts/1/transfer") .contentType(MediaType.APPLICATION_JSON) .content("{\"toAccountId\":2,\"amount\":10000.00}")) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.success").value(false)) .andExpect(jsonPath("$.message").value("转账失败,余额不足")); } @Test public void whenTransferToNonExistingAccount_thenReturnBadRequest() throws Exception { // given BigDecimal transferAmount = new BigDecimal("1000.00"); when(accountService.transfer(1L, 999L, transferAmount)).thenReturn(false); // when & then mockMvc.perform(post("/api/accounts/1/transfer") .contentType(MediaType.APPLICATION_JSON) .content("{\"toAccountId\":999,\"amount\":1000.00}")) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.success").value(false)) .andExpect(jsonPath("$.message").value("转账失败,余额不足")); } @Test public void whenGetBalance_thenReturnBalance() throws Exception { // given when(accountService.findByAid(1L)).thenReturn(Optional.of(testAccount)); // when & then mockMvc.perform(get("/api/accounts/1/balance")) .andExpect(status().isOk()) .andExpect(jsonPath("$.aid").value(1)) .andExpect(jsonPath("$.balance").value(5000.00)); } @Test public void whenGetBalanceForNonExistingAccount_thenReturnNotFound() throws Exception { // given when(accountService.findByAid(999L)).thenReturn(Optional.empty()); // when & then mockMvc.perform(get("/api/accounts/999/balance")) .andExpect(status().isNotFound()); } @Test public void whenUpdateAccountStatus_thenReturnSuccess() throws Exception { // given when(accountService.updateAccountStatus(1L, "inactive")).thenReturn(true); // when & then mockMvc.perform(put("/api/accounts/1/status") .contentType(MediaType.APPLICATION_JSON) .content("{\"status\":\"inactive\"}")) .andExpect(status().isOk()) .andExpect(jsonPath("$.success").value(true)) .andExpect(jsonPath("$.message").value("账户状态更新成功")); } @Test public void whenUpdateStatusForNonExistingAccount_thenReturnNotFound() throws Exception { // given when(accountService.updateAccountStatus(999L, "inactive")).thenReturn(false); // when & then mockMvc.perform(put("/api/accounts/999/status") .contentType(MediaType.APPLICATION_JSON) .content("{\"status\":\"inactive\"}")) .andExpect(status().isNotFound()) .andExpect(jsonPath("$.success").value(false)) .andExpect(jsonPath("$.message").value("账户不存在")); } }