From ea41d26c7bb92435fe7c8275c74006dd8968cf33 Mon Sep 17 00:00:00 2001 From: roost-io Date: Thu, 26 Mar 2026 06:53:59 +0000 Subject: [PATCH 1/2] Unit test generated by RoostGPT Using AI Model global.anthropic.claude-sonnet-4-20250514-v1:0 --- Online-Banking-App-Spring-Boot/pom.xml | 285 ++++---- .../TransactControllerDepositTest.java | 510 +++++++++++++++ .../TransactControllerTransfer744Test.java | 617 ++++++++++++++++++ .../TransactControllerTransfer758Test.java | 344 ++++++++++ .../TransactControllerTransferTest.java | 534 +++++++++++++++ 5 files changed, 2176 insertions(+), 114 deletions(-) create mode 100644 Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerDepositTest.java create mode 100644 Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransfer744Test.java create mode 100644 Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransfer758Test.java create mode 100644 Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransferTest.java 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..9be658d --- /dev/null +++ b/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerDepositTest.java @@ -0,0 +1,510 @@ + +// ********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 + +[ + { + "vulnerability": "CWE-20: Improper Input Validation", + "issue": "The code only checks if strings are empty but doesn't validate format, range, or malicious content. Numeric parsing occurs without validation, allowing potential NumberFormatException or injection of invalid values like negative deposits.", + "solution": "Implement comprehensive input validation using regex patterns for account ID format, range checks for deposit amounts (positive values only), and proper exception handling around parsing operations. Use BigDecimal for monetary calculations instead of double." + }, + { + "vulnerability": "CWE-89: SQL Injection", + "issue": "Direct use of parsed integers and doubles in repository methods without parameterized queries could lead to SQL injection if the repository layer doesn't properly sanitize inputs, especially with the acc_id parameter.", + "solution": "Ensure all repository methods use parameterized queries or prepared statements. Validate that accountRepository methods properly sanitize inputs and use JPA/Hibernate parameter binding instead of string concatenation." + }, + { + "vulnerability": "CWE-284: Improper Access Control", + "issue": "No verification that the account_id belongs to the authenticated user, allowing potential unauthorized access to other users' accounts through account ID manipulation.", + "solution": "Add authorization check to verify account ownership: validate that the provided account_id belongs to the authenticated user before performing any operations. Implement proper access control at the service layer." + }, + { + "vulnerability": "CWE-362: Concurrent Execution Race Condition", + "issue": "The deposit operation involves multiple non-atomic steps (get balance, calculate new balance, update balance) without transaction management, creating race conditions in concurrent environments that could lead to incorrect balance calculations.", + "solution": "Wrap the entire deposit operation in a database transaction using @Transactional annotation. Implement optimistic or pessimistic locking mechanisms to prevent concurrent balance modifications." + }, + { + "vulnerability": "CWE-476: NULL Pointer Dereference", + "issue": "No null checks on session.getAttribute('user') result or repository method returns, potentially causing NullPointerException if session expires or database queries fail.", + "solution": "Add explicit null checks for user object from session and all repository method returns. Implement proper session validation and return appropriate error responses for null scenarios." + }, + { + "vulnerability": "CWE-665: Improper Initialization", + "issue": "Variables like user, user_id, currentBalance, newBalance, and currentDateTime are used without declaration or proper initialization, indicating incomplete code that could lead to compilation errors or runtime exceptions.", + "solution": "Properly declare all variables with appropriate types and initialize them. Add proper variable scoping and ensure currentDateTime is set using LocalDateTime.now() before use in transaction logging." + }, + { + "vulnerability": "CWE-352: Cross-Site Request Forgery", + "issue": "The POST endpoint lacks CSRF protection, allowing malicious websites to perform unauthorized deposit operations on behalf of authenticated users through cross-site requests.", + "solution": "Enable Spring Security's CSRF protection and ensure proper CSRF token validation for state-changing operations. Use @RequestMapping with proper method restrictions and implement anti-CSRF tokens." + }, + { + "vulnerability": "CWE-704: Incorrect Type Conversion", + "issue": "Using double for monetary calculations introduces floating-point precision errors that can lead to incorrect financial calculations and potential loss of funds in banking operations.", + "solution": "Replace double with BigDecimal for all monetary calculations to ensure precise decimal arithmetic. Use appropriate rounding modes and scale settings for financial operations." + } +] +,Scenario 1: Successful Deposit with Valid Inputs + +Details: + TestName: successfulDepositWithValidInputs + Description: This test verifies that a deposit operation completes successfully when provided with valid deposit amount and account ID, with a valid user session. + +Execution: + Arrange: Set up mocks for accountRepository and transactRepository. Create a valid HttpSession with a User object containing a valid user_id. Prepare a requestMap with valid deposit_amount and account_id values. Mock the repository methods to return expected values. + Act: Call the deposit method with the prepared requestMap and session parameters. + Assert: Verify that the response has HTTP status 200 (OK), contains the success message "Amount Deposited Successfully.", and includes the updated accounts information. + +Validation: + The assertion verifies that the deposit operation executes the complete workflow including balance calculation, account update, and transaction logging. This test ensures the primary happy path functionality works as expected for legitimate deposit requests. + +Scenario 2: Bad Request When Deposit Amount is Empty + +Details: + TestName: badRequestWhenDepositAmountIsEmpty + Description: This test validates that the method returns a bad request response when the deposit_amount field in the request is empty or null. + +Execution: + Arrange: Create a requestMap with an empty string for deposit_amount and a valid account_id. Set up a valid HttpSession with User object. + Act: Invoke the deposit method with the requestMap containing empty deposit_amount. + Assert: Verify that the response has HTTP status 400 (Bad Request) and contains the error message "Deposit amount and account ID cannot be empty." + +Validation: + This assertion ensures proper input validation for the deposit amount field. The test confirms that the method properly handles invalid input scenarios and provides appropriate error feedback to prevent processing of incomplete requests. + +Scenario 3: Bad Request When Account ID is Empty + +Details: + TestName: badRequestWhenAccountIdIsEmpty + Description: This test checks that the method returns a bad request response when the account_id field in the request is empty or null. + +Execution: + Arrange: Create a requestMap with a valid deposit_amount and an empty string for account_id. Set up a valid HttpSession with User object. + Act: Call the deposit method with the requestMap containing empty account_id. + Assert: Verify that the response has HTTP status 400 (Bad Request) and the response body contains "Deposit amount and account ID cannot be empty." + +Validation: + The assertion validates that account ID field validation works correctly. This test is crucial for ensuring data integrity and preventing operations on undefined accounts. + +Scenario 4: Bad Request When Both Fields are Empty + +Details: + TestName: badRequestWhenBothFieldsAreEmpty + Description: This test verifies that the method handles the case where both deposit_amount and account_id are empty strings in the request. + +Execution: + Arrange: Create a requestMap with empty strings for both deposit_amount and account_id. Set up a valid HttpSession. + Act: Invoke the deposit method with the requestMap containing both empty fields. + Assert: Verify that the response returns HTTP status 400 and contains the appropriate error message about empty fields. + +Validation: + This assertion confirms that the validation logic properly handles multiple empty fields simultaneously. The test ensures comprehensive input validation for all required parameters. + +Scenario 5: Bad Request When Deposit Amount is Zero + +Details: + TestName: badRequestWhenDepositAmountIsZero + Description: This test validates that the method rejects deposit requests where the deposit amount is exactly zero. + +Execution: + Arrange: Create a requestMap with deposit_amount as "0" and a valid account_id. Set up HttpSession with a valid User object. Mock the user.getUser_id() method to return a valid user ID string. + Act: Call the deposit method with the zero deposit amount. + Assert: Verify that the response has HTTP status 400 and contains the error message "Deposit amount cannot be zero." + +Validation: + The assertion ensures that business logic prevents meaningless zero-value deposits. This test validates that the application enforces reasonable business rules for financial transactions. + +Scenario 6: Successful Deposit with Decimal Amount + +Details: + TestName: successfulDepositWithDecimalAmount + Description: This test verifies that the deposit method correctly handles decimal deposit amounts and performs accurate balance calculations. + +Execution: + Arrange: Set up mocks for repositories. Create requestMap with a decimal deposit_amount like "150.75" and valid account_id. Mock accountRepository.getAccountBalance() to return a current balance. Set up valid HttpSession with User object. + Act: Execute the deposit method with decimal amount parameters. + Assert: Verify HTTP status 200, success message, and that the balance calculation includes the decimal precision correctly. + +Validation: + This assertion confirms that the method handles monetary calculations with proper decimal precision. The test is important for ensuring accurate financial computations in real-world scenarios. + +Scenario 7: Successful Deposit Updates Account Balance + +Details: + TestName: successfulDepositUpdatesAccountBalance + Description: This test validates that a successful deposit operation correctly updates the account balance by calling the appropriate repository method. + +Execution: + Arrange: Mock accountRepository methods. Set currentBalance to 1000.0 via getAccountBalance mock. Create requestMap with deposit_amount "500.0" and valid account_id. Set up valid session. + Act: Call the deposit method and capture the repository interactions. + Assert: Verify that changeAccountsBalanceById was called with the correct new balance (1500.0) and account ID. + +Validation: + The assertion verifies that balance updates are performed correctly with accurate calculations. This test ensures the core financial operation logic works as intended. + +Scenario 8: Successful Deposit Logs Transaction + +Details: + TestName: successfulDepositLogsTransaction + Description: This test checks that a successful deposit operation creates a proper transaction log entry with all required details. + +Execution: + Arrange: Set up repository mocks including transactRepository.logTransaction(). Create valid requestMap and HttpSession. Mock other dependencies to allow successful flow. + Act: Execute the deposit method with valid parameters. + Assert: Verify that logTransaction was called with correct parameters including account ID, "deposit" type, amount, "online" method, "success" status, success message, and currentDateTime. + +Validation: + This assertion ensures proper audit trail creation for financial transactions. The test validates that all deposit operations are properly recorded for compliance and tracking purposes. + +Scenario 9: Number Format Exception When Account ID is Invalid + +Details: + TestName: numberFormatExceptionWhenAccountIdIsInvalid + Description: This test verifies the behavior when account_id contains non-numeric characters that cannot be parsed to integer. + +Execution: + Arrange: Create requestMap with valid deposit_amount and account_id containing non-numeric value like "abc123". Set up valid HttpSession. + Act: Call the deposit method with invalid account_id format. + Assert: Verify that a NumberFormatException is thrown or handled appropriately by the application. + +Validation: + The assertion checks error handling for invalid data format scenarios. This test ensures the application gracefully handles or appropriately fails when receiving malformed input data. + +Scenario 10: Number Format Exception When Deposit Amount is Invalid + +Details: + TestName: numberFormatExceptionWhenDepositAmountIsInvalid + Description: This test validates the behavior when deposit_amount contains non-numeric characters that cannot be parsed to double. + +Execution: + Arrange: Create requestMap with deposit_amount containing invalid format like "invalid_amount" and valid account_id. Set up valid HttpSession with User object. + Act: Invoke the deposit method with invalid deposit_amount format. + Assert: Verify that a NumberFormatException is thrown or handled appropriately. + +Validation: + This assertion ensures proper handling of invalid numeric input for monetary values. The test is critical for preventing application crashes due to malformed financial data. + +Scenario 11: Null Pointer Exception When User Session is Invalid + +Details: + TestName: nullPointerExceptionWhenUserSessionIsInvalid + Description: This test checks the behavior when the HttpSession does not contain a valid User object or the session is null. + +Execution: + Arrange: Create valid requestMap with proper deposit_amount and account_id. Set up HttpSession that returns null for getAttribute("user") or use null session. + Act: Call the deposit method with invalid session state. + Assert: Verify that a NullPointerException is thrown or handled appropriately when trying to access user.getUser_id(). + +Validation: + The assertion validates session management and user authentication requirements. This test ensures the application properly handles unauthenticated or expired session scenarios. + +Scenario 12: Successful Deposit Returns User Accounts + +Details: + TestName: successfulDepositReturnsUserAccounts + Description: This test verifies that a successful deposit operation includes the updated user accounts information in the response. + +Execution: + Arrange: Mock accountRepository.getUserAccountsById() to return a list of user accounts. Set up all other mocks for successful deposit flow. Create valid requestMap and HttpSession. + Act: Execute the deposit method with valid parameters. + Assert: Verify that the response contains "accounts" key with the user accounts data returned from getUserAccountsById method. + +Validation: + This assertion ensures that clients receive updated account information after deposit operations. The test validates that the response provides complete information needed for UI updates or further operations. + +*/ + +// ********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; + + @BeforeEach + void setUp() { + requestMap = new HashMap<>(); + } + + @Test + @Tag("valid") + void successfulDepositWithValidInputs() { + // Arrange + requestMap.put("deposit_amount", "500.0"); + requestMap.put("account_id", "1"); + + when(session.getAttribute("user")).thenReturn(user); + when(user.getUser_id()).thenReturn("123"); + when(accountRepository.getAccountBalance(123, 1)).thenReturn(1000.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(List.of()); + + // Act + ResponseEntity response = transactController.deposit(requestMap, session); + + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getBody()); + + Map responseBody = (Map) response.getBody(); + assertEquals("Amount Deposited Successfully.", responseBody.get("message")); + assertNotNull(responseBody.get("accounts")); + + verify(accountRepository).changeAccountsBalanceById(1500.0, 1); + verify(transactRepository).logTransaction(eq(1), eq("deposit"), eq(500.0), eq("online"), eq("success"), + eq("Deposit Transaction Successfull"), any(LocalDateTime.class)); + } + + @Test + @Tag("invalid") + void badRequestWhenDepositAmountIsEmpty() { + // 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()); + } + + @Test + @Tag("invalid") + void badRequestWhenAccountIdIsEmpty() { + // Arrange + requestMap.put("deposit_amount", "500.0"); + 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()); + } + + @Test + @Tag("invalid") + void badRequestWhenBothFieldsAreEmpty() { + // 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()); + } + + @Test + @Tag("boundary") + void badRequestWhenDepositAmountIsZero() { + // 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()); + } + + @Test + @Tag("valid") + void successfulDepositWithDecimalAmount() { + // Arrange + requestMap.put("deposit_amount", "150.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(List.of()); + + // 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(1150.75, 2); + verify(transactRepository).logTransaction(eq(2), eq("deposit"), eq(150.75), eq("online"), eq("success"), + eq("Deposit Transaction Successfull"), any(LocalDateTime.class)); + } + + @Test + @Tag("integration") + void successfulDepositUpdatesAccountBalance() { + // Arrange + requestMap.put("deposit_amount", "500.0"); + requestMap.put("account_id", "1"); + + when(session.getAttribute("user")).thenReturn(user); + when(user.getUser_id()).thenReturn("123"); + when(accountRepository.getAccountBalance(123, 1)).thenReturn(1000.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(List.of()); + + // Act + transactController.deposit(requestMap, session); + + // Assert + verify(accountRepository).changeAccountsBalanceById(1500.0, 1); + verify(accountRepository).getAccountBalance(123, 1); + } + + @Test + @Tag("integration") + void successfulDepositLogsTransaction() { + // Arrange + requestMap.put("deposit_amount", "300.0"); + requestMap.put("account_id", "3"); + + when(session.getAttribute("user")).thenReturn(user); + when(user.getUser_id()).thenReturn("789"); + when(accountRepository.getAccountBalance(789, 3)).thenReturn(500.0); + when(accountRepository.getUserAccountsById(789)).thenReturn(List.of()); + + // Act + transactController.deposit(requestMap, session); + + // Assert + verify(transactRepository).logTransaction(eq(3), eq("deposit"), eq(300.0), eq("online"), eq("success"), + eq("Deposit Transaction Successfull"), any(LocalDateTime.class)); + } + + @Test + @Tag("invalid") + void numberFormatExceptionWhenAccountIdIsInvalid() { + // Arrange + requestMap.put("deposit_amount", "500.0"); + 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); + }); + } + + @Test + @Tag("invalid") + void numberFormatExceptionWhenDepositAmountIsInvalid() { + // Arrange + requestMap.put("deposit_amount", "invalid_amount"); + 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); + }); + } + + @Test + @Tag("invalid") + void nullPointerExceptionWhenUserSessionIsInvalid() { + // Arrange + requestMap.put("deposit_amount", "500.0"); + requestMap.put("account_id", "1"); + + when(session.getAttribute("user")).thenReturn(null); + + // Act & Assert + assertThrows(NullPointerException.class, () -> { + transactController.deposit(requestMap, session); + }); + } + + @Test + @Tag("integration") + void successfulDepositReturnsUserAccounts() { + // Arrange + requestMap.put("deposit_amount", "750.0"); + requestMap.put("account_id", "4"); + + List mockAccounts = List.of("Account1", "Account2"); + + when(session.getAttribute("user")).thenReturn(user); + when(user.getUser_id()).thenReturn("999"); + when(accountRepository.getAccountBalance(999, 4)).thenReturn(2000.0); + when(accountRepository.getUserAccountsById(999)).thenReturn(mockAccounts); + + // Act + ResponseEntity response = transactController.deposit(requestMap, session); + + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + + Map responseBody = (Map) response.getBody(); + assertEquals(mockAccounts, responseBody.get("accounts")); + + verify(accountRepository).getUserAccountsById(999); + } + +} \ No newline at end of file diff --git a/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransfer744Test.java b/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransfer744Test.java new file mode 100644 index 0000000..208c6e7 --- /dev/null +++ b/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransfer744Test.java @@ -0,0 +1,617 @@ + +// ********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 + +[ + { + "vulnerability": "CWE-862: Missing Authorization", + "issue": "The method lacks proper authentication and authorization checks. Any user with a valid session can potentially perform transfers from any account by manipulating the account_id parameter, leading to unauthorized financial transactions.", + "solution": "Implement proper authorization by verifying that the authenticated user owns the account they are trying to transfer from. Add checks like: if (!accountRepository.isAccountOwnedByUser(user_id, accountID)) { return ResponseEntity.status(HttpStatus.FORBIDDEN).body(\"Unauthorized access to account\"); }" + }, + { + "vulnerability": "CWE-209: Information Exposure Through Error Messages", + "issue": "Error messages reveal sensitive business logic and internal system information such as 'insufficient funds' which can be used by attackers to enumerate valid accounts and understand system behavior.", + "solution": "Use generic error messages for security-sensitive operations and log detailed information server-side only. Replace specific messages with generic ones like 'Transaction failed' and use structured logging for internal debugging." + }, + { + "vulnerability": "CWE-362: Race Condition", + "issue": "The balance check and update operations are not atomic, creating a race condition where multiple concurrent requests could result in overdrafts or inconsistent account states in a multi-threaded environment.", + "solution": "Implement database transactions with appropriate isolation levels or use pessimistic locking: @Transactional(isolation = Isolation.SERIALIZABLE) and SELECT ... FOR UPDATE when checking balances to ensure atomic operations." + }, + { + "vulnerability": "CWE-20: Improper Input Validation", + "issue": "Input validation is insufficient - only checks for empty strings but doesn't validate format, length, or sanitize inputs. Numeric parsing can throw uncaught exceptions, and no validation exists for account number format or beneficiary name.", + "solution": "Implement comprehensive input validation using javax.validation annotations, regex patterns for account numbers, length limits for strings, and proper exception handling for numeric parsing with try-catch blocks around Integer.parseInt() and Double.parseDouble()." + }, + { + "vulnerability": "CWE-476: NULL Pointer Dereference", + "issue": "The code assumes session.getAttribute('user') will never return null and doesn't handle cases where the session is invalid or expired, potentially causing NullPointerException when accessing user properties.", + "solution": "Add null checks for session attributes: User user = (User) session.getAttribute('user'); if (user == null) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body('Session expired'); } before accessing user properties." + }, + { + "vulnerability": "CWE-134: Uncontrolled Format String", + "issue": "Variables like currentDateTime are used but never declared or initialized in the visible code scope, which could lead to compilation errors or runtime exceptions when the method executes.", + "solution": "Properly declare and initialize all variables: LocalDateTime currentDateTime = LocalDateTime.now(); and ensure all required repository fields are properly autowired and initialized." + }, + { + "vulnerability": "CWE-307: Improper Restriction of Excessive Authentication Attempts", + "issue": "No rate limiting or attempt monitoring is implemented for payment operations, allowing potential brute force attacks or denial of service through excessive payment requests.", + "solution": "Implement rate limiting using Spring Security or custom interceptors with Redis/cache-based counters to limit payment attempts per user per time window and add CAPTCHA for suspicious activity patterns." + }, + { + "vulnerability": "CWE-778: Insufficient Logging", + "issue": "While transaction logging exists, there's insufficient security event logging for failed authorization attempts, suspicious activities, or input validation failures that could help detect attacks.", + "solution": "Add comprehensive security logging using a structured logging framework like Logback with appropriate log levels: log.warn('Failed payment attempt for user {} on account {}', user_id, accountID) for security monitoring and incident response." + } +] +,Scenario 1: Valid Payment Request with Sufficient Funds + +Details: + TestName: validPaymentRequestWithSufficientFunds + Description: This test verifies that a valid payment request with all required fields populated and sufficient account balance processes successfully and returns a success response with updated account information. + +Execution: + Arrange: Create a PaymentRequest object 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 logging operations. + Act: Call the transfer method with the valid PaymentRequest and mocked HttpSession. + Assert: Verify that ResponseEntity returns HTTP 200 OK status, response body contains success message "Payment Processed Successfully!", and response includes updated accounts information. + +Validation: + This assertion verifies that the payment processing workflow completes successfully when all validation criteria are met. The test ensures that the business logic correctly processes valid payments, updates account balances, logs successful transactions, and returns appropriate success feedback to the user. + +Scenario 2: Empty Beneficiary Field + +Details: + TestName: emptyBeneficiaryField + Description: This test ensures that the system properly validates the beneficiary field and returns a bad request error when the beneficiary field is empty or null. + +Execution: + Arrange: Create a PaymentRequest object with an empty beneficiary field but valid values for other required fields (account_number, account_id, payment_amount). Mock HttpSession with a valid User object. + Act: Call the transfer method with the PaymentRequest containing empty beneficiary and mocked HttpSession. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request status and response body contains the error message "Beneficiary, account number, account paying from and account payment amount cannot be empty." + +Validation: + This assertion confirms that the input validation logic correctly identifies missing required fields and prevents processing of incomplete payment requests. This is crucial for data integrity and prevents invalid payment transactions from being processed. + +Scenario 3: Empty Account Number Field + +Details: + TestName: emptyAccountNumberField + Description: This test verifies that the system validates the account number field and rejects payment requests when the account number is empty. + +Execution: + Arrange: Create a PaymentRequest object with an empty account_number field but valid values for beneficiary, account_id, and payment_amount. Mock HttpSession with a valid User object. + Act: Call the transfer method with the PaymentRequest containing empty account number and mocked HttpSession. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request status and response body contains the validation error message about empty required fields. + +Validation: + This test ensures that the account number validation prevents payments to invalid or unspecified accounts, maintaining transaction security and data integrity within the banking system. + +Scenario 4: Empty Account ID Field + +Details: + TestName: emptyAccountIdField + Description: This test checks that the system properly validates the account_id field and returns an appropriate error response when the paying account ID is not provided. + +Execution: + Arrange: Create a PaymentRequest object with an empty account_id field but valid beneficiary, account_number, and payment_amount values. Mock HttpSession with a valid User object. + Act: Call the transfer method with the PaymentRequest containing empty account ID and mocked HttpSession. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request status and response body contains the field validation error message. + +Validation: + This assertion validates that the system correctly identifies when the source account for payment is not specified, preventing ambiguous payment processing and ensuring proper account identification for all transactions. + +Scenario 5: Empty Payment Amount Field + +Details: + TestName: emptyPaymentAmountField + Description: This test ensures that the system validates the payment_amount field and rejects requests when no payment amount is specified. + +Execution: + Arrange: Create a PaymentRequest object with an empty payment_amount field but valid beneficiary, account_number, and account_id values. Mock HttpSession with a valid User object. + Act: Call the transfer method with the PaymentRequest containing empty payment amount and mocked HttpSession. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request status and response body contains the validation error message for empty required fields. + +Validation: + This test confirms that payment amount validation prevents processing of payments without specified amounts, which is essential for financial transaction integrity and preventing unintended zero-value or undefined payment processing. + +Scenario 6: Zero Payment Amount + +Details: + TestName: zeroPaymentAmount + Description: This test verifies that the system correctly rejects payment requests when the payment amount is explicitly set to zero, even if all other fields are valid. + +Execution: + Arrange: Create a PaymentRequest object with valid beneficiary, account_number, and account_id, but set payment_amount to "0". Mock HttpSession with a valid User object. + Act: Call the transfer method with the PaymentRequest containing zero payment amount and mocked HttpSession. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request status and response body contains the specific error message "Payment amount cannot be 0." + +Validation: + This assertion ensures that the business logic prevents zero-value payments, which could be meaningless transactions or potential system abuse. The test validates that the system enforces minimum payment amount requirements. + +Scenario 7: Insufficient Funds for Payment + +Details: + TestName: insufficientFundsForPayment + Description: This test verifies that the system correctly handles payment requests when the account balance is insufficient to cover the requested payment amount. + +Execution: + Arrange: Create a valid PaymentRequest object with all required fields. Mock HttpSession with a valid User object. Mock AccountRepository.getAccountBalance to return a balance lower than the requested payment amount. Mock PaymentRepository.makePayment and TransactRepository.logTransaction for failed transaction logging. + Act: Call the transfer method with the valid PaymentRequest but insufficient account balance scenario. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request status and response body contains "You have insufficient Funds to perform this payment." Also verify that failed payment and transaction logs are created. + +Validation: + This test ensures that the system properly enforces account balance constraints and prevents overdraft situations. It also validates that failed transactions are properly logged for audit purposes and appropriate error messages are returned to users. + +Scenario 8: Invalid Account ID Format + +Details: + TestName: invalidAccountIdFormat + Description: This test checks how the system handles payment requests when the account_id field contains non-numeric values that cannot be parsed to an integer. + +Execution: + Arrange: Create a PaymentRequest object with valid beneficiary, account_number, and payment_amount, but set account_id to a non-numeric string like "abc123". Mock HttpSession with a valid User object. + Act: Call the transfer method with the PaymentRequest containing invalid account ID format and mocked HttpSession. + Assert: Verify that a NumberFormatException is thrown during Integer.parseInt(account_id) operation. + +Validation: + This test validates that the system properly handles data type conversion errors and ensures that only valid numeric account IDs are processed. This is important for preventing system errors and maintaining data type integrity. + +Scenario 9: Invalid Payment Amount Format + +Details: + TestName: invalidPaymentAmountFormat + Description: This test verifies the system's behavior when the payment_amount field contains non-numeric values that cannot be parsed to a double. + +Execution: + Arrange: Create a PaymentRequest object with valid beneficiary, account_number, and account_id, but set payment_amount to a non-numeric string like "invalid_amount". Mock HttpSession with a valid User object. + Act: Call the transfer method with the PaymentRequest containing invalid payment amount format and mocked HttpSession. + Assert: Verify that a NumberFormatException is thrown during Double.parseDouble(payment_amount) operation. + +Validation: + This assertion ensures that the system correctly handles numeric conversion errors for payment amounts and prevents processing of payments with invalid amount formats. This is crucial for financial data integrity. + +Scenario 10: Null HttpSession + +Details: + TestName: nullHttpSession + Description: This test checks the system's behavior when a null HttpSession is passed to the transfer method, simulating scenarios where session management fails. + +Execution: + Arrange: Create a valid PaymentRequest object with all required fields properly populated. Pass null as the HttpSession parameter instead of a mocked session object. + Act: Call the transfer method with the valid PaymentRequest but null HttpSession. + Assert: Verify that a NullPointerException is thrown when attempting to access session.getAttribute("user"). + +Validation: + This test validates that the system properly handles session management failures and ensures that user authentication is enforced for payment operations. It helps identify potential security vulnerabilities related to session handling. + +Scenario 11: Null User in Session + +Details: + TestName: nullUserInSession + Description: This test verifies the system's behavior when the HttpSession exists but does not contain a valid User object, simulating expired or invalid sessions. + +Execution: + Arrange: Create a valid PaymentRequest object with all required fields. Mock HttpSession to return null when getAttribute("user") is called. + Act: Call the transfer method with the valid PaymentRequest and mocked HttpSession containing null user. + Assert: Verify that a NullPointerException is thrown when attempting to access user.getUser_id(). + +Validation: + This assertion ensures that the system enforces proper user authentication and prevents payment processing for unauthenticated users. This is essential for security and preventing unauthorized transactions. + +Scenario 12: Successful Payment with Reference Field + +Details: + TestName: successfulPaymentWithReferenceField + Description: This test verifies that payments process correctly when all fields including the optional reference field are provided and the account has sufficient funds. + +Execution: + Arrange: Create a PaymentRequest object with valid values for all fields including a reference description. Mock HttpSession with a valid User object. Mock AccountRepository to return sufficient balance greater than payment amount. Mock PaymentRepository and TransactRepository for successful transaction logging. + Act: Call the transfer method with the complete PaymentRequest and mocked dependencies. + Assert: Verify that ResponseEntity returns HTTP 200 OK status, success message is returned, and verify that PaymentRepository.makePayment is called with the reference field value. + +Validation: + This test confirms that the reference field is properly handled and passed through to the payment logging system, ensuring that payment descriptions and references are maintained for record-keeping purposes. + +Scenario 13: Large Payment Amount Within Balance Limits + +Details: + TestName: largePaymentAmountWithinBalanceLimits + Description: This test verifies that the system correctly processes large payment amounts when the account balance is sufficient to cover the transaction. + +Execution: + Arrange: Create a PaymentRequest object with a large payment amount (e.g., "999999.99"). Mock HttpSession with a valid User object. Mock AccountRepository to return an account balance greater than the large payment amount. Mock payment and transaction repositories for logging operations. + Act: Call the transfer method with the PaymentRequest containing large payment amount and mocked dependencies. + Assert: Verify that ResponseEntity returns HTTP 200 OK status, success message is returned, and account balance is correctly updated by subtracting the large payment amount. + +Validation: + This test ensures that the system can handle large financial transactions correctly without overflow errors and that the balance calculation logic works properly for significant amounts. This is important for high-value banking operations. + +Scenario 14: Multiple Empty Fields Combination + +Details: + TestName: multipleEmptyFieldsCombination + Description: This test checks the system's validation behavior when multiple required fields are empty simultaneously. + +Execution: + Arrange: Create a PaymentRequest object with empty values for beneficiary, account_number, and payment_amount fields, but provide a valid account_id. Mock HttpSession with a valid User object. + Act: Call the transfer method with the PaymentRequest containing multiple empty fields and mocked HttpSession. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request status and response body contains the validation error message about empty required fields. + +Validation: + This assertion confirms that the validation logic correctly identifies multiple missing fields and provides appropriate error feedback. This ensures comprehensive input validation regardless of which combination of fields are missing. + +Scenario 15: Edge Case - Payment Amount Exactly Equal to Balance + +Details: + TestName: paymentAmountExactlyEqualToBalance + Description: This test verifies the system's behavior when the payment amount exactly matches the available account balance. + +Execution: + Arrange: Create a PaymentRequest object with valid fields and set payment amount to exactly match the account balance. Mock HttpSession with a valid User object. Mock AccountRepository to return an account balance that exactly equals the payment amount. Mock payment and transaction repositories. + Act: Call the transfer method with the PaymentRequest where payment amount equals available balance. + Assert: Verify that ResponseEntity returns HTTP 200 OK status, payment processes successfully, and the resulting account balance becomes zero after the transaction. + +Validation: + This test validates that the system correctly handles boundary conditions where payments consume the entire available balance. It ensures that the comparison logic (currentBalance < paymentAmount) works correctly for edge cases and that account balances can be reduced to zero through valid transactions. + +*/ + +// ********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.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.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 TransactControllerTransfer744Test { + + @Mock + private AccountRepository accountRepository; + + @Mock + private PaymentRepository paymentRepository; + + @Mock + private TransactRepository transactRepository; + + @Mock + private HttpSession session; + + @InjectMocks + private TransactController transactController; + + private User mockUser; + + private PaymentRequest paymentRequest; + + @BeforeEach + void setUp() { + mockUser = new User(); + mockUser.setUser_id("123"); + + paymentRequest = new PaymentRequest(); + paymentRequest.setBeneficiary("John Doe"); + paymentRequest.setAccount_number("9876543210"); + paymentRequest.setAccount_id("1"); + paymentRequest.setReference("Test payment"); + paymentRequest.setPayment_amount("100.50"); + } + + @Test + @Tag("valid") + void validPaymentRequestWithSufficientFunds() { + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 1)).thenReturn(500.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(List.of()); + + ResponseEntity result = transactController.transfer(paymentRequest, session); + + assertEquals(HttpStatus.OK, result.getStatusCode()); + assertTrue(result.getBody() instanceof Map); + Map response = (Map) result.getBody(); + assertEquals("Payment Processed Successfully!", response.get("message")); + assertNotNull(response.get("accounts")); + + verify(accountRepository).changeAccountsBalanceById(399.5, 1); + verify(paymentRepository).makePayment(eq(1), eq("John Doe"), eq("9876543210"), + eq(100.5), eq("Test payment"), eq("success"), eq("Payment Processed Successfully!"), any(LocalDateTime.class)); + verify(transactRepository).logTransaction(eq(1), eq("Payment"), eq(100.5), + eq("online"), eq("success"), eq("Payment Transaction Successfull"), any(LocalDateTime.class)); + } + + @Test + @Tag("invalid") + void emptyBeneficiaryField() { + paymentRequest.setBeneficiary(""); + + ResponseEntity result = transactController.transfer(paymentRequest, session); + + assertEquals(HttpStatus.BAD_REQUEST, result.getStatusCode()); + assertEquals("Beneficiary, account number, account paying from and account payment amount cannot be empty.", + result.getBody()); + } + + @Test + @Tag("invalid") + void emptyAccountNumberField() { + paymentRequest.setAccount_number(""); + + ResponseEntity result = transactController.transfer(paymentRequest, session); + + assertEquals(HttpStatus.BAD_REQUEST, result.getStatusCode()); + assertEquals("Beneficiary, account number, account paying from and account payment amount cannot be empty.", + result.getBody()); + } + + @Test + @Tag("invalid") + void emptyAccountIdField() { + paymentRequest.setAccount_id(""); + + ResponseEntity result = transactController.transfer(paymentRequest, session); + + assertEquals(HttpStatus.BAD_REQUEST, result.getStatusCode()); + assertEquals("Beneficiary, account number, account paying from and account payment amount cannot be empty.", + result.getBody()); + } + + @Test + @Tag("invalid") + void emptyPaymentAmountField() { + paymentRequest.setPayment_amount(""); + + ResponseEntity result = transactController.transfer(paymentRequest, session); + + assertEquals(HttpStatus.BAD_REQUEST, result.getStatusCode()); + assertEquals("Beneficiary, account number, account paying from and account payment amount cannot be empty.", + result.getBody()); + } + + @Test + @Tag("invalid") + void zeroPaymentAmount() { + paymentRequest.setPayment_amount("0"); + + ResponseEntity result = transactController.transfer(paymentRequest, session); + + assertEquals(HttpStatus.BAD_REQUEST, result.getStatusCode()); + assertEquals("Payment amount cannot be 0.", result.getBody()); + } + + @Test + @Tag("invalid") + void insufficientFundsForPayment() { + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 1)).thenReturn(50.0); + + ResponseEntity result = transactController.transfer(paymentRequest, session); + + assertEquals(HttpStatus.BAD_REQUEST, result.getStatusCode()); + assertEquals("You have insufficient Funds to perform this payment.", result.getBody()); + + verify(paymentRepository).makePayment(eq(1), eq("John Doe"), eq("9876543210"), + eq(100.5), eq("Test payment"), eq("failed"), + eq("Coult not Processed Payment due to insufficient funds."), any(LocalDateTime.class)); + verify(transactRepository).logTransaction(eq(1), eq("Payment"), eq(100.5), + eq("online"), eq("failed"), eq("Insufficient funds."), any(LocalDateTime.class)); + } + + @Test + @Tag("invalid") + void invalidAccountIdFormat() { + paymentRequest.setAccount_id("abc123"); + + assertThrows(NumberFormatException.class, () -> { + transactController.transfer(paymentRequest, session); + }); + } + + @Test + @Tag("invalid") + void invalidPaymentAmountFormat() { + paymentRequest.setPayment_amount("invalid_amount"); + + assertThrows(NumberFormatException.class, () -> { + transactController.transfer(paymentRequest, session); + }); + } + + @Test + @Tag("invalid") + void nullHttpSession() { + assertThrows(NullPointerException.class, () -> { + transactController.transfer(paymentRequest, null); + }); + } + + @Test + @Tag("invalid") + void nullUserInSession() { + when(session.getAttribute("user")).thenReturn(null); + + assertThrows(NullPointerException.class, () -> { + transactController.transfer(paymentRequest, session); + }); + } + + @Test + @Tag("valid") + void successfulPaymentWithReferenceField() { + paymentRequest.setReference("Monthly subscription payment"); + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 1)).thenReturn(200.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(List.of()); + + ResponseEntity result = transactController.transfer(paymentRequest, session); + + assertEquals(HttpStatus.OK, result.getStatusCode()); + Map response = (Map) result.getBody(); + assertEquals("Payment Processed Successfully!", response.get("message")); + + verify(paymentRepository).makePayment(eq(1), eq("John Doe"), eq("9876543210"), eq(100.5), + eq("Monthly subscription payment"), eq("success"), eq("Payment Processed Successfully!"), + any(LocalDateTime.class)); + } + + @Test + @Tag("boundary") + void largePaymentAmountWithinBalanceLimits() { + paymentRequest.setPayment_amount("999999.99"); + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 1)).thenReturn(1500000.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(List.of()); + + ResponseEntity result = transactController.transfer(paymentRequest, session); + + assertEquals(HttpStatus.OK, result.getStatusCode()); + Map response = (Map) result.getBody(); + assertEquals("Payment Processed Successfully!", response.get("message")); + + verify(accountRepository).changeAccountsBalanceById(500000.01, 1); + } + + @Test + @Tag("invalid") + void multipleEmptyFieldsCombination() { + paymentRequest.setBeneficiary(""); + paymentRequest.setAccount_number(""); + paymentRequest.setPayment_amount(""); + + ResponseEntity result = transactController.transfer(paymentRequest, session); + + assertEquals(HttpStatus.BAD_REQUEST, result.getStatusCode()); + assertEquals("Beneficiary, account number, account paying from and account payment amount cannot be empty.", + result.getBody()); + } + + @Test + @Tag("boundary") + void paymentAmountExactlyEqualToBalance() { + paymentRequest.setPayment_amount("100.0"); + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 1)).thenReturn(100.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(List.of()); + + ResponseEntity result = transactController.transfer(paymentRequest, session); + + assertEquals(HttpStatus.OK, result.getStatusCode()); + Map response = (Map) result.getBody(); + assertEquals("Payment Processed Successfully!", response.get("message")); + + verify(accountRepository).changeAccountsBalanceById(0.0, 1); + } + + @Test + @Tag("invalid") + void nullBeneficiaryField() { + paymentRequest.setBeneficiary(null); + + assertThrows(NullPointerException.class, () -> { + transactController.transfer(paymentRequest, session); + }); + } + + @Test + @Tag("invalid") + void nullAccountNumberField() { + paymentRequest.setAccount_number(null); + + assertThrows(NullPointerException.class, () -> { + transactController.transfer(paymentRequest, session); + }); + } + + @Test + @Tag("invalid") + void nullAccountIdField() { + paymentRequest.setAccount_id(null); + + assertThrows(NullPointerException.class, () -> { + transactController.transfer(paymentRequest, session); + }); + } + + @Test + @Tag("invalid") + void nullPaymentAmountField() { + paymentRequest.setPayment_amount(null); + + assertThrows(NullPointerException.class, () -> { + transactController.transfer(paymentRequest, session); + }); + } + + @Test + @Tag("boundary") + void verySmallPositivePaymentAmount() { + paymentRequest.setPayment_amount("0.01"); + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 1)).thenReturn(1.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(List.of()); + + ResponseEntity result = transactController.transfer(paymentRequest, session); + + assertEquals(HttpStatus.OK, result.getStatusCode()); + Map response = (Map) result.getBody(); + assertEquals("Payment Processed Successfully!", response.get("message")); + + verify(accountRepository).changeAccountsBalanceById(0.99, 1); + } + + @Test + @Tag("integration") + void fullPaymentProcessingWorkflow() { + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 1)).thenReturn(500.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(List.of()); + + ResponseEntity result = transactController.transfer(paymentRequest, session); + + assertEquals(HttpStatus.OK, result.getStatusCode()); + Map response = (Map) result.getBody(); + assertEquals("Payment Processed Successfully!", response.get("message")); + assertNotNull(response.get("accounts")); + + verify(accountRepository).getAccountBalance(123, 1); + verify(accountRepository).changeAccountsBalanceById(399.5, 1); + verify(accountRepository).getUserAccountsById(123); + verify(paymentRepository).makePayment(eq(1), eq("John Doe"), eq("9876543210"), + eq(100.5), eq("Test payment"), eq("success"), eq("Payment Processed Successfully!"), any(LocalDateTime.class)); + verify(transactRepository).logTransaction(eq(1), eq("Payment"), eq(100.5), + eq("online"), eq("success"), eq("Payment Transaction Successfull"), any(LocalDateTime.class)); + } + +} \ No newline at end of file diff --git a/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransfer758Test.java b/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransfer758Test.java new file mode 100644 index 0000000..904076f --- /dev/null +++ b/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransfer758Test.java @@ -0,0 +1,344 @@ + +// ********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 + +[ + { + "vulnerability": "CWE-862: Missing Authorization", + "issue": "The transfer method lacks proper authorization checks to verify if the authenticated user owns the account they are attempting to withdraw from. An attacker could potentially access and withdraw from other users' accounts by manipulating the account_id parameter.", + "solution": "Add authorization validation to ensure the account belongs to the authenticated user before processing the withdrawal: if (!accountRepository.isAccountOwnedByUser(user_id, account_id)) { return ResponseEntity.status(HttpStatus.FORBIDDEN).body(\"Access denied\"); }" + }, + { + "vulnerability": "CWE-209: Information Exposure Through Error Messages", + "issue": "The method returns detailed error messages that could expose sensitive information about account balances, transaction states, and internal system behavior to potential attackers.", + "solution": "Return generic error messages to clients and log detailed information server-side: return ResponseEntity.badRequest().body(\"Transaction failed\"); while logging specific details in application logs for debugging." + }, + { + "vulnerability": "CWE-362: Race Condition", + "issue": "The balance check and withdrawal operations are not atomic, creating a race condition where multiple concurrent requests could lead to overdrafts or inconsistent account states.", + "solution": "Implement database-level transactions with appropriate isolation levels or use pessimistic locking: @Transactional(isolation = Isolation.SERIALIZABLE) and consider using SELECT FOR UPDATE in the balance check query." + }, + { + "vulnerability": "CWE-20: Improper Input Validation", + "issue": "The method uses Integer.parseInt() and Double.parseDouble() without proper exception handling, which could cause NumberFormatException and application crashes when malformed input is provided.", + "solution": "Add proper input validation and exception handling: try { int account_id = Integer.parseInt(accountId); double withdrawal_amount = Double.parseDouble(withdrawalAmount); } catch (NumberFormatException e) { return ResponseEntity.badRequest().body(\"Invalid input format\"); }" + }, + { + "vulnerability": "CWE-476: NULL Pointer Dereference", + "issue": "The code accesses user object from session without null checks, and variable declarations are incomplete (missing type declarations for user, user_id, currentBalance, currentDateTime), potentially causing NullPointerException at runtime.", + "solution": "Add null checks and proper variable declarations: User user = (User) session.getAttribute(\"user\"); if (user == null) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(\"Authentication required\"); } and declare all variables with proper types." + }, + { + "vulnerability": "CWE-284: Improper Access Control", + "issue": "The method lacks proper authentication verification beyond checking session attributes, and there's no validation of user permissions for the specific withdrawal operation.", + "solution": "Implement comprehensive authentication and authorization: Use Spring Security annotations like @PreAuthorize(\"hasRole('USER')\") and verify user session validity with proper timeout and security context checks." + }, + { + "vulnerability": "CWE-665: Improper Initialization", + "issue": "Variables like currentDateTime are referenced but never initialized, which will cause compilation errors and potential runtime issues if the code somehow compiles.", + "solution": "Properly initialize all variables: LocalDateTime currentDateTime = LocalDateTime.now(); and ensure all required variables are declared with appropriate types before use." + } +] +,Scenario 1: Successful Withdrawal with Valid Parameters + +Details: + TestName: successfulWithdrawalWithValidParameters + Description: This test verifies that a withdrawal transaction is processed successfully when valid withdrawal amount and account ID are provided, the user has sufficient funds, and all required session attributes are present. + +Execution: + Arrange: Set up a mock HttpSession with a valid User object, mock AccountRepository to return sufficient balance (e.g., 1000.0), mock TransactRepository for logging, and create a request map with valid withdrawal_amount (500.0) and account_id (123). + Act: Call the transfer method with the prepared request map and mock session. + Assert: Verify that the response status is OK (200), the response body contains success message "Withdrawal Successfull!", and the accounts information is included in the response. + +Validation: + This assertion verifies that the withdrawal operation completes successfully when all conditions are met. The test ensures that the business logic correctly processes valid withdrawal requests and returns appropriate success responses, which is critical for user experience and transaction reliability. + +Scenario 2: Empty Withdrawal Amount Parameter + +Details: + TestName: emptyWithdrawalAmountParameter + Description: This test checks the method's behavior when the withdrawal_amount parameter is empty or null, ensuring proper input validation and error handling. + +Execution: + Arrange: Create a request map with empty withdrawal_amount ("") and valid account_id (123). Set up a mock HttpSession with a valid User object. + 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 validates that the method properly handles missing required parameters and provides meaningful error messages to the client. Input validation is crucial for preventing invalid operations and maintaining data integrity. + +Scenario 3: Empty Account ID Parameter + +Details: + TestName: emptyAccountIdParameter + Description: This test validates the method's response when the account_id parameter is empty, testing the input validation logic for required fields. + +Execution: + Arrange: Create a request map with valid withdrawal_amount (100.0) and empty account_id (""). Set up a mock HttpSession with a valid User object. + 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 both required parameters are properly validated and that appropriate error responses are returned when mandatory fields are missing. This validation prevents operations on invalid or non-existent accounts. + +Scenario 4: Both Parameters Empty + +Details: + TestName: bothParametersEmpty + Description: This test verifies the method's behavior when both withdrawal_amount and account_id parameters are empty, ensuring comprehensive input validation. + +Execution: + Arrange: Create a request map with empty withdrawal_amount ("") and empty account_id (""). Set up a mock HttpSession with a valid User object. + Act: Call the transfer method with the request map containing both empty parameters. + 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 correctly handles scenarios where multiple required fields are missing simultaneously. It ensures consistent error handling regardless of which specific fields are empty. + +Scenario 5: Zero Withdrawal Amount + +Details: + TestName: zeroWithdrawalAmount + Description: This test checks that the method properly rejects withdrawal requests with zero amount, implementing business rule validation for meaningful transaction amounts. + +Execution: + Arrange: Create a request map with withdrawal_amount ("0") and valid account_id (123). Set up a mock HttpSession with a valid User object. + Act: Call the transfer method with 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 validates that the business logic prevents meaningless transactions with zero amounts. Such validation is important for maintaining transaction integrity and preventing unnecessary processing of invalid operations. + +Scenario 6: Insufficient Funds for Withdrawal + +Details: + TestName: insufficientFundsForWithdrawal + Description: This test verifies that the method correctly handles withdrawal requests where the requested amount exceeds the available account balance, ensuring proper fund validation. + +Execution: + Arrange: Set up mock AccountRepository to return a balance of 100.0, mock TransactRepository for failed transaction logging, create a request map with withdrawal_amount (500.0) and account_id (123), and set up HttpSession with valid User object. + Act: Call the transfer method with withdrawal amount exceeding available balance. + 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 method properly enforces account balance constraints and prevents overdraft situations. The test also validates that failed transactions are properly logged for audit purposes, which is essential for financial transaction tracking. + +Scenario 7: Successful Balance Update After Withdrawal + +Details: + TestName: successfulBalanceUpdateAfterWithdrawal + Description: This test verifies that the account balance is correctly updated after a successful withdrawal and that the new balance calculation is accurate. + +Execution: + Arrange: Set up mock AccountRepository with initial balance of 1000.0, mock the changeAccountsBalanceById method, create request map with withdrawal_amount (300.0) and account_id (123), and set up HttpSession with valid User. + Act: Call the transfer method with valid parameters. + Assert: Verify that changeAccountsBalanceById is called with the correct new balance (700.0) and account ID, and confirm the response indicates success. + +Validation: + This test validates that the mathematical calculation for the new balance is correct and that the repository method is called with the appropriate parameters. Accurate balance management is critical for financial applications to maintain account integrity. + +Scenario 8: Transaction Logging for Successful Withdrawal + +Details: + TestName: transactionLoggingForSuccessfulWithdrawal + Description: This test ensures that successful withdrawal transactions are properly logged with correct transaction details for audit and tracking purposes. + +Execution: + Arrange: Set up mock TransactRepository, mock AccountRepository with sufficient balance (500.0), create request map with withdrawal_amount (200.0) and account_id (123), and set up HttpSession with valid User. + Act: Call the transfer method with valid parameters. + Assert: Verify that logTransaction is called with correct parameters: account_id (123), transaction type ("Withdrawal"), amount (200.0), channel ("online"), status ("success"), and success message. + +Validation: + This assertion confirms that all successful transactions are properly recorded with accurate details. Transaction logging is essential for audit trails, compliance requirements, and customer service support in financial applications. + +Scenario 9: Transaction Logging for Failed Withdrawal + +Details: + TestName: transactionLoggingForFailedWithdrawal + Description: This test verifies that failed withdrawal attempts due to insufficient funds are properly logged with failure status and appropriate reason codes. + +Execution: + Arrange: Set up mock AccountRepository with insufficient balance (50.0), mock TransactRepository for logging, create request map with withdrawal_amount (100.0) and account_id (123), and set up HttpSession with valid User. + Act: Call the transfer method with withdrawal amount exceeding balance. + Assert: Verify that logTransaction is called with parameters: account_id (123), transaction type ("withdrawal"), amount (100.0), channel ("online"), status ("failed"), and reason "Insufficient funds.". + +Validation: + This test ensures that failed transactions are properly documented with failure reasons, which is crucial for debugging, customer support, and regulatory compliance in financial systems. + +Scenario 10: Response Contains Updated Account Information + +Details: + TestName: responseContainsUpdatedAccountInformation + Description: This test verifies that the successful withdrawal response includes updated account information, allowing clients to refresh their account data. + +Execution: + Arrange: Set up mock AccountRepository with getUserAccountsById method returning account list, mock sufficient balance (800.0), create request map with valid parameters, and set up HttpSession with User having user_id. + Act: Call the transfer method with valid withdrawal parameters. + Assert: Verify that the response contains both "message" and "accounts" keys, and that getUserAccountsById is called with the correct user_id. + +Validation: + This assertion validates that the API provides complete information in the response, allowing client applications to update their local state without making additional requests. This improves user experience and reduces network overhead. + +Scenario 11: Invalid Account ID Format + +Details: + TestName: invalidAccountIdFormat + Description: This test checks the method's behavior when the account_id parameter contains non-numeric characters that cannot be parsed to an integer. + +Execution: + Arrange: Create a request map with valid withdrawal_amount (100.0) and invalid account_id ("abc"), and set up HttpSession with valid User object. + Act: Call the transfer method with invalid account ID format. + Assert: Verify that a NumberFormatException is thrown or handled appropriately by the method. + +Validation: + This test ensures that the method handles data type conversion errors gracefully and doesn't crash when invalid input formats are provided. Proper exception handling is crucial for application stability. + +Scenario 12: Invalid Withdrawal Amount Format + +Details: + TestName: invalidWithdrawalAmountFormat + Description: This test validates the method's response when the withdrawal_amount parameter contains non-numeric characters that cannot be parsed to a double. + +Execution: + Arrange: Create a request map with invalid withdrawal_amount ("invalid") and valid account_id (123), and set up HttpSession with valid User object. + Act: Call the transfer method with invalid withdrawal amount format. + Assert: Verify that a NumberFormatException is thrown or handled appropriately by the method. + +Validation: + This assertion ensures that input parsing errors are properly managed and don't cause application crashes. Robust error handling for data conversion is essential for maintaining application reliability. + +Scenario 13: Null HttpSession + +Details: + TestName: nullHttpSession + Description: This test verifies the method's behavior when a null HttpSession is passed, testing the session validation and error handling. + +Execution: + Arrange: Create a request map with valid withdrawal_amount (100.0) and account_id (123), and pass null as the HttpSession parameter. + Act: Call the transfer method with null session. + Assert: Verify that a NullPointerException is thrown or handled appropriately when trying to access session attributes. + +Validation: + This test ensures that the method properly handles cases where session information is unavailable, which could occur due to session timeout or other session management issues. + +Scenario 14: Null User in Session + +Details: + TestName: nullUserInSession + Description: This test checks the method's response when the HttpSession exists but doesn't contain a valid User object, simulating unauthenticated requests. + +Execution: + Arrange: Create a mock HttpSession that returns null when getAttribute("user") is called, and create a request map with valid parameters. + Act: Call the transfer method with session containing no user. + Assert: Verify that a NullPointerException is thrown or handled appropriately when trying to access user properties. + +Validation: + This assertion validates that the method properly handles authentication state and prevents unauthorized access to withdrawal functionality, which is critical for security. + +Scenario 15: Negative Withdrawal Amount + +Details: + TestName: negativeWithdrawalAmount + Description: This test verifies the method's behavior when a negative withdrawal amount is provided, ensuring that only positive amounts are accepted. + +Execution: + Arrange: Create a request map with negative withdrawal_amount ("-100.0") and valid account_id (123), set up HttpSession with valid User, and mock AccountRepository. + Act: Call the transfer method with negative withdrawal amount. + Assert: Verify the method's response to negative amounts (behavior may vary based on business rules - could be treated as invalid input or processed differently). + +Validation: + This test ensures that the business logic properly handles edge cases with negative values and enforces appropriate rules for withdrawal amounts, preventing potential system exploitation. + +*/ + +// ********RoostGPT******** +package com.beko.DemoBank_v1.controllers; + +import com.beko.DemoBank_v1.models.PaymentRequest; +import com.beko.DemoBank_v1.models.TransferRequest; +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.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +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 javax.servlet.http.HttpSession; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + +@Controller +@RequestMapping("/transact") +public class TransactControllerTransfer758Test { + + @Autowired + private AccountRepository accountRepository; + + @Autowired + private PaymentRepository paymentRepository; + + @Autowired + private TransactRepository transactRepository; + + private User user; + + private int user_id; + + private double currentBalance; + + private double newBalance; + + private LocalDateTime currentDateTime = LocalDateTime.now(); + + @PostMapping("/transfer") + public ResponseEntity transfer(@RequestBody Map requestMap, HttpSession session) { + String withdrawalAmount = requestMap.get("withdrawal_amount"); + String accountId = requestMap.get("account_id"); + + if (withdrawalAmount.isEmpty() || accountId.isEmpty()) { + return ResponseEntity.badRequest().body("Account withdrawing from and withdrawal amount cannot be empty!"); + } + + int account_id = Integer.parseInt(accountId); + double withdrawal_amount = Double.parseDouble(withdrawalAmount); + + if (withdrawal_amount == 0) { + return ResponseEntity.badRequest().body("Withdrawal amount cannot be 0 value."); + } + + user = (User) session.getAttribute("user"); + + user_id = Integer.parseInt(user.getUser_id()); + currentBalance = accountRepository.getAccountBalance(user_id, account_id); + + if (currentBalance < withdrawal_amount) { + + transactRepository.logTransaction(account_id, "withdrawal", withdrawal_amount, "online", "failed", + "Insufficient funds.", currentDateTime); + return ResponseEntity.badRequest().body("You have insufficient Funds to perform this transfer."); + } + + double newBalance = currentBalance - withdrawal_amount; + + accountRepository.changeAccountsBalanceById(newBalance, account_id); + + transactRepository.logTransaction(account_id, "Withdrawal", withdrawal_amount, "online", "success", + "Withdrawal Transaction Successfull", currentDateTime); + Map response = new HashMap<>(); + response.put("message", "Withdrawal Successfull!"); + + response.put("accounts", accountRepository.getUserAccountsById(user_id)); + return ResponseEntity.ok(response); + } + +} \ 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..e55ce5e --- /dev/null +++ b/Online-Banking-App-Spring-Boot/src/test/java/com/beko/DemoBank_v1/controllers/TransactControllerTransferTest.java @@ -0,0 +1,534 @@ + +// ********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 + +[ + { + "vulnerability": "CWE-862 Missing Authorization", + "issue": "The transfer method lacks proper authorization checks to verify if the authenticated user owns the source and target accounts. An attacker could potentially transfer funds between accounts they don't own by manipulating the transferFromId and transferToId parameters.", + "solution": "Add authorization checks to verify that both source and target accounts belong to the authenticated user before proceeding with the transfer. Use accountRepository.verifyAccountOwnership(user_id, accountId) for both accounts." + }, + { + "vulnerability": "CWE-362 Race Condition", + "issue": "The transfer operation involves multiple separate database operations (balance checks, updates) without proper transaction management. This creates a race condition where concurrent transfers could result in inconsistent account balances or allow overdrafts.", + "solution": "Wrap the entire transfer operation in a database transaction using @Transactional annotation or explicit transaction management to ensure atomicity and consistency of the balance updates." + }, + { + "vulnerability": "CWE-20 Improper Input Validation", + "issue": "The code uses Integer.parseInt() and Double.parseDouble() without proper exception handling. Malformed input could cause NumberFormatException, leading to application crashes or unexpected behavior.", + "solution": "Implement proper input validation with try-catch blocks around parsing operations, and validate that numeric inputs are within acceptable ranges for account IDs and transfer amounts." + }, + { + "vulnerability": "CWE-476 NULL Pointer Dereference", + "issue": "The code accesses session.getAttribute('user') and calls methods on the user object without null checks. If the session is invalid or the user object is null, this will cause a NullPointerException.", + "solution": "Add null checks for both the session and user object before accessing their properties. Return appropriate error responses if authentication state is invalid." + }, + { + "vulnerability": "CWE-200 Information Exposure", + "issue": "Error messages and response bodies may leak sensitive information about account balances, internal system structure, or database operations to unauthorized users.", + "solution": "Implement generic error messages for client responses while logging detailed error information server-side. Avoid exposing internal system details or account information in error responses." + }, + { + "vulnerability": "CWE-134 Use of Externally-Controlled Format String", + "issue": "The variable 'currentDateTime' is referenced but not declared or initialized, which could lead to compilation errors or runtime exceptions if not properly handled elsewhere in the code.", + "solution": "Declare and initialize currentDateTime properly using LocalDateTime.now() within the method scope, or ensure it's properly injected as a dependency with appropriate validation." + }, + { + "vulnerability": "CWE-209 Information Exposure Through Error Messages", + "issue": "Database operations and transaction logging occur without proper exception handling, potentially exposing sensitive database schema information or internal system details through stack traces.", + "solution": "Implement comprehensive exception handling around all database operations, log exceptions securely server-side, and return generic error messages to clients while preserving audit trails." + } +] +,Scenario 1: Valid Transfer Between Different Accounts + +Details: + TestName: validTransferBetweenDifferentAccounts + Description: Test successful transfer between two different accounts when all required fields are provided, accounts are different, transfer amount is greater than zero, and source account has sufficient balance. + +Execution: + Arrange: Create a valid TransferRequest with different source and target accounts, set up HttpSession with a valid User object, mock accountRepository to return sufficient balance for source account and any balance for target account, mock transactRepository and accountRepository methods. + Act: Call the transfer method with the valid TransferRequest and mocked HttpSession. + Assert: Verify that ResponseEntity returns HTTP 200 OK status, response body contains success message "Transfer completed successfully.", response contains updated accounts list, verify accountRepository.changeAccountsBalanceById is called twice with correct new balances, verify transactRepository.logTransaction is called once with success status. + +Validation: + This test verifies the happy path scenario where all business rules are satisfied and the transfer completes successfully. It ensures that balances are correctly updated for both accounts and the transaction is properly logged with success status. + +Scenario 2: Empty Source Account Field + +Details: + TestName: emptySourceAccountField + Description: Test that the method returns a bad request response when the source account field is empty or null in the transfer request. + +Execution: + Arrange: Create a TransferRequest with empty string for source account, valid target account and amount, set up HttpSession with valid User object. + Act: Call the transfer method with the TransferRequest containing empty source account. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request status and response body contains the message "The account transferring from and to along with the amount cannot be empty!". + +Validation: + This test ensures proper input validation for required fields. It verifies that the system prevents transfer operations when essential account information is missing, maintaining data integrity and providing clear error feedback. + +Scenario 3: Empty Target Account Field + +Details: + TestName: emptyTargetAccountField + Description: Test that the method returns a bad request response when the target account field is empty or null in the transfer request. + +Execution: + Arrange: Create a TransferRequest with valid source account, empty string for target account, and valid amount, set up HttpSession with valid User object. + Act: Call the transfer method with the TransferRequest containing empty target account. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request status and response body contains the message "The account transferring from and to along with the amount cannot be empty!". + +Validation: + This test validates that the system properly handles missing target account information and prevents incomplete transfer requests from being processed, ensuring all necessary transfer details are provided. + +Scenario 4: Empty Transfer Amount Field + +Details: + TestName: emptyTransferAmountField + Description: Test that the method returns a bad request response when the transfer amount field is empty or null in the transfer request. + +Execution: + Arrange: Create a TransferRequest with valid source and target accounts but empty string for amount, set up HttpSession with valid User object. + Act: Call the transfer method with the TransferRequest containing empty amount. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request status and response body contains the message "The account transferring from and to along with the amount cannot be empty!". + +Validation: + This test ensures that transfer operations cannot proceed without a specified amount, preventing invalid financial transactions and maintaining the integrity of the transfer process. + +Scenario 5: Transfer to Same Account + +Details: + TestName: transferToSameAccount + Description: Test that the method prevents transfers between the same account by returning a bad request response when source and target account IDs are identical. + +Execution: + Arrange: Create a TransferRequest with identical source and target account IDs (e.g., both set to "123"), valid amount, set up HttpSession with valid User object. + Act: Call the transfer method with the TransferRequest containing same account IDs. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request status and response body contains the message "Cannot Transfer Into The Same Account, Please select the appropriate account to perform transfer.". + +Validation: + This test validates a crucial business rule that prevents self-transfers, which would be meaningless operations. It ensures the system maintains logical consistency in transfer operations. + +Scenario 6: Zero Transfer Amount + +Details: + TestName: zeroTransferAmount + Description: Test that the method rejects transfer requests with zero amount by returning a bad request response. + +Execution: + Arrange: Create a TransferRequest with valid different source and target accounts but amount set to "0", set up HttpSession with valid User object. + Act: Call the transfer method with the TransferRequest containing zero amount. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request status and response body contains the message "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, preventing zero-value transactions that would not result in any actual fund movement and could potentially cause confusion in transaction records. + +Scenario 7: Insufficient Funds in Source Account + +Details: + TestName: insufficientFundsInSourceAccount + Description: Test that the method handles insufficient funds scenario by returning bad request response and logging a failed transaction when source account balance is less than transfer amount. + +Execution: + Arrange: Create a TransferRequest with valid different accounts and amount, set up HttpSession with valid User object, mock accountRepository.getAccountBalance to return balance less than transfer amount, mock transactRepository.logTransaction method. + Act: Call the transfer method with the TransferRequest where source account has insufficient balance. + Assert: Verify that ResponseEntity returns HTTP 400 Bad Request status, response body contains "You have insufficient Funds to perform this transfer.", verify transactRepository.logTransaction is called with "failed" status and "Insufficient funds." reason, verify no balance updates occur. + +Validation: + This test validates the financial safety mechanism that prevents overdrafts and ensures users cannot transfer more money than they have available. It also verifies proper transaction logging for failed attempts. + +Scenario 8: Valid Transfer with Exact Balance Amount + +Details: + TestName: validTransferWithExactBalanceAmount + Description: Test successful transfer when the transfer amount exactly equals the source account balance, resulting in zero balance in source account. + +Execution: + Arrange: Create a TransferRequest with valid different accounts, set transfer amount equal to source account balance, set up HttpSession with valid User object, mock accountRepository to return balance equal to transfer amount for source account, mock all repository methods. + Act: Call the transfer method with the TransferRequest where transfer amount equals source balance. + Assert: Verify that ResponseEntity returns HTTP 200 OK status, verify source account balance becomes zero, verify target account balance increases by transfer amount, verify success transaction logging. + +Validation: + This test ensures the system correctly handles edge case where user transfers their entire account balance, verifying mathematical accuracy in balance calculations and proper handling of zero balance scenarios. + +Scenario 9: Transfer with Decimal Amount + +Details: + TestName: transferWithDecimalAmount + Description: Test that the method correctly processes transfer requests with decimal amounts and performs accurate floating-point calculations. + +Execution: + Arrange: Create a TransferRequest with valid different accounts and decimal amount (e.g., "150.75"), set up HttpSession with valid User object, mock accountRepository with sufficient balance, mock all repository methods. + Act: Call the transfer method with the TransferRequest containing decimal transfer amount. + Assert: Verify that ResponseEntity returns HTTP 200 OK status, verify correct decimal calculations for both account balances, verify successful transaction logging with correct decimal amount. + +Validation: + This test ensures the system properly handles monetary calculations with decimal precision, which is crucial for financial applications to maintain accuracy in cent-level transactions. + +Scenario 10: Transfer with Large Amount + +Details: + TestName: transferWithLargeAmount + Description: Test that the method can handle transfers with large monetary amounts without overflow or precision issues. + +Execution: + Arrange: Create a TransferRequest with valid different accounts and large amount (e.g., "999999.99"), set up HttpSession with valid User object, mock accountRepository with sufficient large balance, mock all repository methods. + Act: Call the transfer method with the TransferRequest containing large transfer amount. + Assert: Verify that ResponseEntity returns HTTP 200 OK status, verify correct handling of large numbers in balance calculations, verify all repository methods are called with correct large amount values. + +Validation: + This test validates the system's ability to handle high-value transactions that might occur in business or premium banking scenarios, ensuring numerical stability and accuracy. + +Scenario 11: Invalid Number Format for Source Account + +Details: + TestName: invalidNumberFormatForSourceAccount + Description: Test that the method handles NumberFormatException when source account contains non-numeric characters. + +Execution: + Arrange: Create a TransferRequest with non-numeric source account (e.g., "abc"), valid target account and amount, set up HttpSession with valid User object. + Act: Call the transfer method with the TransferRequest containing invalid source account format. + Assert: Verify that NumberFormatException is thrown during Integer.parseInt(transfer_from) execution, or if handled, verify appropriate error response. + +Validation: + This test ensures the system properly handles invalid input data and prevents runtime exceptions that could crash the application when users provide malformed account numbers. + +Scenario 12: Invalid Number Format for Transfer Amount + +Details: + TestName: invalidNumberFormatForTransferAmount + Description: Test that the method handles NumberFormatException when transfer amount contains non-numeric characters. + +Execution: + Arrange: Create a TransferRequest with valid accounts but non-numeric amount (e.g., "abc.def"), set up HttpSession with valid User object. + Act: Call the transfer method with the TransferRequest containing invalid amount format. + Assert: Verify that NumberFormatException is thrown during Double.parseDouble(transfer_amount) execution, or if handled, verify appropriate error response. + +Validation: + This test validates input sanitization for monetary amounts, ensuring that only valid numeric values can be processed as transfer amounts and preventing application errors from invalid input. + +Scenario 13: Null User in Session + +Details: + TestName: nullUserInSession + Description: Test that the method handles scenarios where no user is stored in the HTTP session. + +Execution: + Arrange: Create a TransferRequest with valid data, set up HttpSession with null user attribute, mock repository methods. + Act: Call the transfer method with valid TransferRequest but null user in session. + Assert: Verify that NullPointerException is thrown when trying to access user.getUser_id(), or if handled, verify appropriate authentication error response. + +Validation: + This test ensures proper session management and authentication validation, preventing unauthorized access to transfer functionality when user session is invalid or expired. + +Scenario 14: User ID Parsing Exception + +Details: + TestName: userIdParsingException + Description: Test that the method handles NumberFormatException when user ID from session cannot be parsed to integer. + +Execution: + Arrange: Create a TransferRequest with valid data, create User object with non-numeric user_id, set up HttpSession with this User object, mock repository methods. + Act: Call the transfer method with valid request but invalid user ID format. + Assert: Verify that NumberFormatException is thrown during Integer.parseInt(user.getUser_id()), or if handled, verify appropriate error response. + +Validation: + This test validates data integrity in user session management, ensuring that corrupted user data doesn't cause application failures during transfer operations. + +Scenario 15: Repository Method Failure + +Details: + TestName: repositoryMethodFailure + Description: Test that the method handles exceptions thrown by repository methods during database operations. + +Execution: + Arrange: Create a TransferRequest with valid data, set up HttpSession with valid User object, mock accountRepository.getAccountBalance to throw RuntimeException, mock other repository methods. + Act: Call the transfer method with valid request but failing repository operation. + Assert: Verify that the exception is properly handled or propagated, verify that no partial updates occur if one repository operation fails. + +Validation: + This test ensures proper error handling and transaction integrity when database operations fail, preventing data corruption and providing appropriate error responses to users. + +Scenario 16: Successful Transfer with Multiple Repository Interactions + +Details: + TestName: successfulTransferWithMultipleRepositoryInteractions + Description: Test the complete flow of successful transfer verifying all repository method calls occur in correct sequence with correct parameters. + +Execution: + Arrange: Create a TransferRequest with valid different accounts and amount, set up HttpSession with valid User object, mock all repository methods with appropriate return values, capture method calls. + Act: Call the transfer method with valid TransferRequest. + Assert: Verify that accountRepository.getAccountBalance is called twice (for source and target accounts), verify accountRepository.changeAccountsBalanceById is called twice with correct new balances, verify transactRepository.logTransaction is called once with success parameters, verify accountRepository.getUserAccountsById is called once, verify correct order of operations. + +Validation: + This comprehensive test validates the entire transfer workflow, ensuring all necessary database operations are performed in the correct sequence with accurate data, maintaining system integrity and consistency. + +*/ + +// ********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.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.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.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; + + @BeforeEach + void setUp() { + mockUser = new User(); + mockUser.setUser_id("123"); + + transferRequest = new TransferRequest(); + transferRequest.setSourceAccount("100"); + transferRequest.setTargetAccount("200"); + transferRequest.setAmount("500.0"); + } + + @Test + @Tag("valid") + void validTransferBetweenDifferentAccounts() { + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 100)).thenReturn(1000.0); + when(accountRepository.getAccountBalance(123, 200)).thenReturn(500.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(List.of()); + ResponseEntity response = transactController.transfer(transferRequest, session); + assertEquals(HttpStatus.OK, response.getStatusCode()); + Map responseBody = (Map) response.getBody(); + assertEquals("Transfer completed successfully.", responseBody.get("message")); + assertNotNull(responseBody.get("accounts")); + verify(accountRepository).changeAccountsBalanceById(500.0, 100); + verify(accountRepository).changeAccountsBalanceById(1000.0, 200); + verify(transactRepository).logTransaction(eq(100), eq("Transfer"), eq(500.0), eq("online"), eq("success"), eq("Transfer Transaction Successfull"), any(LocalDateTime.class)); + } + + @Test + @Tag("invalid") + void emptySourceAccountField() { + transferRequest.setSourceAccount(""); + 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 emptyTargetAccountField() { + transferRequest.setTargetAccount(""); + 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 emptyTransferAmountField() { + 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("100"); + transferRequest.setTargetAccount("100"); + 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 zeroTransferAmount() { + 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 insufficientFundsInSourceAccount() { + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 100)).thenReturn(300.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(100), eq("transfer"), eq(500.0), eq("online"), eq("failed"), eq("Insufficient funds."), any(LocalDateTime.class)); + verify(accountRepository, never()).changeAccountsBalanceById(anyDouble(), anyInt()); + } + + @Test + @Tag("boundary") + void validTransferWithExactBalanceAmount() { + transferRequest.setAmount("1000.0"); + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 100)).thenReturn(1000.0); + when(accountRepository.getAccountBalance(123, 200)).thenReturn(500.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(List.of()); + 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, 100); + verify(accountRepository).changeAccountsBalanceById(1500.0, 200); + } + + @Test + @Tag("valid") + void transferWithDecimalAmount() { + transferRequest.setAmount("150.75"); + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 100)).thenReturn(1000.0); + when(accountRepository.getAccountBalance(123, 200)).thenReturn(500.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(List.of()); + 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(849.25, 100); + verify(accountRepository).changeAccountsBalanceById(650.75, 200); + verify(transactRepository).logTransaction(eq(100), eq("Transfer"), eq(150.75), eq("online"), eq("success"), + eq("Transfer Transaction Successfull"), any(LocalDateTime.class)); + } + + @Test + @Tag("boundary") + void transferWithLargeAmount() { + transferRequest.setAmount("999999.99"); + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 100)).thenReturn(1000000.0); + when(accountRepository.getAccountBalance(123, 200)).thenReturn(500.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(List.of()); + 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.01, 100); + verify(accountRepository).changeAccountsBalanceById(1000499.99, 200); + verify(transactRepository).logTransaction(eq(100), eq("Transfer"), eq(999999.99), eq("online"), eq("success"), + eq("Transfer Transaction Successfull"), any(LocalDateTime.class)); + } + + @Test + @Tag("invalid") + void invalidNumberFormatForSourceAccount() { + transferRequest.setSourceAccount("abc"); + assertThrows(NumberFormatException.class, () -> { + transactController.transfer(transferRequest, session); + }); + } + + @Test + @Tag("invalid") + void invalidNumberFormatForTransferAmount() { + transferRequest.setAmount("abc.def"); + assertThrows(NumberFormatException.class, () -> { + transactController.transfer(transferRequest, session); + }); + } + + @Test + @Tag("invalid") + void nullUserInSession() { + when(session.getAttribute("user")).thenReturn(null); + assertThrows(NullPointerException.class, () -> { + transactController.transfer(transferRequest, session); + }); + } + + @Test + @Tag("invalid") + void userIdParsingException() { + User invalidUser = new User(); + invalidUser.setUser_id("invalid_id"); + when(session.getAttribute("user")).thenReturn(invalidUser); + assertThrows(NumberFormatException.class, () -> { + transactController.transfer(transferRequest, session); + }); + } + + @Test + @Tag("invalid") + void repositoryMethodFailure() { + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 100)).thenThrow(new RuntimeException("Database error")); + assertThrows(RuntimeException.class, () -> { + transactController.transfer(transferRequest, session); + }); + verify(accountRepository, never()).changeAccountsBalanceById(anyDouble(), anyInt()); + verify(transactRepository, never()).logTransaction(anyInt(), anyString(), anyDouble(), anyString(), anyString(), anyString(), any(LocalDateTime.class)); + } + + @Test + @Tag("integration") + void successfulTransferWithMultipleRepositoryInteractions() { + when(session.getAttribute("user")).thenReturn(mockUser); + when(accountRepository.getAccountBalance(123, 100)).thenReturn(1000.0); + when(accountRepository.getAccountBalance(123, 200)).thenReturn(500.0); + when(accountRepository.getUserAccountsById(123)).thenReturn(List.of()); + ResponseEntity response = transactController.transfer(transferRequest, session); + assertEquals(HttpStatus.OK, response.getStatusCode()); + verify(accountRepository, times(1)).getAccountBalance(123, 100); + verify(accountRepository, times(1)).getAccountBalance(123, 200); + verify(accountRepository, times(1)).changeAccountsBalanceById(500.0, 100); + verify(accountRepository, times(1)).changeAccountsBalanceById(1000.0, 200); + verify(transactRepository, times(1)).logTransaction(eq(100), eq("Transfer"), eq(500.0), eq("online"), eq("success"), eq("Transfer Transaction Successfull"), any(LocalDateTime.class)); + verify(accountRepository, times(1)).getUserAccountsById(123); + } + +} \ No newline at end of file From 51531befeb197bf7abab0a420ceb174660ed8388 Mon Sep 17 00:00:00 2001 From: roost-io Date: Thu, 26 Mar 2026 06:58:35 +0000 Subject: [PATCH 2/2] Improved Unit test generated by RoostGPT Using AI Model global.anthropic.claude-sonnet-4-20250514-v1:0, for user feedback: -\sAdd\smore\scomments\sto\sthe\stest --- .../TransactControllerDepositTest.java | 135 +++++++++++++----- 1 file changed, 99 insertions(+), 36 deletions(-) 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 index 9be658d..fc13862 100644 --- 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 @@ -216,9 +216,12 @@ Validation: This assertion ensures that clients receive updated account information after deposit operations. The test validates that the response provides complete information needed for UI updates or further operations. + +roost_feedback [26/03/2026, 6:56:05 AM]:-\sAdd\smore\scomments\sto\sthe\stest */ // ********RoostGPT******** + package com.beko.DemoBank_v1.controllers; import com.beko.DemoBank_v1.models.User; @@ -274,33 +277,42 @@ class TransactControllerDepositTest { @BeforeEach void setUp() { + // Initialize the request map before each test to ensure clean state requestMap = new HashMap<>(); } @Test @Tag("valid") void successfulDepositWithValidInputs() { - // Arrange + // Test scenario: Valid deposit operation with proper amount and account ID + // This test verifies that a successful deposit updates the account balance, + // logs the transaction, and returns the correct response format + + // Arrange - Setup test data with valid deposit amount and account ID requestMap.put("deposit_amount", "500.0"); requestMap.put("account_id", "1"); + // Mock user session and account operations when(session.getAttribute("user")).thenReturn(user); when(user.getUser_id()).thenReturn("123"); when(accountRepository.getAccountBalance(123, 1)).thenReturn(1000.0); when(accountRepository.getUserAccountsById(123)).thenReturn(List.of()); - // Act + // Act - Execute the deposit operation ResponseEntity response = transactController.deposit(requestMap, session); - // Assert + // Assert - Verify successful response and proper system behavior assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); + // Verify response body contains expected success message and accounts Map responseBody = (Map) response.getBody(); assertEquals("Amount Deposited Successfully.", responseBody.get("message")); assertNotNull(responseBody.get("accounts")); + // Verify account balance was updated correctly (1000 + 500 = 1500) verify(accountRepository).changeAccountsBalanceById(1500.0, 1); + // Verify transaction was logged with correct parameters verify(transactRepository).logTransaction(eq(1), eq("deposit"), eq(500.0), eq("online"), eq("success"), eq("Deposit Transaction Successfull"), any(LocalDateTime.class)); } @@ -308,14 +320,17 @@ void successfulDepositWithValidInputs() { @Test @Tag("invalid") void badRequestWhenDepositAmountIsEmpty() { - // Arrange + // Test scenario: Invalid input with empty deposit amount + // Verifies that the system properly handles and rejects empty deposit amounts + + // Arrange - Setup request with empty deposit amount but valid account ID requestMap.put("deposit_amount", ""); requestMap.put("account_id", "1"); - // Act + // Act - Attempt deposit with invalid input ResponseEntity response = transactController.deposit(requestMap, session); - // Assert + // Assert - Verify proper error handling with BAD_REQUEST status assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); assertEquals("Deposit amount and account ID cannot be empty.", response.getBody()); } @@ -323,14 +338,17 @@ void badRequestWhenDepositAmountIsEmpty() { @Test @Tag("invalid") void badRequestWhenAccountIdIsEmpty() { - // Arrange + // Test scenario: Invalid input with empty account ID + // Ensures the system rejects requests with missing account identification + + // Arrange - Setup request with valid deposit amount but empty account ID requestMap.put("deposit_amount", "500.0"); requestMap.put("account_id", ""); - // Act + // Act - Attempt deposit with missing account ID ResponseEntity response = transactController.deposit(requestMap, session); - // Assert + // Assert - Verify rejection of invalid request assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); assertEquals("Deposit amount and account ID cannot be empty.", response.getBody()); } @@ -338,14 +356,17 @@ void badRequestWhenAccountIdIsEmpty() { @Test @Tag("invalid") void badRequestWhenBothFieldsAreEmpty() { - // Arrange + // Test scenario: Complete invalid input with both required fields empty + // Validates comprehensive input validation for deposit requests + + // Arrange - Setup request with both required fields empty requestMap.put("deposit_amount", ""); requestMap.put("account_id", ""); - // Act + // Act - Attempt deposit with completely invalid input ResponseEntity response = transactController.deposit(requestMap, session); - // Assert + // Assert - Verify proper error response for missing data assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); assertEquals("Deposit amount and account ID cannot be empty.", response.getBody()); } @@ -353,17 +374,21 @@ void badRequestWhenBothFieldsAreEmpty() { @Test @Tag("boundary") void badRequestWhenDepositAmountIsZero() { - // Arrange + // Test scenario: Boundary case testing with zero deposit amount + // Ensures business rule that deposits must be greater than zero + + // Arrange - Setup request with zero amount (boundary case) requestMap.put("deposit_amount", "0"); requestMap.put("account_id", "1"); + // Mock user session for authentication when(session.getAttribute("user")).thenReturn(user); when(user.getUser_id()).thenReturn("123"); - // Act + // Act - Attempt zero-amount deposit ResponseEntity response = transactController.deposit(requestMap, session); - // Assert + // Assert - Verify rejection of zero-amount deposits assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); assertEquals("Deposit amount cannot be zero.", response.getBody()); } @@ -371,24 +396,29 @@ void badRequestWhenDepositAmountIsZero() { @Test @Tag("valid") void successfulDepositWithDecimalAmount() { - // Arrange + // Test scenario: Valid deposit with decimal amount + // Verifies system correctly handles and processes decimal currency values + + // Arrange - Setup request with decimal deposit amount requestMap.put("deposit_amount", "150.75"); requestMap.put("account_id", "2"); + // Mock user session and account data for different user/account 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(List.of()); - // Act + // Act - Execute deposit with decimal amount ResponseEntity response = transactController.deposit(requestMap, session); - // Assert + // Assert - Verify successful processing of decimal amounts assertEquals(HttpStatus.OK, response.getStatusCode()); Map responseBody = (Map) response.getBody(); assertEquals("Amount Deposited Successfully.", responseBody.get("message")); + // Verify correct decimal calculation (1000.0 + 150.75 = 1150.75) verify(accountRepository).changeAccountsBalanceById(1150.75, 2); verify(transactRepository).logTransaction(eq(2), eq("deposit"), eq(150.75), eq("online"), eq("success"), eq("Deposit Transaction Successfull"), any(LocalDateTime.class)); @@ -397,39 +427,50 @@ void successfulDepositWithDecimalAmount() { @Test @Tag("integration") void successfulDepositUpdatesAccountBalance() { - // Arrange + // Test scenario: Integration test focusing on account balance update + // Verifies that the deposit operation correctly integrates with account repository + + // Arrange - Setup standard deposit scenario requestMap.put("deposit_amount", "500.0"); requestMap.put("account_id", "1"); + // Mock all necessary dependencies when(session.getAttribute("user")).thenReturn(user); when(user.getUser_id()).thenReturn("123"); when(accountRepository.getAccountBalance(123, 1)).thenReturn(1000.0); when(accountRepository.getUserAccountsById(123)).thenReturn(List.of()); - // Act + // Act - Execute deposit operation transactController.deposit(requestMap, session); - // Assert + // Assert - Focus on account balance integration + // Verify balance was updated with correct new amount verify(accountRepository).changeAccountsBalanceById(1500.0, 1); + // Verify current balance was retrieved before update verify(accountRepository).getAccountBalance(123, 1); } @Test @Tag("integration") void successfulDepositLogsTransaction() { - // Arrange + // Test scenario: Integration test focusing on transaction logging + // Ensures deposit operations are properly recorded in transaction history + + // Arrange - Setup deposit scenario with different amount and account requestMap.put("deposit_amount", "300.0"); requestMap.put("account_id", "3"); + // Mock dependencies with different test data when(session.getAttribute("user")).thenReturn(user); when(user.getUser_id()).thenReturn("789"); when(accountRepository.getAccountBalance(789, 3)).thenReturn(500.0); when(accountRepository.getUserAccountsById(789)).thenReturn(List.of()); - // Act + // Act - Execute deposit operation transactController.deposit(requestMap, session); - // Assert + // Assert - Focus on transaction logging integration + // Verify transaction was logged with all correct parameters verify(transactRepository).logTransaction(eq(3), eq("deposit"), eq(300.0), eq("online"), eq("success"), eq("Deposit Transaction Successfull"), any(LocalDateTime.class)); } @@ -437,14 +478,19 @@ void successfulDepositLogsTransaction() { @Test @Tag("invalid") void numberFormatExceptionWhenAccountIdIsInvalid() { - // Arrange + // Test scenario: Error handling for invalid account ID format + // Verifies system behavior when non-numeric account ID is provided + + // Arrange - Setup request with invalid account ID format requestMap.put("deposit_amount", "500.0"); - requestMap.put("account_id", "abc123"); + requestMap.put("account_id", "abc123"); // Invalid non-numeric ID + // Mock user session when(session.getAttribute("user")).thenReturn(user); when(user.getUser_id()).thenReturn("123"); - // Act & Assert + // Act & Assert - Verify NumberFormatException is thrown + // This tests the system's input validation and error handling assertThrows(NumberFormatException.class, () -> { transactController.deposit(requestMap, session); }); @@ -453,14 +499,19 @@ void numberFormatExceptionWhenAccountIdIsInvalid() { @Test @Tag("invalid") void numberFormatExceptionWhenDepositAmountIsInvalid() { - // Arrange - requestMap.put("deposit_amount", "invalid_amount"); + // Test scenario: Error handling for invalid deposit amount format + // Ensures proper exception handling when non-numeric amounts are provided + + // Arrange - Setup request with invalid deposit amount format + requestMap.put("deposit_amount", "invalid_amount"); // Invalid non-numeric amount requestMap.put("account_id", "1"); + // Mock user session when(session.getAttribute("user")).thenReturn(user); when(user.getUser_id()).thenReturn("123"); - // Act & Assert + // Act & Assert - Verify NumberFormatException for invalid amount + // Tests numeric validation for deposit amounts assertThrows(NumberFormatException.class, () -> { transactController.deposit(requestMap, session); }); @@ -469,13 +520,18 @@ void numberFormatExceptionWhenDepositAmountIsInvalid() { @Test @Tag("invalid") void nullPointerExceptionWhenUserSessionIsInvalid() { - // Arrange + // Test scenario: Error handling for invalid user session + // Verifies system behavior when user is not properly authenticated + + // Arrange - Setup request with valid data but invalid session requestMap.put("deposit_amount", "500.0"); requestMap.put("account_id", "1"); + // Mock null user session (unauthenticated user) when(session.getAttribute("user")).thenReturn(null); - // Act & Assert + // Act & Assert - Verify NullPointerException for invalid session + // This tests authentication validation in the deposit process assertThrows(NullPointerException.class, () -> { transactController.deposit(requestMap, session); }); @@ -484,27 +540,34 @@ void nullPointerExceptionWhenUserSessionIsInvalid() { @Test @Tag("integration") void successfulDepositReturnsUserAccounts() { - // Arrange + // Test scenario: Integration test for account data retrieval + // Verifies that deposit response includes updated user account information + + // Arrange - Setup deposit with mock account data requestMap.put("deposit_amount", "750.0"); requestMap.put("account_id", "4"); + // Create mock accounts list to verify return data List mockAccounts = List.of("Account1", "Account2"); + // Mock all dependencies with test data when(session.getAttribute("user")).thenReturn(user); when(user.getUser_id()).thenReturn("999"); when(accountRepository.getAccountBalance(999, 4)).thenReturn(2000.0); when(accountRepository.getUserAccountsById(999)).thenReturn(mockAccounts); - // Act + // Act - Execute deposit operation ResponseEntity response = transactController.deposit(requestMap, session); - // Assert + // Assert - Verify response includes user accounts assertEquals(HttpStatus.OK, response.getStatusCode()); + // Verify accounts are included in response body Map responseBody = (Map) response.getBody(); assertEquals(mockAccounts, responseBody.get("accounts")); + // Verify account data was retrieved verify(accountRepository).getUserAccountsById(999); } -} \ No newline at end of file +}