diff --git a/Online-Banking-App-Spring-Boot/pom.xml b/Online-Banking-App-Spring-Boot/pom.xml index 2f14ebe..fdd1762 100644 --- a/Online-Banking-App-Spring-Boot/pom.xml +++ b/Online-Banking-App-Spring-Boot/pom.xml @@ -1,114 +1,171 @@ - - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.7.15 - - - com.beko - DemoBank_v1 - 0.0.1-SNAPSHOT - DemoBank_v1 - Demo project for Spring Boot - - 1.8 - - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-mail - - - org.springframework.boot - spring-boot-starter-web - - - - com.mysql - mysql-connector-j - runtime - - - - org.springframework.boot - spring-boot-starter-tomcat - provided - - - org.springframework.boot - spring-boot-starter-test - test - - - javax.servlet - javax.servlet-api - 3.1.0 - - - - org.apache.tomcat.embed - tomcat-embed-jasper - provided - - - - org.springframework.boot - spring-boot-starter-validation - - - - javax.servlet.jsp - javax.servlet.jsp-api - 2.3.3 - provided - - - - javax.servlet - jstl - 1.2 - - - - org.springframework.security - spring-security-crypto - - - - io.jsonwebtoken - jjwt-api - 0.11.5 - - - - io.jsonwebtoken - jjwt-impl - 0.11.2 - runtime - - - - io.jsonwebtoken - jjwt-jackson - 0.11.2 - runtime - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.15 + + + + com.beko + DemoBank_v1 + 0.0.1-SNAPSHOT + DemoBank_v1 + Demo project for Spring Boot + + 1.8 + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-mail + + + org.springframework.boot + spring-boot-starter-web + + + com.mysql + mysql-connector-j + runtime + + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + javax.servlet + javax.servlet-api + 3.1.0 + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + org.springframework.boot + spring-boot-starter-validation + + + javax.servlet.jsp + javax.servlet.jsp-api + 2.3.3 + provided + + + javax.servlet + jstl + 1.2 + + + org.springframework.security + spring-security-crypto + + + io.jsonwebtoken + jjwt-api + 0.11.5 + + + io.jsonwebtoken + jjwt-impl + 0.11.2 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.2 + runtime + + + org.mockito + mockito-junit-jupiter + 2.23.4 + test + + + + io.spring.javaformat + spring-javaformat-formatter + 0.0.40 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.jacoco + jacoco-maven-plugin + 0.8.11 + + + + prepare-agent + + + + report + test + + report + + + coverageReport + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + + + org.apache.maven.plugins + maven-surefire-report-plugin + 3.2.5 + + testReport + + + + + org.apache.maven.plugins + maven-site-plugin + 2.1 + + testReport + + + + + io.spring.javaformat + spring-javaformat-maven-plugin + 0.0.40 + + + + + + + + \ No newline at end of file diff --git a/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerDepositTest.java b/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerDepositTest.java new file mode 100644 index 0000000..633fcaf --- /dev/null +++ b/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerDepositTest.java @@ -0,0 +1,547 @@ + +// ********RoostGPT******** +/* +Test generated by RoostGPT for test java-demo using AI Type AWS Bedrock Runtime AI and AI Model global.anthropic.claude-sonnet-4-20250514-v1:0 + +ROOST_METHOD_HASH=deposit_d889dac99b +ROOST_METHOD_SIG_HASH=deposit_54d4c8262f + +Scenario 1: Successful Deposit with Valid Data + +Details: + TestName: depositAmountSuccessfully + Description: This test verifies that a deposit transaction is processed successfully when valid deposit amount and account ID are provided, and the user session contains a valid user object. + +Execution: + Arrange: Set up mock repositories, create a valid user object in the session, prepare request map with valid deposit_amount and account_id, mock repository methods to return expected values. + Act: Call the deposit method with the prepared request map and mock session. + Assert: Verify that the response status is OK (200), response body contains success message, account balance is updated correctly, and transaction is logged. + +Validation: + This assertion verifies that the deposit functionality works correctly under normal conditions. It ensures that the business logic for adding funds to an account is properly implemented and that all necessary side effects (balance update and transaction logging) occur as expected. + +Scenario 2: Empty Deposit Amount Validation + +Details: + TestName: depositWithEmptyAmount + Description: This test validates that the method returns a bad request response when the deposit amount field in the request map is empty or null. + +Execution: + Arrange: Create a request map with empty deposit_amount and valid account_id, set up mock session with valid user. + Act: Invoke the deposit method with the prepared request containing empty deposit amount. + Assert: Verify that the response status is BAD_REQUEST (400) and the response body contains the appropriate error message about empty fields. + +Validation: + This test ensures that input validation is working correctly and prevents processing of invalid requests. It verifies that the application properly handles edge cases where required fields are missing, maintaining data integrity and user experience. + +Scenario 3: Empty Account ID Validation + +Details: + TestName: depositWithEmptyAccountId + Description: This test checks that the method correctly handles requests where the account_id field is empty, returning an appropriate error response. + +Execution: + Arrange: Prepare request map with valid deposit_amount but empty account_id, mock session with valid user object. + Act: Call the deposit method with the request map containing empty account ID. + Assert: Confirm that the response status is BAD_REQUEST (400) and the error message indicates that account ID cannot be empty. + +Validation: + This assertion validates that the method properly enforces business rules requiring both deposit amount and account ID to be present. It ensures that incomplete requests are rejected before any processing occurs, preventing potential data corruption or system errors. + +Scenario 4: Both Fields Empty Validation + +Details: + TestName: depositWithBothFieldsEmpty + Description: This test verifies the behavior when both deposit_amount and account_id are empty in the request map. + +Execution: + Arrange: Create request map with both deposit_amount and account_id as empty strings, set up mock session. + Act: Execute the deposit method with the request containing both empty fields. + Assert: Verify that BAD_REQUEST status is returned with appropriate error message about both fields being empty. + +Validation: + This test ensures comprehensive input validation coverage, confirming that the method handles the worst-case scenario of missing all required input data. It validates that the error handling logic works correctly for multiple validation failures. + +Scenario 5: Zero Deposit Amount Validation + +Details: + TestName: depositWithZeroAmount + Description: This test validates that deposits with zero amount are rejected with appropriate error messaging. + +Execution: + Arrange: Set up request map with deposit_amount as "0" and valid account_id, mock session with valid user, mock user.getUser_id() to return valid ID. + Act: Call the deposit method with zero deposit amount. + Assert: Verify that the response status is BAD_REQUEST (400) and the response body contains error message about zero deposit amount. + +Validation: + This assertion ensures that business rules preventing meaningless transactions (zero-value deposits) are properly enforced. It validates that the application maintains logical consistency by rejecting transactions that would not change the account state. + +Scenario 6: Positive Deposit Amount Processing + +Details: + TestName: depositWithPositiveAmount + Description: This test verifies that positive deposit amounts are processed correctly, updating account balance and logging the transaction appropriately. + +Execution: + Arrange: Create request map with positive deposit_amount (e.g., "100.50") and valid account_id, mock all repository methods, set up user session with valid user object having parseable user_id. + Act: Invoke the deposit method with valid positive amount. + Assert: Verify OK status, success message in response, correct balance calculation, proper transaction logging, and user accounts retrieval. + +Validation: + This test confirms that the core deposit functionality operates correctly with typical positive amounts. It validates that all business logic components work together harmoniously, including balance updates, transaction logging, and response generation. + +Scenario 7: Large Deposit Amount Handling + +Details: + TestName: depositWithLargeAmount + Description: This test checks the system's ability to handle large deposit amounts without overflow or precision issues. + +Execution: + Arrange: Prepare request map with large deposit_amount (e.g., "999999.99"), valid account_id, mock repositories to handle large numbers, set up valid user session. + Act: Execute the deposit method with the large amount. + Assert: Verify that the transaction processes successfully, balance is calculated correctly without overflow, and all logging occurs properly. + +Validation: + This assertion ensures that the system can handle edge cases involving large monetary amounts. It validates that the double precision arithmetic works correctly and that the application remains stable under extreme but valid input conditions. + +Scenario 8: Decimal Deposit Amount Processing + +Details: + TestName: depositWithDecimalAmount + Description: This test validates that decimal deposit amounts are processed accurately, maintaining proper precision in financial calculations. + +Execution: + Arrange: Set up request map with decimal deposit_amount (e.g., "25.67"), valid account_id, mock repository responses, create valid user session. + Act: Call the deposit method with decimal amount. + Assert: Confirm successful processing, accurate decimal arithmetic in balance calculation, proper transaction logging with correct amount. + +Validation: + This test ensures that the financial calculations maintain appropriate precision for monetary transactions. It validates that decimal handling is accurate, which is crucial for banking applications where precision errors could have serious consequences. + +Scenario 9: Invalid Account ID Format Handling + +Details: + TestName: depositWithInvalidAccountIdFormat + Description: This test verifies the system's behavior when account_id contains non-numeric characters that cannot be parsed to integer. + +Execution: + Arrange: Create request map with valid deposit_amount and account_id containing non-numeric characters (e.g., "abc123"), set up valid user session. + Act: Invoke the deposit method with invalid account ID format. + Assert: Verify that a NumberFormatException is thrown or handled appropriately by the system. + +Validation: + This assertion validates that the method properly handles data type conversion errors. It ensures that the application has appropriate error handling for malformed input data, preventing system crashes and maintaining stability. + +Scenario 10: Invalid Deposit Amount Format Handling + +Details: + TestName: depositWithInvalidAmountFormat + Description: This test checks the method's response when deposit_amount contains non-numeric characters that cannot be parsed to double. + +Execution: + Arrange: Prepare request map with deposit_amount containing invalid characters (e.g., "abc.def") and valid account_id, mock user session. + Act: Execute the deposit method with invalid amount format. + Assert: Verify that NumberFormatException is thrown or handled gracefully by the system. + +Validation: + This test ensures robust error handling for data parsing failures. It validates that the application can gracefully handle malformed monetary input, which is essential for preventing runtime errors and maintaining system reliability. + +Scenario 11: Null User Session Handling + +Details: + TestName: depositWithNullUserSession + Description: This test validates the method's behavior when the user session does not contain a valid user object. + +Execution: + Arrange: Set up request map with valid deposit_amount and account_id, create mock session that returns null for user attribute. + Act: Call the deposit method with null user session. + Assert: Verify that a NullPointerException is thrown when trying to access user.getUser_id(). + +Validation: + This assertion ensures that the method properly handles authentication edge cases. It validates that the application has appropriate session management and that security measures prevent unauthorized access to deposit functionality. + +Scenario 12: Repository Interaction Verification + +Details: + TestName: depositRepositoryInteractionsCorrect + Description: This test verifies that all repository methods are called with correct parameters in the proper sequence during a successful deposit operation. + +Execution: + Arrange: Set up valid request map and user session, mock all repository methods, configure return values for balance retrieval and account listing. + Act: Execute the deposit method with valid parameters. + Assert: Verify that getAccountBalance, changeAccountsBalanceById, logTransaction, and getUserAccountsById are called with correct parameters. + +Validation: + This test validates the integration between the controller and repository layers. It ensures that the business logic correctly orchestrates all necessary data operations and that the repository methods are invoked with appropriate parameters in the correct sequence. + +Scenario 13: Response Structure Validation + +Details: + TestName: depositResponseStructureCorrect + Description: This test verifies that the success response contains all required fields with correct data types and values. + +Execution: + Arrange: Prepare valid request data, mock all dependencies to return expected values, set up complete user session. + Act: Invoke the deposit method with valid parameters. + Assert: Verify that the response contains "message" and "accounts" keys, message contains success text, accounts contains user account data. + +Validation: + This assertion ensures that the API contract is maintained and that client applications receive the expected response structure. It validates that the response format is consistent and contains all necessary information for proper client-side processing. + +Scenario 14: Balance Calculation Accuracy + +Details: + TestName: depositBalanceCalculationAccurate + Description: This test specifically validates that the balance calculation (currentBalance + depositAmount) is performed accurately. + +Execution: + Arrange: Set up known current balance value, specific deposit amount, mock getAccountBalance to return known value, prepare valid request and session. + Act: Execute the deposit method with predetermined values. + Assert: Verify that changeAccountsBalanceById is called with the correctly calculated new balance value. + +Validation: + This test ensures the mathematical accuracy of the core deposit calculation. It validates that the financial arithmetic is correct, which is fundamental to the integrity of banking operations and user trust in the system. + +Scenario 15: Transaction Logging Parameters Verification + +Details: + TestName: depositTransactionLoggingCorrect + Description: This test verifies that transaction logging occurs with all correct parameters including account ID, transaction type, amount, method, status, and timestamp. + +Execution: + Arrange: Set up valid deposit scenario with known parameters, mock transactRepository.logTransaction method. + Act: Call the deposit method with prepared parameters. + Assert: Verify that logTransaction is called with correct account ID, "deposit" type, exact amount, "online" method, "success" status, and appropriate timestamp. + +Validation: + This assertion ensures proper audit trail creation for deposit transactions. It validates that all transaction details are accurately recorded, which is essential for compliance, debugging, and financial reconciliation processes. + + +roost_feedback [22/03/2026, 3:09:17 PM]:Modify\sCode\sto\sfix\sthis\serror\n[222,57]\sno\ssuitable\smethod\sfound\sfor\sthenReturn(java.util.List)\n[300,57]\sno\ssuitable\smethod\sfound\sfor\sthenReturn(java.util.List)\n[323,57]\sno\ssuitable\smethod\sfound\sfor\sthenReturn(java.util.List)\n[345,57]\sno\ssuitable\smethod\sfound\sfor\sthenReturn(java.util.List)\n[411,57]\sno\ssuitable\smethod\sfound\sfor\sthenReturn(java.util.List)\n[431,57]\sno\ssuitable\smethod\sfound\sfor\sthenReturn(java.util.List)\n[454,57]\sno\ssuitable\smethod\sfound\sfor\sthenReturn(java.util.List)\n[470,57]\sno\ssuitable\smethod\sfound\sfor\sthenReturn(java.util.List) +*/ + +// ********RoostGPT******** + +package com.beko.DemoBank_v1.controllers;import com.beko.DemoBank_v1.models.User; +import com.beko.DemoBank_v1.repository.AccountRepository; +import com.beko.DemoBank_v1.repository.TransactRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +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.HttpStatus; +import org.springframework.http.ResponseEntity; +import javax.servlet.http.HttpSession; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import org.junit.jupiter.api.*; +import com.beko.DemoBank_v1.models.PaymentRequest; +import com.beko.DemoBank_v1.models.TransferRequest; +import com.beko.DemoBank_v1.repository.PaymentRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import javax.servlet.http.HttpServletRequest; + +@ExtendWith(MockitoExtension.class) +class TransactControllerDepositTest { + @Mock + private AccountRepository accountRepository; + @Mock + private TransactRepository transactRepository; + @Mock + private HttpSession session; + @Mock + private User user; + @InjectMocks + private TransactController transactController; + private Map requestMap; + private List mockAccounts; + @BeforeEach + void setUp() { + requestMap = new HashMap<>(); + mockAccounts = List.of("Account1", "Account2"); + } + @Test + @Tag("valid") + void depositAmountSuccessfully() { + // Arrange + requestMap.put("deposit_amount", "100.50"); + requestMap.put("account_id", "1"); + + when(session.getAttribute("user")).thenReturn(user); + when(user.getUser_id()).thenReturn("123"); + when(accountRepository.getAccountBalance(123, 1)).thenReturn(500.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(mockAccounts); + // Act + ResponseEntity response = transactController.deposit(requestMap, session); + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + Map responseBody = (Map) response.getBody(); + assertEquals("Amount Deposited Successfully.", responseBody.get("message")); + assertEquals(mockAccounts, responseBody.get("accounts")); + + verify(accountRepository).changeAccountsBalanceById(600.5, 1); + verify(transactRepository).logTransaction(eq(1), eq("deposit"), eq(100.5), + eq("online"), eq("success"), eq("Deposit Transaction Successfull"), any(LocalDateTime.class)); + } + @Test + @Tag("invalid") + void depositWithEmptyAmount() { + // Arrange + requestMap.put("deposit_amount", ""); + requestMap.put("account_id", "1"); + // Act + ResponseEntity response = transactController.deposit(requestMap, session); + // Assert + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("Deposit amount and account ID cannot be empty.", response.getBody()); + verifyNoInteractions(accountRepository, transactRepository); + } + @Test + @Tag("invalid") + void depositWithEmptyAccountId() { + // Arrange + requestMap.put("deposit_amount", "100.00"); + requestMap.put("account_id", ""); + // Act + ResponseEntity response = transactController.deposit(requestMap, session); + // Assert + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("Deposit amount and account ID cannot be empty.", response.getBody()); + verifyNoInteractions(accountRepository, transactRepository); + } + @Test + @Tag("invalid") + void depositWithBothFieldsEmpty() { + // Arrange + requestMap.put("deposit_amount", ""); + requestMap.put("account_id", ""); + // Act + ResponseEntity response = transactController.deposit(requestMap, session); + // Assert + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("Deposit amount and account ID cannot be empty.", response.getBody()); + verifyNoInteractions(accountRepository, transactRepository); + } + @Test + @Tag("boundary") + void depositWithZeroAmount() { + // Arrange + requestMap.put("deposit_amount", "0"); + requestMap.put("account_id", "1"); + + when(session.getAttribute("user")).thenReturn(user); + when(user.getUser_id()).thenReturn("123"); + // Act + ResponseEntity response = transactController.deposit(requestMap, session); + // Assert + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("Deposit amount cannot be zero.", response.getBody()); + verifyNoInteractions(accountRepository, transactRepository); + } + @Test + @Tag("valid") + void depositWithPositiveAmount() { + // Arrange + requestMap.put("deposit_amount", "250.75"); + requestMap.put("account_id", "2"); + + when(session.getAttribute("user")).thenReturn(user); + when(user.getUser_id()).thenReturn("456"); + when(accountRepository.getAccountBalance(456, 2)).thenReturn(1000.0); + when(accountRepository.getUserAccountsById(456)).thenReturn(mockAccounts); + // Act + ResponseEntity response = transactController.deposit(requestMap, session); + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + Map responseBody = (Map) response.getBody(); + assertEquals("Amount Deposited Successfully.", responseBody.get("message")); + assertEquals(mockAccounts, responseBody.get("accounts")); + + verify(accountRepository).changeAccountsBalanceById(1250.75, 2); + verify(transactRepository).logTransaction(eq(2), eq("deposit"), eq(250.75), + eq("online"), eq("success"), eq("Deposit Transaction Successfull"), any(LocalDateTime.class)); + } + @Test + @Tag("boundary") + void depositWithLargeAmount() { + // Arrange + requestMap.put("deposit_amount", "999999.99"); + requestMap.put("account_id", "3"); + + when(session.getAttribute("user")).thenReturn(user); + when(user.getUser_id()).thenReturn("789"); + when(accountRepository.getAccountBalance(789, 3)).thenReturn(0.01); + when(accountRepository.getUserAccountsById(789)).thenReturn(mockAccounts); + // Act + ResponseEntity response = transactController.deposit(requestMap, session); + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + Map responseBody = (Map) response.getBody(); + assertEquals("Amount Deposited Successfully.", responseBody.get("message")); + + verify(accountRepository).changeAccountsBalanceById(1000000.0, 3); + verify(transactRepository).logTransaction(eq(3), eq("deposit"), eq(999999.99), + eq("online"), eq("success"), eq("Deposit Transaction Successfull"), any(LocalDateTime.class)); + } + @Test + @Tag("valid") + void depositWithDecimalAmount() { + // Arrange + requestMap.put("deposit_amount", "25.67"); + requestMap.put("account_id", "4"); + + when(session.getAttribute("user")).thenReturn(user); + when(user.getUser_id()).thenReturn("101"); + when(accountRepository.getAccountBalance(101, 4)).thenReturn(74.33); + when(accountRepository.getUserAccountsById(101)).thenReturn(mockAccounts); + // Act + ResponseEntity response = transactController.deposit(requestMap, session); + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + verify(accountRepository).changeAccountsBalanceById(100.0, 4); + verify(transactRepository).logTransaction(eq(4), eq("deposit"), eq(25.67), + eq("online"), eq("success"), eq("Deposit Transaction Successfull"), any(LocalDateTime.class)); + } + @Test + @Tag("invalid") + void depositWithInvalidAccountIdFormat() { + // Arrange + requestMap.put("deposit_amount", "100.00"); + requestMap.put("account_id", "abc123"); + + when(session.getAttribute("user")).thenReturn(user); + when(user.getUser_id()).thenReturn("123"); + // Act & Assert + assertThrows(NumberFormatException.class, () -> { + transactController.deposit(requestMap, session); + }); + + verifyNoInteractions(accountRepository, transactRepository); + } + @Test + @Tag("invalid") + void depositWithInvalidAmountFormat() { + // Arrange + requestMap.put("deposit_amount", "abc.def"); + requestMap.put("account_id", "1"); + + when(session.getAttribute("user")).thenReturn(user); + when(user.getUser_id()).thenReturn("123"); + // Act & Assert + assertThrows(NumberFormatException.class, () -> { + transactController.deposit(requestMap, session); + }); + + verifyNoInteractions(accountRepository, transactRepository); + } + @Test + @Tag("invalid") + void depositWithNullUserSession() { + // Arrange + requestMap.put("deposit_amount", "100.00"); + requestMap.put("account_id", "1"); + + when(session.getAttribute("user")).thenReturn(null); + // Act & Assert + assertThrows(NullPointerException.class, () -> { + transactController.deposit(requestMap, session); + }); + + verifyNoInteractions(accountRepository, transactRepository); + } + @Test + @Tag("integration") + void depositRepositoryInteractionsCorrect() { + // Arrange + requestMap.put("deposit_amount", "150.25"); + requestMap.put("account_id", "5"); + + when(session.getAttribute("user")).thenReturn(user); + when(user.getUser_id()).thenReturn("202"); + when(accountRepository.getAccountBalance(202, 5)).thenReturn(300.75); + when(accountRepository.getUserAccountsById(202)).thenReturn(mockAccounts); + // Act + transactController.deposit(requestMap, session); + // Assert + verify(accountRepository).getAccountBalance(202, 5); + verify(accountRepository).changeAccountsBalanceById(451.0, 5); + verify(accountRepository).getUserAccountsById(202); + verify(transactRepository).logTransaction(eq(5), eq("deposit"), eq(150.25), + eq("online"), eq("success"), eq("Deposit Transaction Successfull"), any(LocalDateTime.class)); + } + @Test + @Tag("valid") + void depositResponseStructureCorrect() { + // Arrange + requestMap.put("deposit_amount", "75.50"); + requestMap.put("account_id", "6"); + + when(session.getAttribute("user")).thenReturn(user); + when(user.getUser_id()).thenReturn("303"); + when(accountRepository.getAccountBalance(303, 6)).thenReturn(200.0); + when(accountRepository.getUserAccountsById(303)).thenReturn(mockAccounts); + // Act + ResponseEntity response = transactController.deposit(requestMap, session); + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getBody()); + + Map responseBody = (Map) response.getBody(); + assertTrue(responseBody.containsKey("message")); + assertTrue(responseBody.containsKey("accounts")); + assertEquals("Amount Deposited Successfully.", responseBody.get("message")); + assertEquals(mockAccounts, responseBody.get("accounts")); + } + @Test + @Tag("valid") + void depositBalanceCalculationAccurate() { + // Arrange + requestMap.put("deposit_amount", "123.45"); + requestMap.put("account_id", "7"); + + when(session.getAttribute("user")).thenReturn(user); + when(user.getUser_id()).thenReturn("404"); + when(accountRepository.getAccountBalance(404, 7)).thenReturn(876.55); + when(accountRepository.getUserAccountsById(404)).thenReturn(mockAccounts); + // Act + transactController.deposit(requestMap, session); + // Assert + verify(accountRepository).changeAccountsBalanceById(1000.0, 7); + } + @Test + @Tag("integration") + void depositTransactionLoggingCorrect() { + // Arrange + requestMap.put("deposit_amount", "50.00"); + requestMap.put("account_id", "8"); + + when(session.getAttribute("user")).thenReturn(user); + when(user.getUser_id()).thenReturn("505"); + when(accountRepository.getAccountBalance(505, 8)).thenReturn(950.0); + when(accountRepository.getUserAccountsById(505)).thenReturn(mockAccounts); + // Act + transactController.deposit(requestMap, session); + // Assert + verify(transactRepository).logTransaction( + eq(8), + eq("deposit"), + eq(50.0), + eq("online"), + eq("success"), + eq("Deposit Transaction Successfull"), + any(LocalDateTime.class) + ); + } +} diff --git a/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransfer211Test.java b/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransfer211Test.java new file mode 100644 index 0000000..92b105e --- /dev/null +++ b/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransfer211Test.java @@ -0,0 +1,553 @@ + +// ********RoostGPT******** +/* +Test generated by RoostGPT for test java-demo using AI Type AWS Bedrock Runtime AI and AI Model global.anthropic.claude-sonnet-4-20250514-v1:0 + +ROOST_METHOD_HASH=transfer_05410c1574 +ROOST_METHOD_SIG_HASH=transfer_d754a3fac1 + +Scenario 1: Successful Withdrawal with Valid Data + +Details: + TestName: successfulWithdrawalWithValidData + Description: This test verifies that a withdrawal transaction completes successfully when all required fields are provided with valid values and the account has sufficient funds. + +Execution: + Arrange: Set up a mock HttpSession with a valid User object, mock AccountRepository and TransactRepository. Create a request map with valid withdrawal_amount and account_id. Mock the account balance to be greater than the withdrawal amount. + Act: Call the transfer method with the prepared request map and session. + Assert: Verify that the response status is OK (200), the response body contains success message "Withdrawal Successfull!", and the accounts list is included in the response. + +Validation: + This assertion verifies that the withdrawal process works correctly when all conditions are met. It ensures that the business logic for successful withdrawals functions as expected and returns appropriate success indicators to the client. + +Scenario 2: Empty Withdrawal Amount Field + +Details: + TestName: emptyWithdrawalAmountField + Description: This test checks that the system properly handles cases where the withdrawal_amount field is empty or null in the request. + +Execution: + Arrange: Set up a mock HttpSession and create a request map with an empty withdrawal_amount field and a valid account_id. + Act: Call the transfer method with the request map containing empty withdrawal amount. + Assert: Verify that the response status is BAD_REQUEST (400) and the response body contains the error message "Account withdrawing from and withdrawal amount cannot be empty!". + +Validation: + This assertion ensures that input validation works correctly and prevents processing of incomplete requests. It's crucial for maintaining data integrity and providing clear feedback to users about missing required information. + +Scenario 3: Empty Account ID Field + +Details: + TestName: emptyAccountIdField + Description: This test verifies that the system properly validates the account_id field and rejects requests when this required field is empty. + +Execution: + Arrange: Set up a mock HttpSession and create a request map with a valid withdrawal_amount but an empty account_id field. + Act: Call the transfer method with the request map containing empty account ID. + Assert: Verify that the response status is BAD_REQUEST (400) and the response body contains the error message "Account withdrawing from and withdrawal amount cannot be empty!". + +Validation: + This test ensures that the account identification is mandatory for withdrawal operations. It prevents unauthorized or unidentified account access and maintains the security of the transaction system. + +Scenario 4: Both Fields Empty + +Details: + TestName: bothFieldsEmpty + Description: This test checks the system behavior when both withdrawal_amount and account_id fields are empty in the request. + +Execution: + Arrange: Set up a mock HttpSession and create a request map with both withdrawal_amount and account_id fields empty or null. + Act: Call the transfer method with the request map containing both empty fields. + Assert: Verify that the response status is BAD_REQUEST (400) and the response body contains the error message "Account withdrawing from and withdrawal amount cannot be empty!". + +Validation: + This assertion confirms that the validation logic catches multiple missing fields simultaneously. It ensures comprehensive input validation and prevents processing of completely invalid requests. + +Scenario 5: Zero Withdrawal Amount + +Details: + TestName: zeroWithdrawalAmount + Description: This test verifies that the system rejects withdrawal attempts with zero amount, as zero-value transactions are not meaningful business operations. + +Execution: + Arrange: Set up a mock HttpSession with a valid User object and create a request map with withdrawal_amount as "0" and a valid account_id. + Act: Call the transfer method with the zero withdrawal amount. + Assert: Verify that the response status is BAD_REQUEST (400) and the response body contains the error message "Withdrawal amount cannot be 0 value.". + +Validation: + This test ensures that business rules regarding minimum transaction amounts are enforced. Zero-value withdrawals are meaningless and could potentially be used to exploit the system or create unnecessary transaction logs. + +Scenario 6: Insufficient Funds + +Details: + TestName: insufficientFunds + Description: This test checks that the system properly handles withdrawal attempts when the account balance is less than the requested withdrawal amount. + +Execution: + Arrange: Set up a mock HttpSession with a valid User object, mock repositories, and create a request map with valid data. Mock the account balance to be less than the withdrawal amount. Set up expectations for transaction logging. + Act: Call the transfer method with the insufficient balance scenario. + Assert: Verify that the response status is BAD_REQUEST (400), the response body contains "You have insufficient Funds to perform this transfer.", and verify that a failed transaction is logged. + +Validation: + This assertion ensures that the system maintains financial integrity by preventing overdrafts. It also verifies that failed transactions are properly logged for audit purposes, which is crucial for financial compliance and debugging. + +Scenario 7: Negative Withdrawal Amount + +Details: + TestName: negativeWithdrawalAmount + Description: This test verifies the system behavior when a negative withdrawal amount is provided, which should be handled appropriately. + +Execution: + Arrange: Set up a mock HttpSession with a valid User object and create a request map with a negative withdrawal_amount value and valid account_id. + Act: Call the transfer method with the negative withdrawal amount. + Assert: Verify the appropriate response based on how the system should handle negative values (either as invalid input or by treating absolute value). + +Validation: + This test ensures that the system has proper handling for edge cases involving negative monetary values. It prevents potential security vulnerabilities or unexpected behavior from malformed input data. + +Scenario 8: Invalid Account ID Format + +Details: + TestName: invalidAccountIdFormat + Description: This test checks the system behavior when the account_id field contains non-numeric data that cannot be parsed as an integer. + +Execution: + Arrange: Set up a mock HttpSession and create a request map with valid withdrawal_amount but account_id containing non-numeric characters (e.g., "abc123"). + Act: Call the transfer method with the invalid account ID format. + Assert: Verify that the system handles the NumberFormatException appropriately, either by returning a proper error response or by internal exception handling. + +Validation: + This assertion ensures that the system gracefully handles malformed input data and prevents runtime exceptions from crashing the application. Proper error handling for data type conversion is essential for application stability. + +Scenario 9: Invalid Withdrawal Amount Format + +Details: + TestName: invalidWithdrawalAmountFormat + Description: This test verifies the system behavior when the withdrawal_amount field contains non-numeric data that cannot be parsed as a double. + +Execution: + Arrange: Set up a mock HttpSession and create a request map with valid account_id but withdrawal_amount containing non-numeric characters (e.g., "invalid_amount"). + Act: Call the transfer method with the invalid withdrawal amount format. + Assert: Verify that the system handles the NumberFormatException appropriately and returns a meaningful error response. + +Validation: + This test ensures robust input validation and prevents application crashes due to invalid data type conversion. It's crucial for maintaining system stability when dealing with user input that may not conform to expected formats. + +Scenario 10: Null Session Object + +Details: + TestName: nullSessionObject + Description: This test checks the system behavior when the HttpSession parameter is null, which could occur in certain edge cases or security contexts. + +Execution: + Arrange: Create a valid request map with proper withdrawal_amount and account_id, but pass null as the HttpSession parameter. + Act: Call the transfer method with the null session. + Assert: Verify that the system handles the null session gracefully, either by returning an appropriate error response or handling the NullPointerException. + +Validation: + This assertion ensures that the system maintains security by properly handling authentication edge cases. It prevents unauthorized access and ensures that session management is robust against various failure scenarios. + +Scenario 11: User Not Found in Session + +Details: + TestName: userNotFoundInSession + Description: This test verifies the system behavior when the session exists but does not contain a valid User object in the "user" attribute. + +Execution: + Arrange: Set up a mock HttpSession that returns null when getAttribute("user") is called. Create a valid request map with proper withdrawal_amount and account_id. + Act: Call the transfer method with the session containing no user information. + Assert: Verify that the system handles the missing user gracefully, either by returning an authentication error or handling the NullPointerException appropriately. + +Validation: + This test ensures that user authentication is properly validated before processing financial transactions. It's critical for preventing unauthorized access to account operations and maintaining security standards. + +Scenario 12: Exact Balance Withdrawal + +Details: + TestName: exactBalanceWithdrawal + Description: This test verifies that the system correctly handles withdrawals where the withdrawal amount exactly matches the current account balance. + +Execution: + Arrange: Set up a mock HttpSession with a valid User object, mock repositories, and create a request map with valid data. Mock the account balance to exactly equal the withdrawal amount. + Act: Call the transfer method with the exact balance withdrawal scenario. + Assert: Verify that the response status is OK (200), the transaction completes successfully, the new balance is zero, and a successful transaction is logged. + +Validation: + This assertion ensures that edge cases involving exact balance matches are handled correctly. It verifies that the system can process withdrawals that would result in zero balance, which is a common banking scenario. + +Scenario 13: Very Large Withdrawal Amount + +Details: + TestName: veryLargeWithdrawalAmount + Description: This test checks the system behavior when processing a withdrawal with an extremely large amount that could potentially cause overflow or precision issues. + +Execution: + Arrange: Set up a mock HttpSession with a valid User object and create a request map with a very large withdrawal_amount (e.g., Double.MAX_VALUE) and valid account_id. Mock sufficient account balance. + Act: Call the transfer method with the very large withdrawal amount. + Assert: Verify that the system handles large numbers appropriately without overflow errors and processes the transaction correctly or returns appropriate limits-based error messages. + +Validation: + This test ensures that the system can handle edge cases involving extremely large monetary values without losing precision or causing arithmetic errors. It's important for preventing system failures in unusual but possible scenarios. + +Scenario 14: Successful Transaction Logging Verification + +Details: + TestName: successfulTransactionLoggingVerification + Description: This test specifically verifies that successful withdrawal transactions are properly logged in the transaction repository with correct details. + +Execution: + Arrange: Set up all necessary mocks including a valid session, sufficient account balance, and spy on the transactRepository to verify method calls. + Act: Call the transfer method with valid data that should result in a successful withdrawal. + Assert: Verify that transactRepository.logTransaction is called with correct parameters: account_id, "Withdrawal", withdrawal_amount, "online", "success", "Withdrawal Transaction Successfull", and currentDateTime. + +Validation: + This assertion ensures that audit trails are properly maintained for successful transactions. Transaction logging is crucial for financial compliance, debugging, and providing users with transaction history. + +Scenario 15: Account Balance Update Verification + +Details: + TestName: accountBalanceUpdateVerification + Description: This test verifies that the account balance is correctly updated in the repository after a successful withdrawal transaction. + +Execution: + Arrange: Set up all necessary mocks with valid session, sufficient account balance, and spy on the accountRepository to verify method calls. + Act: Call the transfer method with valid withdrawal data. + Assert: Verify that accountRepository.changeAccountsBalanceById is called with the correct new balance (currentBalance - withdrawal_amount) and the correct account_id. + +Validation: + This test ensures that the core business logic of updating account balances works correctly. Accurate balance management is fundamental to any financial system and must be thoroughly tested to prevent monetary discrepancies. + +*/ + +// ********RoostGPT******** +package com.beko.DemoBank_v1.controllers; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import com.beko.DemoBank_v1.models.User; +import com.beko.DemoBank_v1.repository.AccountRepository; +import com.beko.DemoBank_v1.repository.TransactRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Tag; +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.HttpStatus; +import org.springframework.http.ResponseEntity; +import javax.servlet.http.HttpSession; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.List; +import java.util.Arrays; +import org.junit.jupiter.api.*; +import com.beko.DemoBank_v1.models.PaymentRequest; +import com.beko.DemoBank_v1.models.TransferRequest; +import com.beko.DemoBank_v1.repository.PaymentRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import javax.servlet.http.HttpServletRequest; + +@ExtendWith(MockitoExtension.class) +public class TransactControllerTransfer211Test { + + @Mock + private AccountRepository accountRepository; + + @Mock + private TransactRepository transactRepository; + + @Mock + private HttpSession session; + + @InjectMocks + private TransactController transactController; + + private User testUser; + + private Map requestMap; + + @BeforeEach + void setUp() { + testUser = new User(); + testUser.setUser_id("123"); + requestMap = new HashMap<>(); + } + + @Test + @Tag("valid") + void successfulWithdrawalWithValidData() { + // Arrange + requestMap.put("withdrawal_amount", "100.0"); + requestMap.put("account_id", "456"); + + when(session.getAttribute("user")).thenReturn(testUser); + when(accountRepository.getAccountBalance(123, 456)).thenReturn(500.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(Arrays.asList()); + + // Act + ResponseEntity response = transactController.transfer(requestMap, session); + + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + Map responseBody = (Map) response.getBody(); + assertEquals("Withdrawal Successfull!", responseBody.get("message")); + assertNotNull(responseBody.get("accounts")); + + verify(accountRepository).changeAccountsBalanceById(400.0, 456); + verify(transactRepository).logTransaction(eq(456), eq("Withdrawal"), eq(100.0), eq("online"), eq("success"), + eq("Withdrawal Transaction Successfull"), any(LocalDateTime.class)); + } + + @Test + @Tag("invalid") + void emptyWithdrawalAmountField() { + // Arrange + requestMap.put("withdrawal_amount", ""); + requestMap.put("account_id", "456"); + + // Act + ResponseEntity response = transactController.transfer(requestMap, session); + + // Assert + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("Account withdrawing from and withdrawal amount cannot be empty!", response.getBody()); + } + + @Test + @Tag("invalid") + void emptyAccountIdField() { + // Arrange + requestMap.put("withdrawal_amount", "100.0"); + requestMap.put("account_id", ""); + + // Act + ResponseEntity response = transactController.transfer(requestMap, session); + + // Assert + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("Account withdrawing from and withdrawal amount cannot be empty!", response.getBody()); + } + + @Test + @Tag("invalid") + void bothFieldsEmpty() { + // Arrange + requestMap.put("withdrawal_amount", ""); + requestMap.put("account_id", ""); + + // Act + ResponseEntity response = transactController.transfer(requestMap, session); + + // Assert + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("Account withdrawing from and withdrawal amount cannot be empty!", response.getBody()); + } + + @Test + @Tag("boundary") + void zeroWithdrawalAmount() { + // Arrange + requestMap.put("withdrawal_amount", "0"); + requestMap.put("account_id", "456"); + + when(session.getAttribute("user")).thenReturn(testUser); + + // Act + ResponseEntity response = transactController.transfer(requestMap, session); + + // Assert + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("Withdrawal amount cannot be 0 value.", response.getBody()); + } + + @Test + @Tag("invalid") + void insufficientFunds() { + // Arrange + requestMap.put("withdrawal_amount", "600.0"); + requestMap.put("account_id", "456"); + + when(session.getAttribute("user")).thenReturn(testUser); + when(accountRepository.getAccountBalance(123, 456)).thenReturn(500.0); + + // Act + ResponseEntity response = transactController.transfer(requestMap, session); + + // Assert + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("You have insufficient Funds to perform this transfer.", response.getBody()); + + verify(transactRepository).logTransaction(eq(456), eq("withdrawal"), eq(600.0), eq("online"), eq("failed"), + eq("Insufficient funds."), any(LocalDateTime.class)); + } + + @Test + @Tag("invalid") + void negativeWithdrawalAmount() { + // Arrange + requestMap.put("withdrawal_amount", "-100.0"); + requestMap.put("account_id", "456"); + + when(session.getAttribute("user")).thenReturn(testUser); + when(accountRepository.getAccountBalance(123, 456)).thenReturn(500.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(Arrays.asList()); + + // Act + ResponseEntity response = transactController.transfer(requestMap, session); + + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + Map responseBody = (Map) response.getBody(); + assertEquals("Withdrawal Successfull!", responseBody.get("message")); + + verify(accountRepository).changeAccountsBalanceById(600.0, 456); + } + + @Test + @Tag("invalid") + void invalidAccountIdFormat() { + // Arrange + requestMap.put("withdrawal_amount", "100.0"); + requestMap.put("account_id", "abc123"); + + // Act & Assert + assertThrows(NumberFormatException.class, () -> { + transactController.transfer(requestMap, session); + }); + } + + @Test + @Tag("invalid") + void invalidWithdrawalAmountFormat() { + // Arrange + requestMap.put("withdrawal_amount", "invalid_amount"); + requestMap.put("account_id", "456"); + + when(session.getAttribute("user")).thenReturn(testUser); + + // Act & Assert + assertThrows(NumberFormatException.class, () -> { + transactController.transfer(requestMap, session); + }); + } + + @Test + @Tag("invalid") + void nullSessionObject() { + // Arrange + requestMap.put("withdrawal_amount", "100.0"); + requestMap.put("account_id", "456"); + + // Act & Assert + assertThrows(NullPointerException.class, () -> { + transactController.transfer(requestMap, null); + }); + } + + @Test + @Tag("invalid") + void userNotFoundInSession() { + // Arrange + requestMap.put("withdrawal_amount", "100.0"); + requestMap.put("account_id", "456"); + + when(session.getAttribute("user")).thenReturn(null); + + // Act & Assert + assertThrows(NullPointerException.class, () -> { + transactController.transfer(requestMap, session); + }); + } + + @Test + @Tag("boundary") + void exactBalanceWithdrawal() { + // Arrange + requestMap.put("withdrawal_amount", "500.0"); + requestMap.put("account_id", "456"); + + when(session.getAttribute("user")).thenReturn(testUser); + when(accountRepository.getAccountBalance(123, 456)).thenReturn(500.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(Arrays.asList()); + + // Act + ResponseEntity response = transactController.transfer(requestMap, session); + + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + Map responseBody = (Map) response.getBody(); + assertEquals("Withdrawal Successfull!", responseBody.get("message")); + + verify(accountRepository).changeAccountsBalanceById(0.0, 456); + verify(transactRepository).logTransaction(eq(456), eq("Withdrawal"), eq(500.0), eq("online"), eq("success"), + eq("Withdrawal Transaction Successfull"), any(LocalDateTime.class)); + } + + @Test + @Tag("boundary") + void veryLargeWithdrawalAmount() { + // Arrange + double largeAmount = 999999999.99; + requestMap.put("withdrawal_amount", String.valueOf(largeAmount)); + requestMap.put("account_id", "456"); + + when(session.getAttribute("user")).thenReturn(testUser); + when(accountRepository.getAccountBalance(123, 456)).thenReturn(Double.MAX_VALUE); + when(accountRepository.getUserAccountsById(123)).thenReturn(Arrays.asList()); + + // Act + ResponseEntity response = transactController.transfer(requestMap, session); + + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + Map responseBody = (Map) response.getBody(); + assertEquals("Withdrawal Successfull!", responseBody.get("message")); + + verify(accountRepository).changeAccountsBalanceById(eq(Double.MAX_VALUE - largeAmount), eq(456)); + } + + @Test + @Tag("integration") + void successfulTransactionLoggingVerification() { + // Arrange + requestMap.put("withdrawal_amount", "250.0"); + requestMap.put("account_id", "789"); + + when(session.getAttribute("user")).thenReturn(testUser); + when(accountRepository.getAccountBalance(123, 789)).thenReturn(1000.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(Arrays.asList()); + + // Act + transactController.transfer(requestMap, session); + + // Assert + verify(transactRepository).logTransaction(eq(789), eq("Withdrawal"), eq(250.0), eq("online"), eq("success"), + eq("Withdrawal Transaction Successfull"), any(LocalDateTime.class)); + } + + @Test + @Tag("integration") + void accountBalanceUpdateVerification() { + // Arrange + requestMap.put("withdrawal_amount", "150.0"); + requestMap.put("account_id", "321"); + + when(session.getAttribute("user")).thenReturn(testUser); + when(accountRepository.getAccountBalance(123, 321)).thenReturn(800.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(Arrays.asList()); + + // Act + transactController.transfer(requestMap, session); + + // Assert + verify(accountRepository).changeAccountsBalanceById(650.0, 321); + verify(accountRepository).getUserAccountsById(123); + } + +} \ No newline at end of file diff --git a/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransfer569Test.java b/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransfer569Test.java new file mode 100644 index 0000000..0aea5a4 --- /dev/null +++ b/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransfer569Test.java @@ -0,0 +1,589 @@ + +// ********RoostGPT******** +/* +Test generated by RoostGPT for test java-demo using AI Type AWS Bedrock Runtime AI and AI Model global.anthropic.claude-sonnet-4-20250514-v1:0 + +ROOST_METHOD_HASH=transfer_1f3029f5d2 +ROOST_METHOD_SIG_HASH=transfer_89d8d3e845 + +Scenario 1: Successful Payment Processing with All Valid Fields + +Details: + TestName: successfulPaymentProcessingWithAllValidFields + Description: This test verifies that a payment is processed successfully when all required fields are provided with valid values and the account has sufficient balance. The test ensures the payment flow works correctly under normal conditions. +Execution: + Arrange: Set up a mock PaymentRequest with valid beneficiary, account_number, account_id, reference, and payment_amount. Mock HttpSession with a valid User object. Mock accountRepository to return sufficient balance. Mock paymentRepository and transactRepository for successful operations. + Act: Call the transfer method with the prepared PaymentRequest and HttpSession. + Assert: Verify that ResponseEntity returns HTTP 200 OK status with success message and updated accounts information. +Validation: + This assertion verifies that the payment processing completes successfully when all conditions are met. It's crucial for ensuring the core functionality works as expected and that successful payments are properly logged and processed. + +Scenario 2: Payment Rejection Due to Empty Beneficiary Field + +Details: + TestName: paymentRejectionDueToEmptyBeneficiaryField + Description: This test validates that the system properly rejects payment requests when the beneficiary field is empty or null, returning an appropriate error message to prevent incomplete payment processing. +Execution: + Arrange: Create a PaymentRequest object with empty beneficiary field but valid values for other required fields. Set up mock HttpSession with valid user data. + Act: Invoke the transfer method with the PaymentRequest containing empty beneficiary. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request with the specific error message about empty required fields. +Validation: + This assertion ensures input validation works correctly by preventing payments with missing beneficiary information. This is critical for maintaining data integrity and preventing invalid payment records. + +Scenario 3: Payment Rejection Due to Empty Account Number Field + +Details: + TestName: paymentRejectionDueToEmptyAccountNumberField + Description: This test ensures the system correctly validates and rejects payment requests when the account_number field is empty, maintaining data consistency and preventing incomplete transactions. +Execution: + Arrange: Set up a PaymentRequest with empty account_number field while keeping other required fields valid. Mock HttpSession with proper user authentication. + Act: Call the transfer method with the PaymentRequest containing empty account_number. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request with the error message about required fields being empty. +Validation: + This test confirms that account number validation prevents incomplete payment data from being processed. It's essential for maintaining transaction integrity and ensuring all payment records have complete recipient information. + +Scenario 4: Payment Rejection Due to Empty Account ID Field + +Details: + TestName: paymentRejectionDueToEmptyAccountIdField + Description: This test verifies that the payment system properly validates the account_id field and rejects requests where this critical identifier is missing or empty. +Execution: + Arrange: Create a PaymentRequest object with empty account_id field but populate other required fields with valid data. Set up mock HttpSession with authenticated user. + Act: Execute the transfer method with the PaymentRequest lacking account_id. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request status with the appropriate validation error message. +Validation: + This assertion validates that the system prevents payments without proper account identification. This is crucial for security and ensures payments can be properly attributed to the correct source account. + +Scenario 5: Payment Rejection Due to Empty Payment Amount Field + +Details: + TestName: paymentRejectionDueToEmptyPaymentAmountField + Description: This test confirms that the system validates payment amount field and rejects transactions when this essential field is empty or missing. +Execution: + Arrange: Set up a PaymentRequest with empty payment_amount field while maintaining valid values for other required fields. Mock HttpSession with valid user session. + Act: Invoke the transfer method with the PaymentRequest missing payment_amount. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request with the validation error message. +Validation: + This test ensures that payment amount validation prevents transactions without specified amounts. This is fundamental for transaction integrity and prevents undefined or null amount payments from being processed. + +Scenario 6: Payment Rejection Due to Zero Payment Amount + +Details: + TestName: paymentRejectionDueToZeroPaymentAmount + Description: This test validates that the system correctly identifies and rejects payment requests with zero amount, preventing meaningless transactions from being processed. +Execution: + Arrange: Create a PaymentRequest with payment_amount set to "0" while keeping other fields valid. Set up mock HttpSession and user authentication properly. + Act: Call the transfer method with the PaymentRequest containing zero payment amount. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request with the specific error message "Payment amount cannot be 0." +Validation: + This assertion confirms that zero-amount payment validation works correctly. This prevents system resources from being wasted on meaningless transactions and maintains logical transaction processing. + +Scenario 7: Payment Rejection Due to Insufficient Account Balance + +Details: + TestName: paymentRejectionDueToInsufficientAccountBalance + Description: This test verifies that the system properly handles insufficient balance scenarios by rejecting the payment request and logging the failed transaction appropriately. +Execution: + Arrange: Set up a PaymentRequest with valid fields and payment amount greater than available balance. Mock accountRepository to return insufficient balance. Mock paymentRepository and transactRepository for failed transaction logging. + Act: Execute the transfer method with the PaymentRequest exceeding available balance. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request with insufficient funds error message. +Validation: + This test ensures that balance validation prevents overdraft situations and properly logs failed transactions. It's critical for financial integrity and maintaining accurate transaction records. + +Scenario 8: Successful Payment with Reference Field + +Details: + TestName: successfulPaymentWithReferenceField + Description: This test confirms that payments process correctly when an optional reference field is provided, ensuring the reference information is properly handled and stored. +Execution: + Arrange: Create a PaymentRequest with all required fields valid and include a reference value. Mock HttpSession with authenticated user. Set up repository mocks for successful transaction processing. + Act: Invoke the transfer method with the complete PaymentRequest including reference. + Assert: Verify that ResponseEntity returns HTTP 200 OK with success message and that payment processing includes the reference information. +Validation: + This assertion validates that optional reference field handling works correctly and doesn't interfere with successful payment processing. This ensures additional payment metadata is properly managed. + +Scenario 9: Payment Processing with Exact Balance Match + +Details: + TestName: paymentProcessingWithExactBalanceMatch + Description: This test verifies that payments process successfully when the payment amount exactly matches the available account balance, testing the boundary condition. +Execution: + Arrange: Set up a PaymentRequest with payment amount exactly equal to the current account balance. Mock accountRepository to return matching balance. Configure other mocks for successful processing. + Act: Call the transfer method with the PaymentRequest matching exact balance. + Assert: Verify that ResponseEntity returns HTTP 200 OK and that the account balance becomes zero after processing. +Validation: + This test ensures that boundary conditions are handled correctly and that payments can process when they consume the entire available balance. This validates the balance comparison logic accuracy. + +Scenario 10: Payment Processing Updates Account Balance Correctly + +Details: + TestName: paymentProcessingUpdatesAccountBalanceCorrectly + Description: This test confirms that successful payment processing correctly calculates and updates the account balance by subtracting the payment amount from the current balance. +Execution: + Arrange: Create a PaymentRequest with valid fields and a specific payment amount. Mock accountRepository with a known current balance and verify balance update calls. Set up other repository mocks. + Act: Execute the transfer method with the prepared PaymentRequest. + Assert: Verify that accountRepository.changeAccountsBalanceById is called with the correctly calculated new balance (currentBalance - paymentAmount). +Validation: + This assertion ensures that balance calculations are performed correctly during payment processing. Accurate balance management is fundamental to financial transaction integrity. + +Scenario 11: Payment Processing Logs Successful Transaction + +Details: + TestName: paymentProcessingLogsSuccessfulTransaction + Description: This test verifies that successful payment processing properly logs the transaction with correct details including account ID, transaction type, amount, and success status. +Execution: + Arrange: Set up a PaymentRequest with valid data for successful processing. Mock all repositories and verify that transactRepository.logTransaction is called with expected parameters. + Act: Invoke the transfer method with the valid PaymentRequest. + Assert: Verify that transactRepository.logTransaction is called with correct parameters for successful payment logging. +Validation: + This test ensures that transaction logging works correctly for successful payments. Proper transaction logging is essential for audit trails and financial record keeping. + +Scenario 12: Payment Processing Logs Failed Transaction for Insufficient Funds + +Details: + TestName: paymentProcessingLogsFailedTransactionForInsufficientFunds + Description: This test confirms that failed payments due to insufficient funds are properly logged with appropriate failure status and reason codes for audit purposes. +Execution: + Arrange: Create a PaymentRequest that exceeds available balance. Mock accountRepository to return insufficient balance. Verify that both paymentRepository and transactRepository log the failure appropriately. + Act: Call the transfer method with the PaymentRequest causing insufficient funds. + Assert: Verify that failed transaction logging occurs with correct failure status and insufficient funds reason. +Validation: + This assertion validates that failed transaction logging works correctly, which is crucial for maintaining complete audit trails and understanding transaction failure patterns. + +Scenario 13: Payment Processing Creates Payment Record Successfully + +Details: + TestName: paymentProcessingCreatesPaymentRecordSuccessfully + Description: This test verifies that successful payments create appropriate payment records through the paymentRepository with all necessary payment details and success status. +Execution: + Arrange: Set up a PaymentRequest with complete valid data. Mock repositories for successful processing and verify paymentRepository.makePayment is called with correct parameters. + Act: Execute the transfer method with the valid PaymentRequest. + Assert: Verify that paymentRepository.makePayment is called with success status and correct payment details. +Validation: + This test ensures that payment record creation works properly for successful transactions. Accurate payment records are essential for financial tracking and reporting. + +Scenario 14: Payment Processing Returns Updated Account Information + +Details: + TestName: paymentProcessingReturnsUpdatedAccountInformation + Description: This test confirms that successful payment processing returns updated account information in the response, allowing clients to refresh their account data immediately. +Execution: + Arrange: Create a PaymentRequest for successful processing. Mock accountRepository to return updated account information through getUserAccountsById method. + Act: Invoke the transfer method with valid PaymentRequest. + Assert: Verify that the response contains updated accounts information retrieved from accountRepository.getUserAccountsById. +Validation: + This assertion ensures that clients receive current account information after payment processing. This improves user experience by providing immediate feedback on account status changes. + +Scenario 15: Payment Processing Handles Number Format Exception for Account ID + +Details: + TestName: paymentProcessingHandlesNumberFormatExceptionForAccountId + Description: This test verifies system behavior when account_id contains non-numeric values that cannot be parsed to integer, ensuring graceful error handling. +Execution: + Arrange: Set up a PaymentRequest with non-numeric account_id value (e.g., "invalid"). Configure other fields with valid values. + Act: Call the transfer method with the PaymentRequest containing invalid account_id format. + Assert: Verify that NumberFormatException is thrown or handled appropriately by the system. +Validation: + This test ensures that input validation handles data type conversion errors gracefully. Proper exception handling prevents system crashes from invalid input data. + +Scenario 16: Payment Processing Handles Number Format Exception for Payment Amount + +Details: + TestName: paymentProcessingHandlesNumberFormatExceptionForPaymentAmount + Description: This test validates system behavior when payment_amount contains non-numeric values, ensuring the application handles parsing errors appropriately. +Execution: + Arrange: Create a PaymentRequest with non-numeric payment_amount value while keeping other fields valid. Set up necessary mocks. + Act: Execute the transfer method with the PaymentRequest containing unparseable payment_amount. + Assert: Verify that NumberFormatException is handled correctly without causing system failure. +Validation: + This assertion confirms that numeric input validation works properly for payment amounts. Robust input handling is essential for preventing application errors from user input mistakes. + +*/ + +// ********RoostGPT******** +package com.beko.DemoBank_v1.controllers; + +import com.beko.DemoBank_v1.models.PaymentRequest; +import com.beko.DemoBank_v1.models.User; +import com.beko.DemoBank_v1.repository.AccountRepository; +import com.beko.DemoBank_v1.repository.PaymentRepository; +import com.beko.DemoBank_v1.repository.TransactRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +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.HttpStatus; +import org.springframework.http.ResponseEntity; +import javax.servlet.http.HttpSession; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import org.junit.jupiter.api.*; +import com.beko.DemoBank_v1.models.TransferRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; + +@ExtendWith(MockitoExtension.class) +class TransactControllerTransfer569Test { + + @Mock + private AccountRepository accountRepository; + + @Mock + private PaymentRepository paymentRepository; + + @Mock + private TransactRepository transactRepository; + + @Mock + private HttpSession session; + + @Mock + private User user; + + @InjectMocks + private TransactController transactController; + + private PaymentRequest paymentRequest; + + @BeforeEach + void setUp() { + paymentRequest = new PaymentRequest(); + when(user.getUser_id()).thenReturn("123"); + when(session.getAttribute("user")).thenReturn(user); + } + + @Test + @Tag("valid") + void successfulPaymentProcessingWithAllValidFields() { + // Arrange + paymentRequest.setBeneficiary("John Doe"); + paymentRequest.setAccount_number("987654321"); + paymentRequest.setAccount_id("1"); + paymentRequest.setReference("Payment for services"); + paymentRequest.setPayment_amount("100.0"); + when(accountRepository.getAccountBalance(123, 1)).thenReturn(500.0); + when(accountRepository.getUserAccountsById(123)).thenReturn((List) List.of("Account1", "Account2")); + // Act + ResponseEntity response = transactController.transfer(paymentRequest, session); + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertTrue(response.getBody() instanceof Map); + Map responseBody = (Map) response.getBody(); + assertEquals("Payment Processed Successfully!", responseBody.get("message")); + assertNotNull(responseBody.get("accounts")); + verify(accountRepository).changeAccountsBalanceById(400.0, 1); + verify(paymentRepository).makePayment(eq(1), eq("John Doe"), eq("987654321"), eq(100.0), + eq("Payment for services"), eq("success"), eq("Payment Processed Successfully!"), + any(LocalDateTime.class)); + verify(transactRepository).logTransaction(eq(1), eq("Payment"), eq(100.0), eq("online"), eq("success"), + eq("Payment Transaction Successfull"), any(LocalDateTime.class)); + } + + @Test + @Tag("invalid") + void paymentRejectionDueToEmptyBeneficiaryField() { + // Arrange + paymentRequest.setBeneficiary(""); + paymentRequest.setAccount_number("987654321"); + paymentRequest.setAccount_id("1"); + paymentRequest.setReference("Payment for services"); + paymentRequest.setPayment_amount("100.0"); + // Act + ResponseEntity response = transactController.transfer(paymentRequest, session); + // Assert + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("Beneficiary, account number, account paying from and account payment amount cannot be empty.", + response.getBody()); + + verify(accountRepository, never()).getAccountBalance(anyInt(), anyInt()); + verify(paymentRepository, never()).makePayment(anyInt(), anyString(), anyString(), anyDouble(), anyString(), + anyString(), anyString(), any(LocalDateTime.class)); + } + + @Test + @Tag("invalid") + void paymentRejectionDueToEmptyAccountNumberField() { + // Arrange + paymentRequest.setBeneficiary("John Doe"); + paymentRequest.setAccount_number(""); + paymentRequest.setAccount_id("1"); + paymentRequest.setReference("Payment for services"); + paymentRequest.setPayment_amount("100.0"); + // Act + ResponseEntity response = transactController.transfer(paymentRequest, session); + // Assert + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("Beneficiary, account number, account paying from and account payment amount cannot be empty.", + response.getBody()); + + verify(accountRepository, never()).getAccountBalance(anyInt(), anyInt()); + verify(paymentRepository, never()).makePayment(anyInt(), anyString(), anyString(), anyDouble(), anyString(), + anyString(), anyString(), any(LocalDateTime.class)); + } + + @Test + @Tag("invalid") + void paymentRejectionDueToEmptyAccountIdField() { + // Arrange + paymentRequest.setBeneficiary("John Doe"); + paymentRequest.setAccount_number("987654321"); + paymentRequest.setAccount_id(""); + paymentRequest.setReference("Payment for services"); + paymentRequest.setPayment_amount("100.0"); + // Act + ResponseEntity response = transactController.transfer(paymentRequest, session); + // Assert + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("Beneficiary, account number, account paying from and account payment amount cannot be empty.", + response.getBody()); + + verify(accountRepository, never()).getAccountBalance(anyInt(), anyInt()); + verify(paymentRepository, never()).makePayment(anyInt(), anyString(), anyString(), anyDouble(), anyString(), + anyString(), anyString(), any(LocalDateTime.class)); + } + + @Test + @Tag("invalid") + void paymentRejectionDueToEmptyPaymentAmountField() { + // Arrange + paymentRequest.setBeneficiary("John Doe"); + paymentRequest.setAccount_number("987654321"); + paymentRequest.setAccount_id("1"); + paymentRequest.setReference("Payment for services"); + paymentRequest.setPayment_amount(""); + // Act + ResponseEntity response = transactController.transfer(paymentRequest, session); + // Assert + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("Beneficiary, account number, account paying from and account payment amount cannot be empty.", + response.getBody()); + + verify(accountRepository, never()).getAccountBalance(anyInt(), anyInt()); + verify(paymentRepository, never()).makePayment(anyInt(), anyString(), anyString(), anyDouble(), anyString(), + anyString(), anyString(), any(LocalDateTime.class)); + } + + @Test + @Tag("boundary") + void paymentRejectionDueToZeroPaymentAmount() { + // Arrange + paymentRequest.setBeneficiary("John Doe"); + paymentRequest.setAccount_number("987654321"); + paymentRequest.setAccount_id("1"); + paymentRequest.setReference("Payment for services"); + paymentRequest.setPayment_amount("0"); + // Act + ResponseEntity response = transactController.transfer(paymentRequest, session); + // Assert + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("Payment amount cannot be 0.", response.getBody()); + + verify(accountRepository, never()).getAccountBalance(anyInt(), anyInt()); + verify(paymentRepository, never()).makePayment(anyInt(), anyString(), anyString(), anyDouble(), anyString(), + anyString(), anyString(), any(LocalDateTime.class)); + } + + @Test + @Tag("invalid") + void paymentRejectionDueToInsufficientAccountBalance() { + // Arrange + paymentRequest.setBeneficiary("John Doe"); + paymentRequest.setAccount_number("987654321"); + paymentRequest.setAccount_id("1"); + paymentRequest.setReference("Payment for services"); + paymentRequest.setPayment_amount("500.0"); + when(accountRepository.getAccountBalance(123, 1)).thenReturn(100.0); + // Act + ResponseEntity response = transactController.transfer(paymentRequest, session); + // Assert + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("You have insufficient Funds to perform this payment.", response.getBody()); + + verify(paymentRepository).makePayment(eq(1), eq("John Doe"), eq("987654321"), eq(500.0), + eq("Payment for services"), eq("failed"), eq("Coult not Processed Payment due to insufficient funds."), + any(LocalDateTime.class)); + verify(transactRepository).logTransaction(eq(1), eq("Payment"), eq(500.0), eq("online"), eq("failed"), + eq("Insufficient funds."), any(LocalDateTime.class)); + verify(accountRepository, never()).changeAccountsBalanceById(anyDouble(), anyInt()); + } + + @Test + @Tag("valid") + void successfulPaymentWithReferenceField() { + // Arrange + paymentRequest.setBeneficiary("Jane Smith"); + paymentRequest.setAccount_number("123456789"); + paymentRequest.setAccount_id("2"); + paymentRequest.setReference("Invoice #12345"); + paymentRequest.setPayment_amount("250.0"); + when(accountRepository.getAccountBalance(123, 2)).thenReturn(1000.0); + when(accountRepository.getUserAccountsById(123)).thenReturn((List) List.of("Account1", "Account2")); + // Act + ResponseEntity response = transactController.transfer(paymentRequest, session); + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertTrue(response.getBody() instanceof Map); + Map responseBody = (Map) response.getBody(); + assertEquals("Payment Processed Successfully!", responseBody.get("message")); + verify(paymentRepository).makePayment(eq(2), eq("Jane Smith"), eq("123456789"), eq(250.0), eq("Invoice #12345"), + eq("success"), eq("Payment Processed Successfully!"), any(LocalDateTime.class)); + } + + @Test + @Tag("boundary") + void paymentProcessingWithExactBalanceMatch() { + // Arrange + paymentRequest.setBeneficiary("John Doe"); + paymentRequest.setAccount_number("987654321"); + paymentRequest.setAccount_id("1"); + paymentRequest.setReference("Payment for services"); + paymentRequest.setPayment_amount("100.0"); + when(accountRepository.getAccountBalance(123, 1)).thenReturn(100.0); + when(accountRepository.getUserAccountsById(123)).thenReturn((List) List.of("Account1")); + // Act + ResponseEntity response = transactController.transfer(paymentRequest, session); + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + verify(accountRepository).changeAccountsBalanceById(0.0, 1); + } + + @Test + @Tag("integration") + void paymentProcessingUpdatesAccountBalanceCorrectly() { + // Arrange + paymentRequest.setBeneficiary("John Doe"); + paymentRequest.setAccount_number("987654321"); + paymentRequest.setAccount_id("1"); + paymentRequest.setReference("Payment for services"); + paymentRequest.setPayment_amount("75.50"); + double currentBalance = 200.0; + double expectedNewBalance = currentBalance - 75.50; + when(accountRepository.getAccountBalance(123, 1)).thenReturn(currentBalance); + when(accountRepository.getUserAccountsById(123)).thenReturn((List) List.of("Account1")); + // Act + transactController.transfer(paymentRequest, session); + // Assert + verify(accountRepository).changeAccountsBalanceById(expectedNewBalance, 1); + } + + @Test + @Tag("integration") + void paymentProcessingLogsSuccessfulTransaction() { + // Arrange + paymentRequest.setBeneficiary("John Doe"); + paymentRequest.setAccount_number("987654321"); + paymentRequest.setAccount_id("1"); + paymentRequest.setReference("Payment for services"); + paymentRequest.setPayment_amount("100.0"); + when(accountRepository.getAccountBalance(123, 1)).thenReturn(500.0); + when(accountRepository.getUserAccountsById(123)).thenReturn((List) List.of("Account1")); + // Act + transactController.transfer(paymentRequest, session); + // Assert + verify(transactRepository).logTransaction(eq(1), eq("Payment"), eq(100.0), eq("online"), eq("success"), + eq("Payment Transaction Successfull"), any(LocalDateTime.class)); + } + + @Test + @Tag("integration") + void paymentProcessingLogsFailedTransactionForInsufficientFunds() { + // Arrange + paymentRequest.setBeneficiary("John Doe"); + paymentRequest.setAccount_number("987654321"); + paymentRequest.setAccount_id("1"); + paymentRequest.setReference("Payment for services"); + paymentRequest.setPayment_amount("500.0"); + when(accountRepository.getAccountBalance(123, 1)).thenReturn(100.0); + // Act + transactController.transfer(paymentRequest, session); + // Assert + verify(transactRepository).logTransaction(eq(1), eq("Payment"), eq(500.0), eq("online"), eq("failed"), + eq("Insufficient funds."), any(LocalDateTime.class)); + verify(paymentRepository).makePayment(eq(1), eq("John Doe"), eq("987654321"), eq(500.0), + eq("Payment for services"), eq("failed"), eq("Coult not Processed Payment due to insufficient funds."), + any(LocalDateTime.class)); + } + + @Test + @Tag("integration") + void paymentProcessingCreatesPaymentRecordSuccessfully() { + // Arrange + paymentRequest.setBeneficiary("John Doe"); + paymentRequest.setAccount_number("987654321"); + paymentRequest.setAccount_id("1"); + paymentRequest.setReference("Payment for services"); + paymentRequest.setPayment_amount("100.0"); + when(accountRepository.getAccountBalance(123, 1)).thenReturn(500.0); + when(accountRepository.getUserAccountsById(123)).thenReturn((List) List.of("Account1")); + // Act + transactController.transfer(paymentRequest, session); + // Assert + verify(paymentRepository).makePayment(eq(1), eq("John Doe"), eq("987654321"), eq(100.0), + eq("Payment for services"), eq("success"), eq("Payment Processed Successfully!"), + any(LocalDateTime.class)); + } + + @Test + @Tag("integration") + void paymentProcessingReturnsUpdatedAccountInformation() { + // Arrange + paymentRequest.setBeneficiary("John Doe"); + paymentRequest.setAccount_number("987654321"); + paymentRequest.setAccount_id("1"); + paymentRequest.setReference("Payment for services"); + paymentRequest.setPayment_amount("100.0"); + List expectedAccounts = List.of("Account1", "Account2", "Account3"); + when(accountRepository.getAccountBalance(123, 1)).thenReturn(500.0); + when(accountRepository.getUserAccountsById(123)).thenReturn((List) expectedAccounts); + // Act + ResponseEntity response = transactController.transfer(paymentRequest, session); + // Assert + assertTrue(response.getBody() instanceof Map); + Map responseBody = (Map) response.getBody(); + assertEquals(expectedAccounts, responseBody.get("accounts")); + verify(accountRepository).getUserAccountsById(123); + } + + @Test + @Tag("invalid") + void paymentProcessingHandlesNumberFormatExceptionForAccountId() { + // Arrange + paymentRequest.setBeneficiary("John Doe"); + paymentRequest.setAccount_number("987654321"); + paymentRequest.setAccount_id("invalid"); + paymentRequest.setReference("Payment for services"); + paymentRequest.setPayment_amount("100.0"); + // Act & Assert + assertThrows(NumberFormatException.class, () -> { + transactController.transfer(paymentRequest, session); + }); + } + + @Test + @Tag("invalid") + void paymentProcessingHandlesNumberFormatExceptionForPaymentAmount() { + // Arrange + paymentRequest.setBeneficiary("John Doe"); + paymentRequest.setAccount_number("987654321"); + paymentRequest.setAccount_id("1"); + paymentRequest.setReference("Payment for services"); + paymentRequest.setPayment_amount("invalid_amount"); + // Act & Assert + assertThrows(NumberFormatException.class, () -> { + transactController.transfer(paymentRequest, session); + }); + } + +} \ No newline at end of file diff --git a/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransferTest.java b/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransferTest.java new file mode 100644 index 0000000..0419780 --- /dev/null +++ b/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransferTest.java @@ -0,0 +1,511 @@ + +// ********RoostGPT******** +/* +Test generated by RoostGPT for test java-demo using AI Type AWS Bedrock Runtime AI and AI Model global.anthropic.claude-sonnet-4-20250514-v1:0 + +ROOST_METHOD_HASH=transfer_a34cb5e196 +ROOST_METHOD_SIG_HASH=transfer_23bf246fa5 + +Scenario 1: Successful transfer between different accounts + +Details: + TestName: successfulTransferBetweenDifferentAccounts + Description: This test verifies that a valid transfer request between two different accounts with sufficient funds completes successfully and returns appropriate response with updated account information. +Execution: + Arrange: Set up a valid TransferRequest with different source and target accounts, mock HttpSession with authenticated user, mock AccountRepository to return sufficient balance for source account, and mock all repository interactions. + Act: Invoke the transfer method with the prepared request and session parameters. + Assert: Verify that ResponseEntity returns HTTP 200 status, response body contains success message "Transfer completed successfully.", and response includes updated accounts information. +Validation: + This assertion verifies that the core functionality of transferring funds between accounts works correctly when all conditions are met. It ensures that the business logic for successful transfers is properly implemented and the response format matches the expected API contract. + +Scenario 2: Transfer request with empty source account field + +Details: + TestName: transferWithEmptySourceAccount + Description: This test checks that the method properly validates input and rejects transfer requests when the source account field is empty or null. +Execution: + Arrange: Create a TransferRequest object with empty string for source account field, valid target account and amount fields, and mock HttpSession. + Act: Call the transfer method with the request containing empty source account. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request status and response body contains the error message "The account transferring from and to along with the amount cannot be empty!". +Validation: + This assertion ensures that input validation works correctly for the source account field. It prevents processing of incomplete transfer requests and provides clear error messaging to the client, which is crucial for API usability and data integrity. + +Scenario 3: Transfer request with empty target account field + +Details: + TestName: transferWithEmptyTargetAccount + Description: This test validates that the method correctly handles and rejects transfer requests when the target account field is empty. +Execution: + Arrange: Set up a TransferRequest with valid source account and amount but empty target account field, and prepare mock HttpSession. + Act: Execute the transfer method with the request containing empty target account. + Assert: Check that ResponseEntity returns HTTP 400 status and the response body contains the validation error message about empty fields. +Validation: + This test confirms that all required fields are properly validated before processing. It ensures that incomplete requests are caught early in the validation process, preventing potential data corruption or system errors downstream. + +Scenario 4: Transfer request with empty amount field + +Details: + TestName: transferWithEmptyAmount + Description: This test ensures that transfer requests with empty amount fields are properly rejected with appropriate error messaging. +Execution: + Arrange: Create a TransferRequest with valid source and target accounts but empty amount field, and set up mock HttpSession. + Act: Invoke the transfer method with the request containing empty amount. + Assert: Verify ResponseEntity returns HTTP 400 Bad Request and response body contains the empty fields validation message. +Validation: + This assertion validates that the amount field validation works correctly. Since financial transactions require explicit amounts, this test ensures that no transfers can be processed without a specified amount, maintaining transaction integrity. + +Scenario 5: Transfer request with all fields empty + +Details: + TestName: transferWithAllFieldsEmpty + Description: This test verifies that requests with all required fields empty are rejected with the appropriate validation error message. +Execution: + Arrange: Set up a TransferRequest object with all fields (source account, target account, amount) as empty strings, and mock HttpSession. + Act: Call the transfer method with the completely empty request. + Assert: Confirm that ResponseEntity returns HTTP 400 status and response body contains the validation error message for empty fields. +Validation: + This test ensures comprehensive input validation by checking the most extreme case of completely empty input. It verifies that the validation logic correctly identifies and handles requests with no valid data. + +Scenario 6: Transfer to same account validation + +Details: + TestName: transferToSameAccount + Description: This test checks that the method properly prevents transfers where the source and target accounts are identical. +Execution: + Arrange: Create a TransferRequest with identical source and target account IDs, valid amount, and mock HttpSession. + Act: Execute the transfer method with the request containing same source and target accounts. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request status and response body contains "Cannot Transfer Into The Same Account, Please select the appropriate account to perform transfer.". +Validation: + This assertion ensures that business logic prevents self-transfers, which would be meaningless operations. It validates that the system correctly identifies and rejects such requests with clear messaging to guide users toward valid operations. + +Scenario 7: Transfer with zero amount + +Details: + TestName: transferWithZeroAmount + Description: This test validates that transfer requests with zero amount are rejected as invalid transactions. +Execution: + Arrange: Set up a TransferRequest with valid different source and target accounts but amount set to "0", and prepare mock HttpSession. + Act: Invoke the transfer method with the zero amount request. + Assert: Check that ResponseEntity returns HTTP 400 status and response body contains "Cannot Transfer an amount of 0 (Zero) value, please enter a value greater than.". +Validation: + This test ensures that only meaningful transfer amounts are processed. Zero-value transfers would not change account balances and could indicate user error or system issues, so proper rejection with clear messaging is essential. + +Scenario 8: Transfer with insufficient funds + +Details: + TestName: transferWithInsufficientFunds + Description: This test verifies that transfers are properly rejected when the source account has insufficient balance to cover the transfer amount. +Execution: + Arrange: Create valid TransferRequest with different accounts, mock HttpSession with authenticated user, mock AccountRepository to return balance lower than transfer amount, and mock TransactRepository for logging failed transaction. + Act: Call the transfer method with the insufficient funds scenario. + Assert: Verify ResponseEntity returns HTTP 400 Bad Request, response body contains "You have insufficient Funds to perform this transfer.", and confirm that failed transaction is logged. +Validation: + This assertion ensures that the system prevents overdrafts and maintains account balance integrity. It also verifies that failed transactions are properly logged for audit purposes, which is crucial for financial applications. + +Scenario 9: Successful transfer with exact balance amount + +Details: + TestName: transferWithExactBalanceAmount + Description: This test validates that transfers work correctly when the transfer amount exactly matches the available balance in the source account. +Execution: + Arrange: Set up TransferRequest with valid different accounts, mock HttpSession with authenticated user, mock AccountRepository to return balance exactly equal to transfer amount, and mock all repository operations. + Act: Execute the transfer method with the exact balance scenario. + Assert: Verify ResponseEntity returns HTTP 200 status, success message is returned, and both account balances are updated correctly (source becomes 0, target increases by transfer amount). +Validation: + This test ensures that edge case scenarios where the entire account balance is transferred are handled correctly. It validates that balance calculations are precise and that accounts can be completely emptied through valid transfers. + +Scenario 10: Transfer with decimal amount values + +Details: + TestName: transferWithDecimalAmount + Description: This test checks that the method correctly handles transfer amounts with decimal values and performs accurate financial calculations. +Execution: + Arrange: Create TransferRequest with valid accounts and decimal transfer amount (e.g., "123.45"), mock HttpSession with user, mock AccountRepository with sufficient balance, and mock repository operations. + Act: Invoke the transfer method with decimal amount request. + Assert: Confirm ResponseEntity returns HTTP 200 status, transfer completes successfully, and balance calculations maintain decimal precision. +Validation: + This assertion ensures that the system handles real-world financial amounts with decimal precision correctly. It validates that floating-point arithmetic in balance calculations doesn't introduce rounding errors that could affect financial accuracy. + +Scenario 11: Transfer with negative amount (edge case) + +Details: + TestName: transferWithNegativeAmount + Description: This test verifies system behavior when a transfer request contains a negative amount value, which should be handled as an invalid input. +Execution: + Arrange: Set up TransferRequest with valid accounts but negative transfer amount (e.g., "-100.00"), and mock HttpSession. + Act: Call the transfer method with negative amount request. + Assert: Verify that the system handles negative amounts appropriately, either through validation error or by treating it as invalid input. +Validation: + This test ensures that the system prevents potentially malicious or erroneous requests with negative amounts. While the current implementation may not explicitly check for negative values, this scenario helps identify whether additional validation is needed. + +Scenario 12: Transfer when user session is null + +Details: + TestName: transferWithNullUserSession + Description: This test checks system behavior when the HttpSession does not contain a valid user attribute, simulating an unauthenticated request. +Execution: + Arrange: Create valid TransferRequest and HttpSession that returns null for user attribute. + Act: Execute the transfer method with null user session. + Assert: Verify that the method handles the null user scenario appropriately, likely throwing NullPointerException or similar error. +Validation: + This test identifies potential security vulnerabilities and ensures that proper authentication checks are in place. It helps validate that the system doesn't process financial transactions for unauthenticated users. + +Scenario 13: Transfer with invalid account ID format + +Details: + TestName: transferWithInvalidAccountIdFormat + Description: This test validates system behavior when account IDs cannot be parsed as integers, such as when they contain non-numeric characters. +Execution: + Arrange: Set up TransferRequest with account IDs containing non-numeric characters (e.g., "abc123"), valid amount, and mock HttpSession. + Act: Invoke the transfer method with invalid account ID format. + Assert: Verify that NumberFormatException is thrown when attempting to parse invalid account IDs. +Validation: + This assertion ensures that the system properly handles malformed input data. It validates that proper error handling is in place for parsing operations and helps identify where additional input validation might be needed. + +Scenario 14: Transfer with invalid amount format + +Details: + TestName: transferWithInvalidAmountFormat + Description: This test checks system response when the transfer amount cannot be parsed as a valid double value. +Execution: + Arrange: Create TransferRequest with valid account IDs but invalid amount format (e.g., "not-a-number"), and mock HttpSession. + Act: Call the transfer method with invalid amount format. + Assert: Verify that NumberFormatException is thrown when attempting to parse the invalid amount. +Validation: + This test ensures that amount parsing is properly handled and that invalid financial data doesn't cause system crashes. It helps validate that robust input validation and error handling are in place for critical financial operations. + +Scenario 15: Successful transfer with transaction logging verification + +Details: + TestName: successfulTransferWithTransactionLogging + Description: This test specifically verifies that successful transfers are properly logged in the transaction repository with correct details. +Execution: + Arrange: Set up valid TransferRequest with different accounts, mock HttpSession with user, mock AccountRepository with sufficient funds, and mock TransactRepository to capture logging calls. + Act: Execute the transfer method with valid parameters. + Assert: Verify successful response and confirm that TransactRepository.logTransaction is called with correct parameters including account ID, transaction type "Transfer", amount, channel "online", status "success", and success message. +Validation: + This assertion ensures that audit trails are properly maintained for all successful transfers. Transaction logging is crucial for financial applications for compliance, troubleshooting, and audit purposes, making this validation essential for system integrity. + +*/ + +// ********RoostGPT******** +package com.beko.DemoBank_v1.controllers; + +import com.beko.DemoBank_v1.models.TransferRequest; +import com.beko.DemoBank_v1.models.User; +import com.beko.DemoBank_v1.models.Account; +import com.beko.DemoBank_v1.repository.AccountRepository; +import com.beko.DemoBank_v1.repository.TransactRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Tag; +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.HttpStatus; +import org.springframework.http.ResponseEntity; +import javax.servlet.http.HttpSession; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; +import org.junit.jupiter.api.*; +import com.beko.DemoBank_v1.models.PaymentRequest; +import com.beko.DemoBank_v1.repository.PaymentRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; + +@ExtendWith(MockitoExtension.class) +class TransactControllerTransferTest { + + @Mock + private AccountRepository accountRepository; + + @Mock + private TransactRepository transactRepository; + + @Mock + private HttpSession session; + + @InjectMocks + private TransactController transactController; + + private User mockUser; + + private TransferRequest transferRequest; + + private List mockAccounts; + + @BeforeEach + void setUp() { + mockUser = new User(); + mockUser.setUser_id("123"); + + transferRequest = new TransferRequest(); + + Account account1 = new Account(); + Account account2 = new Account(); + mockAccounts = Arrays.asList(account1, account2); + } + + @Test + @Tag("valid") + void successfulTransferBetweenDifferentAccounts() { + transferRequest.setSourceAccount("1001"); + transferRequest.setTargetAccount("1002"); + transferRequest.setAmount("500.00"); + + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 1001)).thenReturn(1000.0); + when(accountRepository.getAccountBalance(123, 1002)).thenReturn(200.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(mockAccounts); + + ResponseEntity response = transactController.transfer(transferRequest, session); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertTrue(response.getBody() instanceof Map); + Map responseBody = (Map) response.getBody(); + assertEquals("Transfer completed successfully.", responseBody.get("message")); + assertNotNull(responseBody.get("accounts")); + + verify(accountRepository).changeAccountsBalanceById(500.0, 1001); + verify(accountRepository).changeAccountsBalanceById(700.0, 1002); + verify(transactRepository).logTransaction(eq(1001), eq("Transfer"), eq(500.0), eq("online"), eq("success"), + eq("Transfer Transaction Successfull"), any(LocalDateTime.class)); + } + + @Test + @Tag("invalid") + void transferWithEmptySourceAccount() { + transferRequest.setSourceAccount(""); + transferRequest.setTargetAccount("1002"); + transferRequest.setAmount("500.00"); + + ResponseEntity response = transactController.transfer(transferRequest, session); + + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("The account transferring from and to along with the amount cannot be empty!", response.getBody()); + } + + @Test + @Tag("invalid") + void transferWithEmptyTargetAccount() { + transferRequest.setSourceAccount("1001"); + transferRequest.setTargetAccount(""); + transferRequest.setAmount("500.00"); + + ResponseEntity response = transactController.transfer(transferRequest, session); + + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("The account transferring from and to along with the amount cannot be empty!", response.getBody()); + } + + @Test + @Tag("invalid") + void transferWithEmptyAmount() { + transferRequest.setSourceAccount("1001"); + transferRequest.setTargetAccount("1002"); + transferRequest.setAmount(""); + + ResponseEntity response = transactController.transfer(transferRequest, session); + + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("The account transferring from and to along with the amount cannot be empty!", response.getBody()); + } + + @Test + @Tag("invalid") + void transferWithAllFieldsEmpty() { + transferRequest.setSourceAccount(""); + transferRequest.setTargetAccount(""); + transferRequest.setAmount(""); + + ResponseEntity response = transactController.transfer(transferRequest, session); + + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("The account transferring from and to along with the amount cannot be empty!", response.getBody()); + } + + @Test + @Tag("invalid") + void transferToSameAccount() { + transferRequest.setSourceAccount("1001"); + transferRequest.setTargetAccount("1001"); + transferRequest.setAmount("500.00"); + + ResponseEntity response = transactController.transfer(transferRequest, session); + + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals( + "Cannot Transfer Into The Same Account, Please select the appropriate account to perform transfer.", + response.getBody()); + } + + @Test + @Tag("boundary") + void transferWithZeroAmount() { + transferRequest.setSourceAccount("1001"); + transferRequest.setTargetAccount("1002"); + transferRequest.setAmount("0"); + + ResponseEntity response = transactController.transfer(transferRequest, session); + + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("Cannot Transfer an amount of 0 (Zero) value, please enter a value greater than.", + response.getBody()); + } + + @Test + @Tag("invalid") + void transferWithInsufficientFunds() { + transferRequest.setSourceAccount("1001"); + transferRequest.setTargetAccount("1002"); + transferRequest.setAmount("1500.00"); + + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 1001)).thenReturn(1000.0); + + ResponseEntity response = transactController.transfer(transferRequest, session); + + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("You have insufficient Funds to perform this transfer.", response.getBody()); + + verify(transactRepository).logTransaction(eq(1001), eq("transfer"), eq(1500.0), eq("online"), eq("failed"), + eq("Insufficient funds."), any(LocalDateTime.class)); + } + + @Test + @Tag("boundary") + void transferWithExactBalanceAmount() { + transferRequest.setSourceAccount("1001"); + transferRequest.setTargetAccount("1002"); + transferRequest.setAmount("1000.00"); + + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 1001)).thenReturn(1000.0); + when(accountRepository.getAccountBalance(123, 1002)).thenReturn(500.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(mockAccounts); + + ResponseEntity response = transactController.transfer(transferRequest, session); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + Map responseBody = (Map) response.getBody(); + assertEquals("Transfer completed successfully.", responseBody.get("message")); + + verify(accountRepository).changeAccountsBalanceById(0.0, 1001); + verify(accountRepository).changeAccountsBalanceById(1500.0, 1002); + } + + @Test + @Tag("valid") + void transferWithDecimalAmount() { + transferRequest.setSourceAccount("1001"); + transferRequest.setTargetAccount("1002"); + transferRequest.setAmount("123.45"); + + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 1001)).thenReturn(500.0); + when(accountRepository.getAccountBalance(123, 1002)).thenReturn(300.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(mockAccounts); + + ResponseEntity response = transactController.transfer(transferRequest, session); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + Map responseBody = (Map) response.getBody(); + assertEquals("Transfer completed successfully.", responseBody.get("message")); + + verify(accountRepository).changeAccountsBalanceById(376.55, 1001); + verify(accountRepository).changeAccountsBalanceById(423.45, 1002); + } + + @Test + @Tag("invalid") + void transferWithNegativeAmount() { + transferRequest.setSourceAccount("1001"); + transferRequest.setTargetAccount("1002"); + transferRequest.setAmount("-100.00"); + + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 1001)).thenReturn(1000.0); + + ResponseEntity response = transactController.transfer(transferRequest, session); + + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertEquals("You have insufficient Funds to perform this transfer.", response.getBody()); + + verify(transactRepository).logTransaction(eq(1001), eq("transfer"), eq(-100.0), eq("online"), eq("failed"), + eq("Insufficient funds."), any(LocalDateTime.class)); + } + + @Test + @Tag("invalid") + void transferWithNullUserSession() { + transferRequest.setSourceAccount("1001"); + transferRequest.setTargetAccount("1002"); + transferRequest.setAmount("500.00"); + + when(session.getAttribute("user")).thenReturn(null); + + assertThrows(NullPointerException.class, () -> { + transactController.transfer(transferRequest, session); + }); + } + + @Test + @Tag("invalid") + void transferWithInvalidAccountIdFormat() { + transferRequest.setSourceAccount("abc123"); + transferRequest.setTargetAccount("1002"); + transferRequest.setAmount("500.00"); + + assertThrows(NumberFormatException.class, () -> { + transactController.transfer(transferRequest, session); + }); + } + + @Test + @Tag("invalid") + void transferWithInvalidAmountFormat() { + transferRequest.setSourceAccount("1001"); + transferRequest.setTargetAccount("1002"); + transferRequest.setAmount("not-a-number"); + + assertThrows(NumberFormatException.class, () -> { + transactController.transfer(transferRequest, session); + }); + } + + @Test + @Tag("integration") + void successfulTransferWithTransactionLogging() { + transferRequest.setSourceAccount("1001"); + transferRequest.setTargetAccount("1002"); + transferRequest.setAmount("750.00"); + + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 1001)).thenReturn(1000.0); + when(accountRepository.getAccountBalance(123, 1002)).thenReturn(200.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(mockAccounts); + + ResponseEntity response = transactController.transfer(transferRequest, session); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + Map responseBody = (Map) response.getBody(); + assertEquals("Transfer completed successfully.", responseBody.get("message")); + + verify(transactRepository).logTransaction(eq(1001), eq("Transfer"), eq(750.0), eq("online"), eq("success"), + eq("Transfer Transaction Successfull"), any(LocalDateTime.class)); + + verify(accountRepository).getUserAccountsById(123); + verify(accountRepository, times(2)).changeAccountsBalanceById(anyDouble(), anyInt()); + } + +} \ No newline at end of file