diff --git a/Account-Service/src/test/java/org/training/account/service/controller/AccountControllerTest.java b/Account-Service/src/test/java/org/training/account/service/controller/AccountControllerTest.java new file mode 100644 index 0000000..f0af0d8 --- /dev/null +++ b/Account-Service/src/test/java/org/training/account/service/controller/AccountControllerTest.java @@ -0,0 +1,211 @@ +package org.training.account.service.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +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 org.training.account.service.exception.ResourceNotFound; +import org.training.account.service.model.AccountStatus; +import org.training.account.service.model.dto.AccountDto; +import org.training.account.service.model.dto.AccountStatusUpdate; +import org.training.account.service.model.dto.external.TransactionResponse; +import org.training.account.service.model.dto.response.Response; +import org.training.account.service.service.AccountService; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.List; + +import static org.mockito.ArgumentMatchers.*; +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) +class AccountControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @MockBean + private AccountService accountService; + + @Test + void createAccount_validRequest_returnsCreated() throws Exception { + AccountDto accountDto = AccountDto.builder() + .userId(1L) + .accountType("SAVINGS") + .build(); + + Response response = Response.builder() + .responseCode("200") + .message("Account created successfully") + .build(); + + when(accountService.createAccount(any(AccountDto.class))) + .thenReturn(response); + + mockMvc.perform(post("/accounts") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(accountDto))) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.responseCode").value("200")) + .andExpect(jsonPath("$.message").value("Account created successfully")); + } + + @Test + void updateAccountStatus_validRequest_returnsOk() throws Exception { + String accountNumber = "ACC0001234"; + AccountStatusUpdate statusUpdate = new AccountStatusUpdate(); + statusUpdate.setAccountStatus(AccountStatus.ACTIVE); + + Response response = Response.builder() + .responseCode("200") + .message("Account updated successfully") + .build(); + + when(accountService.updateStatus(eq(accountNumber), any(AccountStatusUpdate.class))) + .thenReturn(response); + + mockMvc.perform(patch("/accounts") + .param("accountNumber", accountNumber) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(statusUpdate))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.responseCode").value("200")) + .andExpect(jsonPath("$.message").value("Account updated successfully")); + } + + @Test + void readByAccountNumber_accountExists_returnsOk() throws Exception { + String accountNumber = "ACC0001234"; + AccountDto accountDto = AccountDto.builder() + .accountNumber(accountNumber) + .accountType("SAVINGS") + .accountStatus("ACTIVE") + .availableBalance(BigDecimal.valueOf(1000)) + .userId(1L) + .build(); + + when(accountService.readAccountByAccountNumber(accountNumber)) + .thenReturn(accountDto); + + mockMvc.perform(get("/accounts") + .param("accountNumber", accountNumber)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.accountNumber").value(accountNumber)) + .andExpect(jsonPath("$.accountType").value("SAVINGS")) + .andExpect(jsonPath("$.accountStatus").value("ACTIVE")); + } + + @Test + void readByAccountNumber_accountNotFound_throwsException() throws Exception { + String accountNumber = "ACC9999999"; + + when(accountService.readAccountByAccountNumber(accountNumber)) + .thenThrow(new ResourceNotFound()); + + mockMvc.perform(get("/accounts") + .param("accountNumber", accountNumber)) + .andExpect(status().isBadRequest()); + } + + @Test + void updateAccount_validRequest_returnsOk() throws Exception { + String accountNumber = "ACC0001234"; + AccountDto accountDto = AccountDto.builder() + .accountNumber(accountNumber) + .availableBalance(BigDecimal.valueOf(2000)) + .build(); + + Response response = Response.builder() + .responseCode("200") + .message("Account updated successfully") + .build(); + + when(accountService.updateAccount(eq(accountNumber), any(AccountDto.class))) + .thenReturn(response); + + mockMvc.perform(put("/accounts") + .param("accountNumber", accountNumber) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(accountDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.responseCode").value("200")); + } + + @Test + void accountBalance_accountExists_returnsOk() throws Exception { + String accountNumber = "ACC0001234"; + String balance = "1500.50"; + + when(accountService.getBalance(accountNumber)) + .thenReturn(balance); + + mockMvc.perform(get("/accounts/balance") + .param("accountNumber", accountNumber)) + .andExpect(status().isOk()) + .andExpect(content().string(balance)); + } + + @Test + void getTransactionsFromAccountId_returnsOk() throws Exception { + String accountId = "ACC0001234"; + List transactions = Arrays.asList( + new TransactionResponse(), + new TransactionResponse() + ); + + when(accountService.getTransactionsFromAccountId(accountId)) + .thenReturn(transactions); + + mockMvc.perform(get("/accounts/{accountId}/transactions", accountId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$.length()").value(2)); + } + + @Test + void closeAccount_validRequest_returnsOk() throws Exception { + String accountNumber = "ACC0001234"; + Response response = Response.builder() + .responseCode("200") + .message("Account closed successfully") + .build(); + + when(accountService.closeAccount(accountNumber)) + .thenReturn(response); + + mockMvc.perform(put("/accounts/closure") + .param("accountNumber", accountNumber)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("Account closed successfully")); + } + + @Test + void readAccountByUserId_accountExists_returnsOk() throws Exception { + Long userId = 1L; + AccountDto accountDto = AccountDto.builder() + .userId(userId) + .accountNumber("ACC0001234") + .accountType("SAVINGS") + .accountStatus("ACTIVE") + .availableBalance(BigDecimal.valueOf(1000)) + .build(); + + when(accountService.readAccountByUserId(userId)) + .thenReturn(accountDto); + + mockMvc.perform(get("/accounts/{userId}", userId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.userId").value(userId)) + .andExpect(jsonPath("$.accountNumber").value("ACC0001234")) + .andExpect(jsonPath("$.accountStatus").value("ACTIVE")); + } +} diff --git a/Account-Service/src/test/java/org/training/account/service/service/implementation/AccountServiceImplTest.java b/Account-Service/src/test/java/org/training/account/service/service/implementation/AccountServiceImplTest.java new file mode 100644 index 0000000..14c4b4c --- /dev/null +++ b/Account-Service/src/test/java/org/training/account/service/service/implementation/AccountServiceImplTest.java @@ -0,0 +1,431 @@ +package org.training.account.service.service.implementation; + +import org.junit.jupiter.api.BeforeEach; +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.http.ResponseEntity; +import org.springframework.test.util.ReflectionTestUtils; +import org.training.account.service.exception.*; +import org.training.account.service.external.SequenceService; +import org.training.account.service.external.TransactionService; +import org.training.account.service.external.UserService; +import org.training.account.service.model.AccountStatus; +import org.training.account.service.model.AccountType; +import org.training.account.service.model.dto.AccountDto; +import org.training.account.service.model.dto.AccountStatusUpdate; +import org.training.account.service.model.dto.external.SequenceDto; +import org.training.account.service.model.dto.external.TransactionResponse; +import org.training.account.service.model.dto.external.UserDto; +import org.training.account.service.model.dto.response.Response; +import org.training.account.service.model.entity.Account; +import org.training.account.service.repository.AccountRepository; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class AccountServiceImplTest { + + @Mock + private UserService userService; + + @Mock + private AccountRepository accountRepository; + + @Mock + private SequenceService sequenceService; + + @Mock + private TransactionService transactionService; + + @InjectMocks + private AccountServiceImpl accountService; + + @BeforeEach + void setUp() { + ReflectionTestUtils.setField(accountService, "success", "200"); + } + + @Test + void createAccount_validUser_success() { + AccountDto accountDto = AccountDto.builder() + .userId(1L) + .accountType("SAVINGS_ACCOUNT") + .build(); + + UserDto userDto = UserDto.builder() + .userId(1L) + .firstName("John") + .lastName("Doe") + .build(); + + SequenceDto sequenceDto = new SequenceDto(); + sequenceDto.setAccountNumber(1234567L); + + when(userService.readUserById(1L)) + .thenReturn(ResponseEntity.ok(userDto)); + when(accountRepository.findAccountByUserIdAndAccountType(1L, AccountType.SAVINGS_ACCOUNT)) + .thenReturn(Optional.empty()); + when(sequenceService.generateAccountNumber()) + .thenReturn(sequenceDto); + when(accountRepository.save(any(Account.class))) + .thenReturn(new Account()); + + Response response = accountService.createAccount(accountDto); + + assertNotNull(response); + assertEquals("200", response.getResponseCode()); + verify(accountRepository, times(1)).save(any(Account.class)); + } + + @Test + void createAccount_userNotFound_throwsResourceNotFound() { + AccountDto accountDto = AccountDto.builder() + .userId(999L) + .accountType("SAVINGS_ACCOUNT") + .build(); + + when(userService.readUserById(999L)) + .thenReturn(ResponseEntity.ok(null)); + + assertThrows(ResourceNotFound.class, () -> accountService.createAccount(accountDto)); + verify(accountRepository, never()).save(any(Account.class)); + } + + @Test + void createAccount_duplicateAccount_throwsResourceConflict() { + AccountDto accountDto = AccountDto.builder() + .userId(1L) + .accountType("SAVINGS_ACCOUNT") + .build(); + + UserDto userDto = UserDto.builder() + .userId(1L) + .firstName("John") + .lastName("Doe") + .build(); + + Account existingAccount = new Account(); + existingAccount.setAccountId(1L); + existingAccount.setUserId(1L); + existingAccount.setAccountType(AccountType.SAVINGS_ACCOUNT); + + when(userService.readUserById(1L)) + .thenReturn(ResponseEntity.ok(userDto)); + when(accountRepository.findAccountByUserIdAndAccountType(1L, AccountType.SAVINGS_ACCOUNT)) + .thenReturn(Optional.of(existingAccount)); + + assertThrows(ResourceConflict.class, () -> accountService.createAccount(accountDto)); + verify(accountRepository, never()).save(any(Account.class)); + } + + @Test + void updateStatus_validUpdate_success() { + String accountNumber = "ACC0001234"; + AccountStatusUpdate statusUpdate = new AccountStatusUpdate(); + statusUpdate.setAccountStatus(AccountStatus.ACTIVE); + + Account account = new Account(); + account.setAccountNumber(accountNumber); + account.setAccountStatus(AccountStatus.PENDING); + account.setAvailableBalance(BigDecimal.valueOf(1000)); + + when(accountRepository.findAccountByAccountNumber(accountNumber)) + .thenReturn(Optional.of(account)); + when(accountRepository.save(any(Account.class))) + .thenReturn(account); + + Response response = accountService.updateStatus(accountNumber, statusUpdate); + + assertNotNull(response); + assertEquals("200", response.getResponseCode()); + verify(accountRepository, times(1)).save(any(Account.class)); + } + + @Test + void updateStatus_accountAlreadyActive_throwsAccountStatusException() { + String accountNumber = "ACC0001234"; + AccountStatusUpdate statusUpdate = new AccountStatusUpdate(); + statusUpdate.setAccountStatus(AccountStatus.ACTIVE); + + Account account = new Account(); + account.setAccountNumber(accountNumber); + account.setAccountStatus(AccountStatus.ACTIVE); + account.setAvailableBalance(BigDecimal.valueOf(1000)); + + when(accountRepository.findAccountByAccountNumber(accountNumber)) + .thenReturn(Optional.of(account)); + + assertThrows(AccountStatusException.class, + () -> accountService.updateStatus(accountNumber, statusUpdate)); + verify(accountRepository, never()).save(any(Account.class)); + } + + @Test + void updateStatus_balanceLessThanMinimum_throwsInSufficientFunds() { + String accountNumber = "ACC0001234"; + AccountStatusUpdate statusUpdate = new AccountStatusUpdate(); + statusUpdate.setAccountStatus(AccountStatus.ACTIVE); + + Account account = new Account(); + account.setAccountNumber(accountNumber); + account.setAccountStatus(AccountStatus.PENDING); + account.setAvailableBalance(BigDecimal.valueOf(999)); + + when(accountRepository.findAccountByAccountNumber(accountNumber)) + .thenReturn(Optional.of(account)); + + assertThrows(InSufficientFunds.class, + () -> accountService.updateStatus(accountNumber, statusUpdate)); + verify(accountRepository, never()).save(any(Account.class)); + } + + @Test + void updateStatus_balanceZero_throwsInSufficientFunds() { + String accountNumber = "ACC0001234"; + AccountStatusUpdate statusUpdate = new AccountStatusUpdate(); + statusUpdate.setAccountStatus(AccountStatus.ACTIVE); + + Account account = new Account(); + account.setAccountNumber(accountNumber); + account.setAccountStatus(AccountStatus.PENDING); + account.setAvailableBalance(BigDecimal.ZERO); + + when(accountRepository.findAccountByAccountNumber(accountNumber)) + .thenReturn(Optional.of(account)); + + assertThrows(InSufficientFunds.class, + () -> accountService.updateStatus(accountNumber, statusUpdate)); + verify(accountRepository, never()).save(any(Account.class)); + } + + @Test + void updateStatus_accountNotFound_throwsResourceNotFound() { + String accountNumber = "ACC9999999"; + AccountStatusUpdate statusUpdate = new AccountStatusUpdate(); + statusUpdate.setAccountStatus(AccountStatus.ACTIVE); + + when(accountRepository.findAccountByAccountNumber(accountNumber)) + .thenReturn(Optional.empty()); + + assertThrows(ResourceNotFound.class, + () -> accountService.updateStatus(accountNumber, statusUpdate)); + } + + @Test + void closeAccount_zeroBalance_success() { + String accountNumber = "ACC0001234"; + + Account account = new Account(); + account.setAccountNumber(accountNumber); + account.setAvailableBalance(BigDecimal.ZERO); + account.setAccountStatus(AccountStatus.ACTIVE); + + when(accountRepository.findAccountByAccountNumber(accountNumber)) + .thenReturn(Optional.of(account)); + + Response response = accountService.closeAccount(accountNumber); + + assertNotNull(response); + assertEquals(AccountStatus.CLOSED, account.getAccountStatus()); + } + + @Test + void closeAccount_nonZeroBalance_throwsAccountClosingException() { + String accountNumber = "ACC0001234"; + + Account account = new Account(); + account.setAccountNumber(accountNumber); + account.setAvailableBalance(BigDecimal.valueOf(100)); + account.setAccountStatus(AccountStatus.ACTIVE); + + when(accountRepository.findAccountByAccountNumber(accountNumber)) + .thenReturn(Optional.of(account)); + + assertThrows(AccountClosingException.class, + () -> accountService.closeAccount(accountNumber)); + } + + @Test + void closeAccount_accountNotFound_throwsResourceNotFound() { + String accountNumber = "ACC9999999"; + + when(accountRepository.findAccountByAccountNumber(accountNumber)) + .thenReturn(Optional.empty()); + + assertThrows(ResourceNotFound.class, + () -> accountService.closeAccount(accountNumber)); + } + + @Test + void readAccountByUserId_activeAccount_success() { + Long userId = 1L; + + Account account = new Account(); + account.setUserId(userId); + account.setAccountNumber("ACC0001234"); + account.setAccountStatus(AccountStatus.ACTIVE); + account.setAccountType(AccountType.SAVINGS_ACCOUNT); + account.setAvailableBalance(BigDecimal.valueOf(1000)); + + when(accountRepository.findAccountByUserId(userId)) + .thenReturn(Optional.of(account)); + + AccountDto result = accountService.readAccountByUserId(userId); + + assertNotNull(result); + assertEquals(userId, result.getUserId()); + assertEquals("ACTIVE", result.getAccountStatus()); + } + + @Test + void readAccountByUserId_inactiveAccount_throwsAccountStatusException() { + Long userId = 1L; + + Account account = new Account(); + account.setUserId(userId); + account.setAccountStatus(AccountStatus.PENDING); + + when(accountRepository.findAccountByUserId(userId)) + .thenReturn(Optional.of(account)); + + assertThrows(AccountStatusException.class, + () -> accountService.readAccountByUserId(userId)); + } + + @Test + void readAccountByUserId_accountNotFound_throwsResourceNotFound() { + Long userId = 999L; + + when(accountRepository.findAccountByUserId(userId)) + .thenReturn(Optional.empty()); + + assertThrows(ResourceNotFound.class, + () -> accountService.readAccountByUserId(userId)); + } + + @Test + void readAccountByAccountNumber_accountExists_returnsAccountDto() { + String accountNumber = "ACC0001234"; + + Account account = new Account(); + account.setAccountNumber(accountNumber); + account.setAccountStatus(AccountStatus.ACTIVE); + account.setAccountType(AccountType.SAVINGS_ACCOUNT); + account.setAvailableBalance(BigDecimal.valueOf(1000)); + + when(accountRepository.findAccountByAccountNumber(accountNumber)) + .thenReturn(Optional.of(account)); + + AccountDto result = accountService.readAccountByAccountNumber(accountNumber); + + assertNotNull(result); + assertEquals(accountNumber, result.getAccountNumber()); + assertEquals("ACTIVE", result.getAccountStatus()); + assertEquals("SAVINGS_ACCOUNT", result.getAccountType()); + } + + @Test + void readAccountByAccountNumber_accountNotFound_throwsResourceNotFound() { + String accountNumber = "ACC9999999"; + + when(accountRepository.findAccountByAccountNumber(accountNumber)) + .thenReturn(Optional.empty()); + + assertThrows(ResourceNotFound.class, + () -> accountService.readAccountByAccountNumber(accountNumber)); + } + + @Test + void updateAccount_validUpdate_success() { + String accountNumber = "ACC0001234"; + AccountDto accountDto = AccountDto.builder() + .accountNumber(accountNumber) + .availableBalance(BigDecimal.valueOf(2000)) + .build(); + + Account account = new Account(); + account.setAccountNumber(accountNumber); + account.setAvailableBalance(BigDecimal.valueOf(1000)); + + when(accountRepository.findAccountByAccountNumber(accountNumber)) + .thenReturn(Optional.of(account)); + when(accountRepository.save(any(Account.class))) + .thenReturn(account); + + Response response = accountService.updateAccount(accountNumber, accountDto); + + assertNotNull(response); + assertEquals("200", response.getResponseCode()); + verify(accountRepository, times(1)).save(any(Account.class)); + } + + @Test + void updateAccount_accountNotFound_throwsResourceNotFound() { + String accountNumber = "ACC9999999"; + AccountDto accountDto = AccountDto.builder() + .accountNumber(accountNumber) + .build(); + + when(accountRepository.findAccountByAccountNumber(accountNumber)) + .thenReturn(Optional.empty()); + + assertThrows(ResourceNotFound.class, + () -> accountService.updateAccount(accountNumber, accountDto)); + } + + @Test + void getBalance_accountExists_returnsBalance() { + String accountNumber = "ACC0001234"; + + Account account = new Account(); + account.setAccountNumber(accountNumber); + account.setAvailableBalance(BigDecimal.valueOf(1500.50)); + + when(accountRepository.findAccountByAccountNumber(accountNumber)) + .thenReturn(Optional.of(account)); + + String balance = accountService.getBalance(accountNumber); + + assertNotNull(balance); + assertEquals("1500.5", balance); + } + + @Test + void getBalance_accountNotFound_throwsResourceNotFound() { + String accountNumber = "ACC9999999"; + + when(accountRepository.findAccountByAccountNumber(accountNumber)) + .thenReturn(Optional.empty()); + + assertThrows(ResourceNotFound.class, + () -> accountService.getBalance(accountNumber)); + } + + @Test + void getTransactionsFromAccountId_returnsTransactionList() { + String accountId = "ACC0001234"; + List transactions = Arrays.asList( + new TransactionResponse(), + new TransactionResponse() + ); + + when(transactionService.getTransactionsFromAccountId(accountId)) + .thenReturn(transactions); + + List result = accountService.getTransactionsFromAccountId(accountId); + + assertNotNull(result); + assertEquals(2, result.size()); + verify(transactionService, times(1)).getTransactionsFromAccountId(accountId); + } +} diff --git a/Fund-Transfer/src/test/java/org/training/fundtransfer/controller/FundTransferControllerTest.java b/Fund-Transfer/src/test/java/org/training/fundtransfer/controller/FundTransferControllerTest.java new file mode 100644 index 0000000..c85f049 --- /dev/null +++ b/Fund-Transfer/src/test/java/org/training/fundtransfer/controller/FundTransferControllerTest.java @@ -0,0 +1,135 @@ +package org.training.fundtransfer.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +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 org.training.fundtransfer.exception.ResourceNotFound; +import org.training.fundtransfer.model.dto.FundTransferDto; +import org.training.fundtransfer.model.dto.request.FundTransferRequest; +import org.training.fundtransfer.model.dto.response.FundTransferResponse; +import org.training.fundtransfer.service.FundTransferService; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@WebMvcTest(FundTransferController.class) +class FundTransferControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @MockBean + private FundTransferService fundTransferService; + + @Test + void fundTransfer_validRequest_returnsCreated() throws Exception { + FundTransferRequest request = FundTransferRequest.builder() + .fromAccount("ACC0000001") + .toAccount("ACC0000002") + .amount(BigDecimal.valueOf(500)) + .build(); + + FundTransferResponse response = FundTransferResponse.builder() + .transactionId("txn123") + .message("Fund transfer was successful") + .build(); + + when(fundTransferService.fundTransfer(any(FundTransferRequest.class))) + .thenReturn(response); + + mockMvc.perform(post("/fund-transfers") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.transactionId").value("txn123")) + .andExpect(jsonPath("$.message").value("Fund transfer was successful")); + } + + @Test + void getTransferDetailsFromReferenceId_transferExists_returnsOk() throws Exception { + String referenceId = "ref123"; + FundTransferDto transferDto = FundTransferDto.builder() + .fromAccount("ACC0000001") + .toAccount("ACC0000002") + .amount(BigDecimal.valueOf(500)) + .transactionReference(referenceId) + .build(); + + when(fundTransferService.getTransferDetailsFromReferenceId(referenceId)) + .thenReturn(transferDto); + + mockMvc.perform(get("/fund-transfers/{referenceId}", referenceId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.fromAccount").value("ACC0000001")) + .andExpect(jsonPath("$.toAccount").value("ACC0000002")) + .andExpect(jsonPath("$.amount").value(500)); + } + + @Test + void getTransferDetailsFromReferenceId_transferNotFound_throwsException() throws Exception { + String referenceId = "nonexistent"; + + when(fundTransferService.getTransferDetailsFromReferenceId(referenceId)) + .thenThrow(new ResourceNotFound("Fund transfer not found", null)); + + mockMvc.perform(get("/fund-transfers/{referenceId}", referenceId)) + .andExpect(status().isBadRequest()); + } + + @Test + void getAllTransfersByAccountId_transfersExist_returnsOk() throws Exception { + String accountId = "ACC0000001"; + List transfers = Arrays.asList( + FundTransferDto.builder() + .fromAccount(accountId) + .toAccount("ACC0000002") + .amount(BigDecimal.valueOf(100)) + .build(), + FundTransferDto.builder() + .fromAccount(accountId) + .toAccount("ACC0000003") + .amount(BigDecimal.valueOf(200)) + .build() + ); + + when(fundTransferService.getAllTransfersByAccountId(accountId)) + .thenReturn(transfers); + + mockMvc.perform(get("/fund-transfers") + .param("accountId", accountId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$.length()").value(2)) + .andExpect(jsonPath("$[0].fromAccount").value(accountId)) + .andExpect(jsonPath("$[1].fromAccount").value(accountId)); + } + + @Test + void getAllTransfersByAccountId_noTransfers_returnsEmptyList() throws Exception { + String accountId = "ACC0000001"; + + when(fundTransferService.getAllTransfersByAccountId(accountId)) + .thenReturn(Arrays.asList()); + + mockMvc.perform(get("/fund-transfers") + .param("accountId", accountId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$.length()").value(0)); + } +} diff --git a/Fund-Transfer/src/test/java/org/training/fundtransfer/service/implementation/FundTransferServiceImplTest.java b/Fund-Transfer/src/test/java/org/training/fundtransfer/service/implementation/FundTransferServiceImplTest.java new file mode 100644 index 0000000..59492ff --- /dev/null +++ b/Fund-Transfer/src/test/java/org/training/fundtransfer/service/implementation/FundTransferServiceImplTest.java @@ -0,0 +1,278 @@ +package org.training.fundtransfer.service.implementation; + +import org.junit.jupiter.api.BeforeEach; +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.http.ResponseEntity; +import org.springframework.test.util.ReflectionTestUtils; +import org.training.fundtransfer.exception.AccountUpdateException; +import org.training.fundtransfer.exception.InsufficientBalance; +import org.training.fundtransfer.exception.ResourceNotFound; +import org.training.fundtransfer.external.AccountService; +import org.training.fundtransfer.external.TransactionService; +import org.training.fundtransfer.model.dto.Account; +import org.training.fundtransfer.model.dto.FundTransferDto; +import org.training.fundtransfer.model.dto.Transaction; +import org.training.fundtransfer.model.dto.request.FundTransferRequest; +import org.training.fundtransfer.model.dto.response.FundTransferResponse; +import org.training.fundtransfer.model.dto.response.Response; +import org.training.fundtransfer.model.entity.FundTransfer; +import org.training.fundtransfer.repository.FundTransferRepository; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class FundTransferServiceImplTest { + + @Mock + private AccountService accountService; + + @Mock + private FundTransferRepository fundTransferRepository; + + @Mock + private TransactionService transactionService; + + @InjectMocks + private FundTransferServiceImpl fundTransferService; + + @BeforeEach + void setUp() { + ReflectionTestUtils.setField(fundTransferService, "ok", "200"); + } + + @Test + void fundTransfer_validTransfer_success() { + FundTransferRequest request = FundTransferRequest.builder() + .fromAccount("ACC0000001") + .toAccount("ACC0000002") + .amount(BigDecimal.valueOf(500)) + .build(); + + Account fromAccount = Account.builder() + .accountId(1L) + .accountNumber("ACC0000001") + .accountStatus("ACTIVE") + .availableBalance(BigDecimal.valueOf(1000)) + .userId(1L) + .build(); + + Account toAccount = Account.builder() + .accountId(2L) + .accountNumber("ACC0000002") + .accountStatus("ACTIVE") + .availableBalance(BigDecimal.valueOf(500)) + .userId(2L) + .build(); + + when(accountService.readByAccountNumber("ACC0000001")) + .thenReturn(ResponseEntity.ok(fromAccount)); + when(accountService.readByAccountNumber("ACC0000002")) + .thenReturn(ResponseEntity.ok(toAccount)); + when(accountService.updateAccount(anyString(), any(Account.class))) + .thenReturn(ResponseEntity.ok(Response.builder().build())); + when(transactionService.makeInternalTransactions(anyList(), anyString())) + .thenReturn(ResponseEntity.ok(Response.builder().build())); + when(fundTransferRepository.save(any(FundTransfer.class))) + .thenReturn(new FundTransfer()); + + FundTransferResponse response = fundTransferService.fundTransfer(request); + + assertNotNull(response); + assertNotNull(response.getTransactionId()); + assertEquals("Fund transfer was successful", response.getMessage()); + verify(fundTransferRepository, times(1)).save(any(FundTransfer.class)); + verify(accountService, times(2)).updateAccount(anyString(), any(Account.class)); + verify(transactionService, times(1)).makeInternalTransactions(anyList(), anyString()); + } + + @Test + void fundTransfer_sourceAccountNotFound_throwsResourceNotFound() { + FundTransferRequest request = FundTransferRequest.builder() + .fromAccount("ACC9999999") + .toAccount("ACC0000002") + .amount(BigDecimal.valueOf(500)) + .build(); + + when(accountService.readByAccountNumber("ACC9999999")) + .thenReturn(ResponseEntity.ok(null)); + + assertThrows(ResourceNotFound.class, () -> fundTransferService.fundTransfer(request)); + verify(fundTransferRepository, never()).save(any(FundTransfer.class)); + } + + @Test + void fundTransfer_destinationAccountNotFound_throwsResourceNotFound() { + FundTransferRequest request = FundTransferRequest.builder() + .fromAccount("ACC0000001") + .toAccount("ACC9999999") + .amount(BigDecimal.valueOf(500)) + .build(); + + Account fromAccount = Account.builder() + .accountId(1L) + .accountNumber("ACC0000001") + .accountStatus("ACTIVE") + .availableBalance(BigDecimal.valueOf(1000)) + .build(); + + when(accountService.readByAccountNumber("ACC0000001")) + .thenReturn(ResponseEntity.ok(fromAccount)); + when(accountService.readByAccountNumber("ACC9999999")) + .thenReturn(ResponseEntity.ok(null)); + + assertThrows(ResourceNotFound.class, () -> fundTransferService.fundTransfer(request)); + verify(fundTransferRepository, never()).save(any(FundTransfer.class)); + } + + @Test + void fundTransfer_sourceAccountNotActive_throwsAccountUpdateException() { + FundTransferRequest request = FundTransferRequest.builder() + .fromAccount("ACC0000001") + .toAccount("ACC0000002") + .amount(BigDecimal.valueOf(500)) + .build(); + + Account fromAccount = Account.builder() + .accountId(1L) + .accountNumber("ACC0000001") + .accountStatus("PENDING") + .availableBalance(BigDecimal.valueOf(1000)) + .build(); + + when(accountService.readByAccountNumber("ACC0000001")) + .thenReturn(ResponseEntity.ok(fromAccount)); + + assertThrows(AccountUpdateException.class, () -> fundTransferService.fundTransfer(request)); + verify(fundTransferRepository, never()).save(any(FundTransfer.class)); + } + + @Test + void fundTransfer_insufficientBalance_throwsInsufficientBalance() { + FundTransferRequest request = FundTransferRequest.builder() + .fromAccount("ACC0000001") + .toAccount("ACC0000002") + .amount(BigDecimal.valueOf(1500)) + .build(); + + Account fromAccount = Account.builder() + .accountId(1L) + .accountNumber("ACC0000001") + .accountStatus("ACTIVE") + .availableBalance(BigDecimal.valueOf(1000)) + .build(); + + when(accountService.readByAccountNumber("ACC0000001")) + .thenReturn(ResponseEntity.ok(fromAccount)); + + assertThrows(InsufficientBalance.class, () -> fundTransferService.fundTransfer(request)); + verify(fundTransferRepository, never()).save(any(FundTransfer.class)); + } + + @Test + void fundTransfer_balanceEqualToAmount_throwsInsufficientBalance() { + FundTransferRequest request = FundTransferRequest.builder() + .fromAccount("ACC0000001") + .toAccount("ACC0000002") + .amount(BigDecimal.valueOf(1000)) + .build(); + + Account fromAccount = Account.builder() + .accountId(1L) + .accountNumber("ACC0000001") + .accountStatus("ACTIVE") + .availableBalance(BigDecimal.valueOf(1000)) + .build(); + + when(accountService.readByAccountNumber("ACC0000001")) + .thenReturn(ResponseEntity.ok(fromAccount)); + + assertThrows(InsufficientBalance.class, () -> fundTransferService.fundTransfer(request)); + verify(fundTransferRepository, never()).save(any(FundTransfer.class)); + } + + @Test + void getTransferDetailsFromReferenceId_transferFound_returnsTransferDto() { + String referenceId = "ref123"; + FundTransfer fundTransfer = FundTransfer.builder() + .fundTransferId(1L) + .fromAccount("ACC0000001") + .toAccount("ACC0000002") + .amount(BigDecimal.valueOf(500)) + .transactionReference(referenceId) + .build(); + + when(fundTransferRepository.findFundTransferByTransactionReference(referenceId)) + .thenReturn(Optional.of(fundTransfer)); + + FundTransferDto result = fundTransferService.getTransferDetailsFromReferenceId(referenceId); + + assertNotNull(result); + assertEquals("ACC0000001", result.getFromAccount()); + assertEquals("ACC0000002", result.getToAccount()); + verify(fundTransferRepository, times(1)).findFundTransferByTransactionReference(referenceId); + } + + @Test + void getTransferDetailsFromReferenceId_transferNotFound_throwsResourceNotFound() { + String referenceId = "nonexistent"; + + when(fundTransferRepository.findFundTransferByTransactionReference(referenceId)) + .thenReturn(Optional.empty()); + + assertThrows(ResourceNotFound.class, + () -> fundTransferService.getTransferDetailsFromReferenceId(referenceId)); + } + + @Test + void getAllTransfersByAccountId_transfersExist_returnsTransferList() { + String accountId = "ACC0000001"; + List transfers = Arrays.asList( + FundTransfer.builder() + .fundTransferId(1L) + .fromAccount(accountId) + .toAccount("ACC0000002") + .amount(BigDecimal.valueOf(100)) + .build(), + FundTransfer.builder() + .fundTransferId(2L) + .fromAccount(accountId) + .toAccount("ACC0000003") + .amount(BigDecimal.valueOf(200)) + .build() + ); + + when(fundTransferRepository.findFundTransferByFromAccount(accountId)) + .thenReturn(transfers); + + List result = fundTransferService.getAllTransfersByAccountId(accountId); + + assertNotNull(result); + assertEquals(2, result.size()); + verify(fundTransferRepository, times(1)).findFundTransferByFromAccount(accountId); + } + + @Test + void getAllTransfersByAccountId_noTransfers_returnsEmptyList() { + String accountId = "ACC0000001"; + + when(fundTransferRepository.findFundTransferByFromAccount(accountId)) + .thenReturn(Arrays.asList()); + + List result = fundTransferService.getAllTransfersByAccountId(accountId); + + assertNotNull(result); + assertTrue(result.isEmpty()); + verify(fundTransferRepository, times(1)).findFundTransferByFromAccount(accountId); + } +} diff --git a/Transaction-Service/src/test/java/org/training/transactions/controller/TransactionControllerTest.java b/Transaction-Service/src/test/java/org/training/transactions/controller/TransactionControllerTest.java new file mode 100644 index 0000000..00f71ea --- /dev/null +++ b/Transaction-Service/src/test/java/org/training/transactions/controller/TransactionControllerTest.java @@ -0,0 +1,155 @@ +package org.training.transactions.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +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 org.training.transactions.model.dto.TransactionDto; +import org.training.transactions.model.response.Response; +import org.training.transactions.model.response.TransactionRequest; +import org.training.transactions.service.TransactionService; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.List; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@WebMvcTest(TransactionController.class) +class TransactionControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @MockBean + private TransactionService transactionService; + + @Test + void addTransactions_validRequest_returnsCreated() throws Exception { + TransactionDto transactionDto = TransactionDto.builder() + .accountId("ACC0001234") + .transactionType("DEPOSIT") + .amount(BigDecimal.valueOf(500)) + .description("Deposit funds") + .build(); + + Response response = Response.builder() + .responseCode("200") + .message("Transaction completed successfully") + .build(); + + when(transactionService.addTransaction(any(TransactionDto.class))) + .thenReturn(response); + + mockMvc.perform(post("/transactions") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(transactionDto))) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.responseCode").value("200")) + .andExpect(jsonPath("$.message").value("Transaction completed successfully")); + } + + @Test + void makeInternalTransaction_validRequest_returnsCreated() throws Exception { + List transactionDtos = Arrays.asList( + TransactionDto.builder() + .accountId("ACC0001234") + .amount(BigDecimal.valueOf(500).negate()) + .build(), + TransactionDto.builder() + .accountId("ACC0005678") + .amount(BigDecimal.valueOf(500)) + .build() + ); + + String transactionReference = "ref123"; + + Response response = Response.builder() + .responseCode("200") + .message("Transaction completed successfully") + .build(); + + when(transactionService.internalTransaction(anyList(), eq(transactionReference))) + .thenReturn(response); + + mockMvc.perform(post("/transactions/internal") + .param("transactionReference", transactionReference) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(transactionDtos))) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.responseCode").value("200")) + .andExpect(jsonPath("$.message").value("Transaction completed successfully")); + } + + @Test + void getTransactions_transactionsExist_returnsOk() throws Exception { + String accountId = "ACC0001234"; + List transactions = Arrays.asList( + new TransactionRequest(), + new TransactionRequest() + ); + + when(transactionService.getTransaction(accountId)) + .thenReturn(transactions); + + mockMvc.perform(get("/transactions") + .param("accountId", accountId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$.length()").value(2)); + } + + @Test + void getTransactions_noTransactions_returnsEmptyList() throws Exception { + String accountId = "ACC0001234"; + + when(transactionService.getTransaction(accountId)) + .thenReturn(Arrays.asList()); + + mockMvc.perform(get("/transactions") + .param("accountId", accountId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$.length()").value(0)); + } + + @Test + void getTransactionByTransactionReference_transactionsExist_returnsOk() throws Exception { + String referenceId = "ref123"; + List transactions = Arrays.asList( + new TransactionRequest(), + new TransactionRequest() + ); + + when(transactionService.getTransactionByTransactionReference(referenceId)) + .thenReturn(transactions); + + mockMvc.perform(get("/transactions/{referenceId}", referenceId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$.length()").value(2)); + } + + @Test + void getTransactionByTransactionReference_noTransactions_returnsEmptyList() throws Exception { + String referenceId = "nonexistent"; + + when(transactionService.getTransactionByTransactionReference(referenceId)) + .thenReturn(Arrays.asList()); + + mockMvc.perform(get("/transactions/{referenceId}", referenceId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$.length()").value(0)); + } +} diff --git a/Transaction-Service/src/test/java/org/training/transactions/service/TransactionServiceTest.java b/Transaction-Service/src/test/java/org/training/transactions/service/TransactionServiceTest.java new file mode 100644 index 0000000..8f0209d --- /dev/null +++ b/Transaction-Service/src/test/java/org/training/transactions/service/TransactionServiceTest.java @@ -0,0 +1,291 @@ +package org.training.transactions.service; + +import org.junit.jupiter.api.BeforeEach; +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.http.ResponseEntity; +import org.springframework.test.util.ReflectionTestUtils; +import org.training.transactions.exception.AccountStatusException; +import org.training.transactions.exception.InsufficientBalance; +import org.training.transactions.exception.ResourceNotFound; +import org.training.transactions.external.AccountService; +import org.training.transactions.model.TransactionStatus; +import org.training.transactions.model.TransactionType; +import org.training.transactions.model.dto.TransactionDto; +import org.training.transactions.model.entity.Transaction; +import org.training.transactions.model.external.Account; +import org.training.transactions.model.response.Response; +import org.training.transactions.model.response.TransactionRequest; +import org.training.transactions.repository.TransactionRepository; +import org.training.transactions.service.implementation.TransactionServiceImpl; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class TransactionServiceTest { + + @Mock + private TransactionRepository transactionRepository; + + @Mock + private AccountService accountService; + + @InjectMocks + private TransactionServiceImpl transactionService; + + @BeforeEach + void setUp() { + ReflectionTestUtils.setField(transactionService, "ok", "200"); + } + + @Test + void addTransaction_deposit_success() { + TransactionDto transactionDto = TransactionDto.builder() + .accountId("ACC0001234") + .transactionType("DEPOSIT") + .amount(BigDecimal.valueOf(500)) + .description("Deposit funds") + .build(); + + Account account = Account.builder() + .accountId(1L) + .accountNumber("ACC0001234") + .accountStatus("ACTIVE") + .availableBalance(BigDecimal.valueOf(1000)) + .build(); + + when(accountService.readByAccountNumber("ACC0001234")) + .thenReturn(ResponseEntity.ok(account)); + when(accountService.updateAccount(anyString(), any(Account.class))) + .thenReturn(ResponseEntity.ok().build()); + when(transactionRepository.save(any(Transaction.class))) + .thenReturn(new Transaction()); + + Response response = transactionService.addTransaction(transactionDto); + + assertNotNull(response); + assertEquals("200", response.getResponseCode()); + assertEquals("Transaction completed successfully", response.getMessage()); + verify(transactionRepository, times(1)).save(any(Transaction.class)); + verify(accountService, times(1)).updateAccount(anyString(), any(Account.class)); + } + + @Test + void addTransaction_deposit_accountNotFound_throwsResourceNotFound() { + TransactionDto transactionDto = TransactionDto.builder() + .accountId("ACC9999999") + .transactionType("DEPOSIT") + .amount(BigDecimal.valueOf(500)) + .build(); + + when(accountService.readByAccountNumber("ACC9999999")) + .thenReturn(ResponseEntity.ok(null)); + + assertThrows(ResourceNotFound.class, () -> transactionService.addTransaction(transactionDto)); + verify(transactionRepository, never()).save(any(Transaction.class)); + } + + @Test + void addTransaction_withdrawal_success() { + TransactionDto transactionDto = TransactionDto.builder() + .accountId("ACC0001234") + .transactionType("WITHDRAWAL") + .amount(BigDecimal.valueOf(300)) + .description("Withdraw funds") + .build(); + + Account account = Account.builder() + .accountId(1L) + .accountNumber("ACC0001234") + .accountStatus("ACTIVE") + .availableBalance(BigDecimal.valueOf(1000)) + .build(); + + when(accountService.readByAccountNumber("ACC0001234")) + .thenReturn(ResponseEntity.ok(account)); + when(accountService.updateAccount(anyString(), any(Account.class))) + .thenReturn(ResponseEntity.ok().build()); + when(transactionRepository.save(any(Transaction.class))) + .thenReturn(new Transaction()); + + Response response = transactionService.addTransaction(transactionDto); + + assertNotNull(response); + assertEquals("200", response.getResponseCode()); + verify(transactionRepository, times(1)).save(any(Transaction.class)); + } + + @Test + void addTransaction_withdrawal_accountNotActive_throwsAccountStatusException() { + TransactionDto transactionDto = TransactionDto.builder() + .accountId("ACC0001234") + .transactionType("WITHDRAWAL") + .amount(BigDecimal.valueOf(300)) + .build(); + + Account account = Account.builder() + .accountId(1L) + .accountNumber("ACC0001234") + .accountStatus("PENDING") + .availableBalance(BigDecimal.valueOf(1000)) + .build(); + + when(accountService.readByAccountNumber("ACC0001234")) + .thenReturn(ResponseEntity.ok(account)); + + assertThrows(AccountStatusException.class, + () -> transactionService.addTransaction(transactionDto)); + verify(transactionRepository, never()).save(any(Transaction.class)); + } + + @Test + void addTransaction_withdrawal_insufficientBalance_throwsInsufficientBalance() { + TransactionDto transactionDto = TransactionDto.builder() + .accountId("ACC0001234") + .transactionType("WITHDRAWAL") + .amount(BigDecimal.valueOf(1500)) + .build(); + + Account account = Account.builder() + .accountId(1L) + .accountNumber("ACC0001234") + .accountStatus("ACTIVE") + .availableBalance(BigDecimal.valueOf(1000)) + .build(); + + when(accountService.readByAccountNumber("ACC0001234")) + .thenReturn(ResponseEntity.ok(account)); + + assertThrows(InsufficientBalance.class, + () -> transactionService.addTransaction(transactionDto)); + verify(transactionRepository, never()).save(any(Transaction.class)); + } + + @Test + void addTransaction_withdrawal_accountNotFound_throwsResourceNotFound() { + TransactionDto transactionDto = TransactionDto.builder() + .accountId("ACC9999999") + .transactionType("WITHDRAWAL") + .amount(BigDecimal.valueOf(300)) + .build(); + + when(accountService.readByAccountNumber("ACC9999999")) + .thenReturn(ResponseEntity.ok(null)); + + assertThrows(ResourceNotFound.class, + () -> transactionService.addTransaction(transactionDto)); + verify(transactionRepository, never()).save(any(Transaction.class)); + } + + @Test + void internalTransaction_validTransactions_success() { + List transactionDtos = Arrays.asList( + TransactionDto.builder() + .accountId("ACC0001234") + .amount(BigDecimal.valueOf(500).negate()) + .description("Debit") + .build(), + TransactionDto.builder() + .accountId("ACC0005678") + .amount(BigDecimal.valueOf(500)) + .description("Credit") + .build() + ); + + String transactionReference = "ref123"; + + when(transactionRepository.saveAll(anyList())) + .thenReturn(Arrays.asList(new Transaction(), new Transaction())); + + Response response = transactionService.internalTransaction(transactionDtos, transactionReference); + + assertNotNull(response); + assertEquals("200", response.getResponseCode()); + assertEquals("Transaction completed successfully", response.getMessage()); + verify(transactionRepository, times(1)).saveAll(anyList()); + } + + @Test + void getTransaction_transactionsExist_returnsTransactionList() { + String accountId = "ACC0001234"; + List transactions = Arrays.asList( + createTransaction(accountId, TransactionType.DEPOSIT, BigDecimal.valueOf(100)), + createTransaction(accountId, TransactionType.WITHDRAWAL, BigDecimal.valueOf(50)) + ); + + when(transactionRepository.findTransactionByAccountId(accountId)) + .thenReturn(transactions); + + List result = transactionService.getTransaction(accountId); + + assertNotNull(result); + assertEquals(2, result.size()); + verify(transactionRepository, times(1)).findTransactionByAccountId(accountId); + } + + @Test + void getTransaction_noTransactions_returnsEmptyList() { + String accountId = "ACC0001234"; + + when(transactionRepository.findTransactionByAccountId(accountId)) + .thenReturn(Arrays.asList()); + + List result = transactionService.getTransaction(accountId); + + assertNotNull(result); + assertTrue(result.isEmpty()); + verify(transactionRepository, times(1)).findTransactionByAccountId(accountId); + } + + @Test + void getTransactionByTransactionReference_transactionsExist_returnsTransactionList() { + String transactionReference = "ref123"; + List transactions = Arrays.asList( + createTransaction("ACC0001234", TransactionType.INTERNAL_TRANSFER, BigDecimal.valueOf(500).negate()), + createTransaction("ACC0005678", TransactionType.INTERNAL_TRANSFER, BigDecimal.valueOf(500)) + ); + + when(transactionRepository.findTransactionByReferenceId(transactionReference)) + .thenReturn(transactions); + + List result = transactionService.getTransactionByTransactionReference(transactionReference); + + assertNotNull(result); + assertEquals(2, result.size()); + verify(transactionRepository, times(1)).findTransactionByReferenceId(transactionReference); + } + + @Test + void getTransactionByTransactionReference_noTransactions_returnsEmptyList() { + String transactionReference = "nonexistent"; + + when(transactionRepository.findTransactionByReferenceId(transactionReference)) + .thenReturn(Arrays.asList()); + + List result = transactionService.getTransactionByTransactionReference(transactionReference); + + assertNotNull(result); + assertTrue(result.isEmpty()); + verify(transactionRepository, times(1)).findTransactionByReferenceId(transactionReference); + } + + private Transaction createTransaction(String accountId, TransactionType type, BigDecimal amount) { + Transaction transaction = new Transaction(); + transaction.setAccountId(accountId); + transaction.setTransactionType(type); + transaction.setAmount(amount); + transaction.setStatus(TransactionStatus.COMPLETED); + transaction.setReferenceId("ref123"); + return transaction; + } +}