Add test data schedule configuration methods to ConfigManager

This commit is contained in:
Dev
2025-09-11 18:44:09 +03:00
commit a5ec70e0af
25 changed files with 8079 additions and 0 deletions

376
README.md Normal file
View File

@@ -0,0 +1,376 @@
# Dracarys - Financial API Automation Framework
A comprehensive test automation framework for testing financial APIs, including wallet and credit scoring services.
## Table of Contents
- [Features](#features)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Configuration](#configuration)
- [Project Structure](#project-structure)
- [Running Tests](#running-tests)
- [Test Categories](#test-categories)
- [Reporting](#reporting)
- [Extending the Framework](#extending-the-framework)
- [Best Practices](#best-practices)
## Features
- **API Testing**: Comprehensive testing of REST APIs
- **Performance Testing**: Load and stress testing capabilities
- **Security Testing**: Security vulnerability testing
- **Data-Driven Testing**: Support for data-driven tests
- **Parallel Execution**: Parallel test execution for faster results
- **Detailed Reporting**: Comprehensive test reports with metrics
- **Database Validation**: Ability to validate data in databases
- **Environment Configuration**: Support for multiple environments
## Prerequisites
- Java 8 or higher
- Maven 3.6 or higher
- TestNG 7.4 or higher
- RestAssured 4.3 or higher
- MySQL or PostgreSQL (for database validation)
## Installation
1. Clone the repository:
```bash
git clone https://github.com/iwasforcedtobehere/Dracarys.git
cd Dracarys
```
2. Install dependencies:
```bash
mvn clean install
```
## Configuration
### Environment Configuration
The framework supports multiple environments (dev, test, staging, prod). Configuration files are located in `src/main/resources/`.
1. **config.properties**:
```properties
# Base URI for API
baseURI=https://api.example.com
# API version
apiVersion=v1
# Environment
environment=test
# Database configuration
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/test_db
db.username=test_user
db.password=test_password
# Authentication
auth.type=bearer
auth.token=your_auth_token
```
2. **log4j2.xml**:
Configure logging levels and appenders as needed.
### Test Configuration
Test configurations are defined in `src/test/resources/testng.xml`. You can create different test suites for different purposes.
## Project Structure
```
Dracarys/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/financial/api/
│ │ │ ├── config/ # Configuration classes
│ │ │ ├── models/ # Data models
│ │ │ │ ├── wallet/ # Wallet-related models
│ │ │ │ └── credit/ # Credit-related models
│ │ │ ├── services/ # API service classes
│ │ │ └── utils/ # Utility classes
│ │ └── resources/
│ │ ├── config.properties # Configuration file
│ │ └── log4j2.xml # Logging configuration
│ └── test/
│ ├── java/
│ │ └── com/financial/api/
│ │ └── tests/ # Test classes
│ │ ├── WalletApiTest.java
│ │ ├── CreditApiTest.java
│ │ ├── WalletPerformanceTest.java
│ │ ├── CreditPerformanceTest.java
│ │ ├── WalletSecurityTest.java
│ │ └── CreditSecurityTest.java
│ └── resources/
│ └── testng.xml # Test configuration
├── pom.xml # Maven dependencies
└── README.md # This file
```
## Running Tests
### Running All Tests
```bash
mvn clean test
```
### Running Specific Test Suite
```bash
mvn clean test -Dsuite=src/test/resources/testng.xml
```
### Running Tests with Specific Groups
```bash
mvn clean test -Dgroups=smoke
mvn clean test -Dgroups=regression
mvn clean test -Dgroups=performance
mvn clean test -Dgroups=security
```
### Running Tests in Parallel
```bash
mvn clean test -Dparallel=methods -DthreadCount=5
```
## Test Categories
The framework includes several categories of tests:
### 1. Functional Tests
- **WalletApiTest**: Tests for wallet operations
- Create wallet
- Get wallet
- Update wallet
- Get wallet balance
- Create transaction
- Get transaction
- Update transaction status
- Deposit funds
- Withdraw funds
- **CreditApiTest**: Tests for credit operations
- Submit credit application
- Get credit application
- Update credit application
- Get applications by status
- Calculate credit score
- Get credit score
- Get credit score history
- Approve application
- Reject application
- Get credit products
### 2. Performance Tests
- **WalletPerformanceTest**: Performance tests for wallet operations
- Create multiple wallets
- Get multiple wallets
- Create multiple transactions
- Deposit funds performance
- Get wallet balance performance
- Get transactions performance
- Transfer funds performance
- **CreditPerformanceTest**: Performance tests for credit operations
- Submit multiple applications
- Get multiple applications
- Calculate multiple credit scores
- Get multiple credit scores
- Get credit score histories
- Get applications by status performance
- Get credit products performance
### 3. Security Tests
- **WalletSecurityTest**: Security tests for wallet operations
- SQL injection in wallet creation
- XSS in wallet creation
- Authentication bypass in wallet retrieval
- Authorization bypass in wallet retrieval
- IDOR in wallet retrieval
- SQL injection in transaction creation
- XSS in transaction creation
- Negative amount in transaction creation
- Extremely large amount in transaction creation
- Authentication bypass in transaction retrieval
- Authorization bypass in transaction retrieval
- CSRF protection in wallet update
- Rate limiting in wallet creation
- Input validation in wallet creation
- Sensitive data exposure in wallet response
- **CreditSecurityTest**: Security tests for credit operations
- SQL injection in credit application submission
- XSS in credit application submission
- Authentication bypass in credit application retrieval
- Authorization bypass in credit application retrieval
- IDOR in credit application retrieval
- SQL injection in credit score calculation
- XSS in credit score calculation
- Negative loan amount in credit application submission
- Extremely large loan amount in credit application submission
- Negative loan term in credit application submission
- Extremely large loan term in credit application submission
- Authentication bypass in credit score retrieval
- Authorization bypass in credit score retrieval
- CSRF protection in credit application update
- Rate limiting in credit application submission
- Input validation in credit application submission
- Sensitive data exposure in credit application response
- Sensitive data exposure in credit score response
## Reporting
The framework generates detailed reports for test execution:
### Console Reports
- Real-time test execution status
- Test pass/fail statistics
- Test execution time metrics
- Error logs and stack traces
### HTML Reports
- Comprehensive test reports with charts
- Test execution summary
- Detailed test results
- Performance metrics
- Security vulnerability findings
### Log Files
- Detailed execution logs
- Error logs
- Debug information
## Extending the Framework
### Adding New API Tests
1. Create a new test class in `src/test/java/com/financial/api/tests/`
2. Extend the appropriate base class if available
3. Add test methods with appropriate annotations
4. Use the existing service classes or create new ones
### Adding New API Services
1. Create a new service class in `src/main/java/com/financial/api/services/`
2. Extend the `BaseService` class
3. Implement methods for API operations
4. Use RestAssured for HTTP requests
### Adding New Data Models
1. Create a new model class in `src/main/java/com/financial/api/models/`
2. Define fields with appropriate data types
3. Add getters and setters
4. Add validation annotations if needed
### Adding New Utilities
1. Create a new utility class in `src/main/java/com/financial/api/utils/`
2. Implement utility methods
3. Make methods static if appropriate
4. Add Javadoc comments
## Best Practices
### Test Design
- Follow the Arrange-Act-Assert pattern
- Keep tests independent and isolated
- Use descriptive test names
- Add proper test documentation
- Use assertions effectively
### Code Quality
- Follow Java coding standards
- Use meaningful variable and method names
- Add proper comments and documentation
- Keep methods small and focused
- Handle exceptions properly
### Performance Testing
- Define realistic performance thresholds
- Use appropriate load levels
- Monitor system resources during tests
- Analyze performance metrics
- Identify and address bottlenecks
### Security Testing
- Test for common security vulnerabilities
- Use both positive and negative test cases
- Validate input sanitization
- Test authentication and authorization
- Check for sensitive data exposure
### Maintenance
- Regularly update dependencies
- Refactor code as needed
- Add new tests for new features
- Fix failing tests promptly
- Keep documentation up to date
## Troubleshooting
### Common Issues
1. **Test Failures Due to Environment Changes**
- Update configuration files
- Check API endpoints
- Verify authentication tokens
2. **Database Connection Issues**
- Check database credentials
- Verify database URL
- Ensure database is running
3. **Performance Test Failures**
- Check system resources
- Verify test data
- Adjust performance thresholds
4. **Security Test Failures**
- Check API security implementations
- Verify input validation
- Review authentication and authorization
### Getting Help
- Check the project documentation
- Review test code examples
- Contact the framework maintainers
- Submit issues on GitHub
## Contributing
We welcome contributions to the framework! Please follow these guidelines:
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests for new functionality
5. Ensure all tests pass
6. Submit a pull request
## License
This project is licensed under the MIT License - see the LICENSE file for details.

162
pom.xml Normal file
View File

@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.financial.api</groupId>
<artifactId>dracarys</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>Dracarys</name>
<description>Financial API Automation Framework</description>
<url>https://github.com/iwasforcedtobehere/Dracarys</url>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- Dependency Versions -->
<testng.version>7.4.0</testng.version>
<rest-assured.version>4.3.3</rest-assured.version>
<jackson.version>2.12.3</jackson.version>
<log4j.version>2.14.1</log4j.version>
<mysql-connector.version>8.0.25</mysql-connector.version>
<postgresql.version>42.2.23</postgresql.version>
<lombok.version>1.18.20</lombok.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<extentreports.version>5.0.8</extentreports.version>
<json-simple.version>1.1.1</json-simple.version>
</properties>
<dependencies>
<!-- TestNG -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
<!-- REST Assured -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>${rest-assured.version}</version>
<scope>test</scope>
</dependency>
<!-- Jackson for JSON processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- Log4j for logging -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector.version}</version>
<scope>test</scope>
</dependency>
<!-- PostgreSQL Connector -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
<scope>test</scope>
</dependency>
<!-- Lombok for boilerplate code reduction -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- Apache Commons Lang -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<!-- Extent Reports for reporting -->
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>${extentreports.version}</version>
</dependency>
<!-- JSON Simple for JSON processing -->
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>${json-simple.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Maven Compiler Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<!-- Maven Surefire Plugin for running tests -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile>
</suiteXmlFiles>
<systemPropertyVariables>
<baseURI>${baseURI}</baseURI>
<apiVersion>${apiVersion}</apiVersion>
<environment>${environment}</environment>
</systemPropertyVariables>
</configuration>
</plugin>
<!-- Maven Failsafe Plugin for integration tests -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M5</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,245 @@
package com.financial.api.config;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* Configuration Manager class to handle loading and accessing configuration properties.
*/
public class ConfigManager {
private static final Logger logger = LogManager.getLogger(ConfigManager.class);
private static ConfigManager instance;
private Properties properties;
private ConfigManager() {
properties = new Properties();
loadProperties();
}
/**
* Get the singleton instance of ConfigManager
* @return ConfigManager instance
*/
public static synchronized ConfigManager getInstance() {
if (instance == null) {
instance = new ConfigManager();
}
return instance;
}
/**
* Load properties from the configuration file
*/
private void loadProperties() {
try (InputStream input = getClass().getClassLoader().getResourceAsStream("config.properties")) {
if (input == null) {
logger.error("Unable to find config.properties");
return;
}
properties.load(input);
logger.info("Configuration loaded successfully");
} catch (IOException e) {
logger.error("Error loading configuration: {}", e.getMessage(), e);
}
}
/**
* Get property value as String
* @param key Property key
* @return Property value as String
*/
public String getProperty(String key) {
return properties.getProperty(key);
}
/**
* Get property value as String with default value
* @param key Property key
* @param defaultValue Default value if property not found
* @return Property value as String or default value
*/
public String getProperty(String key, String defaultValue) {
return properties.getProperty(key, defaultValue);
}
/**
* Get property value as Integer
* @param key Property key
* @return Property value as Integer
*/
public int getIntProperty(String key) {
try {
return Integer.parseInt(properties.getProperty(key));
} catch (NumberFormatException e) {
logger.error("Error parsing integer property for key {}: {}", key, e.getMessage(), e);
throw new RuntimeException("Invalid integer property for key: " + key, e);
}
}
/**
* Get property value as Integer with default value
* @param key Property key
* @param defaultValue Default value if property not found or invalid
* @return Property value as Integer or default value
*/
public int getIntProperty(String key, int defaultValue) {
try {
return Integer.parseInt(properties.getProperty(key, String.valueOf(defaultValue)));
} catch (NumberFormatException e) {
logger.warn("Error parsing integer property for key {}, using default value {}: {}",
key, defaultValue, e.getMessage(), e);
return defaultValue;
}
}
/**
* Get property value as Boolean
* @param key Property key
* @return Property value as Boolean
*/
public boolean getBooleanProperty(String key) {
return Boolean.parseBoolean(properties.getProperty(key));
}
/**
* Get property value as Boolean with default value
* @param key Property key
* @param defaultValue Default value if property not found
* @return Property value as Boolean or default value
*/
public boolean getBooleanProperty(String key, boolean defaultValue) {
return Boolean.parseBoolean(properties.getProperty(key, String.valueOf(defaultValue)));
}
/**
* Get API base URL
* @return API base URL
*/
public String getApiBaseUrl() {
return getProperty("api.base.url");
}
/**
* Get API version
* @return API version
*/
public String getApiVersion() {
return getProperty("api.version");
}
/**
* Get API timeout in milliseconds
* @return API timeout
*/
public int getApiTimeout() {
return getIntProperty("api.timeout", 10000);
}
/**
* Get database URL
* @return Database URL
*/
public String getDbUrl() {
return getProperty("db.url");
}
/**
* Get database username
* @return Database username
*/
public String getDbUsername() {
return getProperty("db.username");
}
/**
* Get database password
* @return Database password
*/
public String getDbPassword() {
return getProperty("db.password");
}
/**
* Get Elasticsearch host
* @return Elasticsearch host
*/
public String getElasticsearchHost() {
return getProperty("elasticsearch.host");
}
/**
* Get Elasticsearch port
* @return Elasticsearch port
*/
public int getElasticsearchPort() {
return getIntProperty("elasticsearch.port", 9200);
}
/**
* Get Couchbase host
* @return Couchbase host
*/
public String getCouchbaseHost() {
return getProperty("couchbase.host");
}
/**
* Get Couchbase username
* @return Couchbase username
*/
public String getCouchbaseUsername() {
return getProperty("couchbase.username");
}
/**
* Get Couchbase password
* @return Couchbase password
*/
public String getCouchbasePassword() {
return getProperty("couchbase.password");
}
/**
* Get Couchbase bucket name
* @return Couchbase bucket name
*/
public String getCouchbaseBucketName() {
return getProperty("couchbase.bucket.name");
}
/**
* Get test data directory
* @return Test data directory
*/
public String getTestDataDir() {
return getProperty("test.data.dir", "src/test/resources/testdata");
}
/**
* Get test report directory
* @return Test report directory
*/
public String getTestReportDir() {
return getProperty("test.report.dir", "target/test-reports");
}
/**
* Get log directory
* @return Log directory
*/
public String getLogDir() {
return getProperty("log.dir", "target/logs");
}
/**
* Get current environment
* @return Current environment (dev, staging, prod)
*/
public String getEnvironment() {
return getProperty("env", "dev");
}
}

View File

@@ -0,0 +1,459 @@
package com.financial.api.models.credit;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.LocalDate;
import java.util.UUID;
/**
* CreditApplication model representing a credit application in the financial system.
*/
public class CreditApplication {
private String id;
private String customerId;
private String applicantName;
private String applicantEmail;
private String applicantPhone;
private LocalDate dateOfBirth;
private BigDecimal monthlyIncome;
private BigDecimal loanAmount;
private int loanTermMonths;
private String loanPurpose;
private EmploymentStatus employmentStatus;
private CreditApplicationStatus status;
private Integer creditScore;
private BigDecimal approvedAmount;
private BigDecimal interestRate;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private LocalDateTime reviewedAt;
private String reviewedBy;
private String rejectionReason;
public enum EmploymentStatus {
@JsonProperty("EMPLOYED_FULL_TIME")
EMPLOYED_FULL_TIME,
@JsonProperty("EMPLOYED_PART_TIME")
EMPLOYED_PART_TIME,
@JsonProperty("SELF_EMPLOYED")
SELF_EMPLOYED,
@JsonProperty("UNEMPLOYED")
UNEMPLOYED,
@JsonProperty("RETIRED")
RETIRED,
@JsonProperty("STUDENT")
STUDENT
}
public enum CreditApplicationStatus {
@JsonProperty("DRAFT")
DRAFT,
@JsonProperty("SUBMITTED")
SUBMITTED,
@JsonProperty("UNDER_REVIEW")
UNDER_REVIEW,
@JsonProperty("APPROVED")
APPROVED,
@JsonProperty("REJECTED")
REJECTED,
@JsonProperty("CANCELLED")
CANCELLED
}
/**
* Default constructor
*/
public CreditApplication() {
this.id = UUID.randomUUID().toString();
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
this.status = CreditApplicationStatus.DRAFT;
}
/**
* Parameterized constructor
* @param customerId Customer ID
* @param applicantName Applicant name
* @param applicantEmail Applicant email
* @param applicantPhone Applicant phone
* @param dateOfBirth Date of birth
* @param monthlyIncome Monthly income
* @param loanAmount Loan amount
* @param loanTermMonths Loan term in months
* @param loanPurpose Loan purpose
* @param employmentStatus Employment status
*/
public CreditApplication(String customerId, String applicantName, String applicantEmail,
String applicantPhone, LocalDate dateOfBirth, BigDecimal monthlyIncome,
BigDecimal loanAmount, int loanTermMonths, String loanPurpose,
EmploymentStatus employmentStatus) {
this();
this.customerId = customerId;
this.applicantName = applicantName;
this.applicantEmail = applicantEmail;
this.applicantPhone = applicantPhone;
this.dateOfBirth = dateOfBirth;
this.monthlyIncome = monthlyIncome;
this.loanAmount = loanAmount;
this.loanTermMonths = loanTermMonths;
this.loanPurpose = loanPurpose;
this.employmentStatus = employmentStatus;
}
/**
* Get application ID
* @return Application ID
*/
public String getId() {
return id;
}
/**
* Set application ID
* @param id Application ID
*/
public void setId(String id) {
this.id = id;
}
/**
* Get customer ID
* @return Customer ID
*/
public String getCustomerId() {
return customerId;
}
/**
* Set customer ID
* @param customerId Customer ID
*/
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
/**
* Get applicant name
* @return Applicant name
*/
public String getApplicantName() {
return applicantName;
}
/**
* Set applicant name
* @param applicantName Applicant name
*/
public void setApplicantName(String applicantName) {
this.applicantName = applicantName;
}
/**
* Get applicant email
* @return Applicant email
*/
public String getApplicantEmail() {
return applicantEmail;
}
/**
* Set applicant email
* @param applicantEmail Applicant email
*/
public void setApplicantEmail(String applicantEmail) {
this.applicantEmail = applicantEmail;
}
/**
* Get applicant phone
* @return Applicant phone
*/
public String getApplicantPhone() {
return applicantPhone;
}
/**
* Set applicant phone
* @param applicantPhone Applicant phone
*/
public void setApplicantPhone(String applicantPhone) {
this.applicantPhone = applicantPhone;
}
/**
* Get date of birth
* @return Date of birth
*/
public LocalDate getDateOfBirth() {
return dateOfBirth;
}
/**
* Set date of birth
* @param dateOfBirth Date of birth
*/
public void setDateOfBirth(LocalDate dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
/**
* Get monthly income
* @return Monthly income
*/
public BigDecimal getMonthlyIncome() {
return monthlyIncome;
}
/**
* Set monthly income
* @param monthlyIncome Monthly income
*/
public void setMonthlyIncome(BigDecimal monthlyIncome) {
this.monthlyIncome = monthlyIncome;
}
/**
* Get loan amount
* @return Loan amount
*/
public BigDecimal getLoanAmount() {
return loanAmount;
}
/**
* Set loan amount
* @param loanAmount Loan amount
*/
public void setLoanAmount(BigDecimal loanAmount) {
this.loanAmount = loanAmount;
}
/**
* Get loan term in months
* @return Loan term in months
*/
public int getLoanTermMonths() {
return loanTermMonths;
}
/**
* Set loan term in months
* @param loanTermMonths Loan term in months
*/
public void setLoanTermMonths(int loanTermMonths) {
this.loanTermMonths = loanTermMonths;
}
/**
* Get loan purpose
* @return Loan purpose
*/
public String getLoanPurpose() {
return loanPurpose;
}
/**
* Set loan purpose
* @param loanPurpose Loan purpose
*/
public void setLoanPurpose(String loanPurpose) {
this.loanPurpose = loanPurpose;
}
/**
* Get employment status
* @return Employment status
*/
public EmploymentStatus getEmploymentStatus() {
return employmentStatus;
}
/**
* Set employment status
* @param employmentStatus Employment status
*/
public void setEmploymentStatus(EmploymentStatus employmentStatus) {
this.employmentStatus = employmentStatus;
}
/**
* Get application status
* @return Application status
*/
public CreditApplicationStatus getStatus() {
return status;
}
/**
* Set application status
* @param status Application status
*/
public void setStatus(CreditApplicationStatus status) {
this.status = status;
}
/**
* Get credit score
* @return Credit score
*/
public Integer getCreditScore() {
return creditScore;
}
/**
* Set credit score
* @param creditScore Credit score
*/
public void setCreditScore(Integer creditScore) {
this.creditScore = creditScore;
}
/**
* Get approved amount
* @return Approved amount
*/
public BigDecimal getApprovedAmount() {
return approvedAmount;
}
/**
* Set approved amount
* @param approvedAmount Approved amount
*/
public void setApprovedAmount(BigDecimal approvedAmount) {
this.approvedAmount = approvedAmount;
}
/**
* Get interest rate
* @return Interest rate
*/
public BigDecimal getInterestRate() {
return interestRate;
}
/**
* Set interest rate
* @param interestRate Interest rate
*/
public void setInterestRate(BigDecimal interestRate) {
this.interestRate = interestRate;
}
/**
* Get creation timestamp
* @return Creation timestamp
*/
public LocalDateTime getCreatedAt() {
return createdAt;
}
/**
* Set creation timestamp
* @param createdAt Creation timestamp
*/
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
/**
* Get last update timestamp
* @return Last update timestamp
*/
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
/**
* Set last update timestamp
* @param updatedAt Last update timestamp
*/
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
/**
* Get review timestamp
* @return Review timestamp
*/
public LocalDateTime getReviewedAt() {
return reviewedAt;
}
/**
* Set review timestamp
* @param reviewedAt Review timestamp
*/
public void setReviewedAt(LocalDateTime reviewedAt) {
this.reviewedAt = reviewedAt;
}
/**
* Get reviewer ID
* @return Reviewer ID
*/
public String getReviewedBy() {
return reviewedBy;
}
/**
* Set reviewer ID
* @param reviewedBy Reviewer ID
*/
public void setReviewedBy(String reviewedBy) {
this.reviewedBy = reviewedBy;
}
/**
* Get rejection reason
* @return Rejection reason
*/
public String getRejectionReason() {
return rejectionReason;
}
/**
* Set rejection reason
* @param rejectionReason Rejection reason
*/
public void setRejectionReason(String rejectionReason) {
this.rejectionReason = rejectionReason;
}
@Override
public String toString() {
return "CreditApplication{" +
"id='" + id + '\'' +
", customerId='" + customerId + '\'' +
", applicantName='" + applicantName + '\'' +
", applicantEmail='" + applicantEmail + '\'' +
", applicantPhone='" + applicantPhone + '\'' +
", dateOfBirth=" + dateOfBirth +
", monthlyIncome=" + monthlyIncome +
", loanAmount=" + loanAmount +
", loanTermMonths=" + loanTermMonths +
", loanPurpose='" + loanPurpose + '\'' +
", employmentStatus=" + employmentStatus +
", status=" + status +
", creditScore=" + creditScore +
", approvedAmount=" + approvedAmount +
", interestRate=" + interestRate +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
", reviewedAt=" + reviewedAt +
", reviewedBy='" + reviewedBy + '\'' +
", rejectionReason='" + rejectionReason + '\'' +
'}';
}
}

View File

@@ -0,0 +1,320 @@
package com.financial.api.models.credit;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.LocalDateTime;
import java.util.UUID;
/**
* CreditScore model representing a credit score calculation in the financial system.
*/
public class CreditScore {
private String id;
private String applicationId;
private String customerId;
private int score;
private String scoreBand;
private CreditRating rating;
private LocalDateTime calculatedAt;
private LocalDateTime expiresAt;
private String calculationMethod;
private String factors;
private String recommendation;
public enum CreditRating {
@JsonProperty("EXCELLENT")
EXCELLENT, // 800-850
@JsonProperty("VERY_GOOD")
VERY_GOOD, // 740-799
@JsonProperty("GOOD")
GOOD, // 670-739
@JsonProperty("FAIR")
FAIR, // 580-669
@JsonProperty("POOR")
POOR, // 300-579
@JsonProperty("NO_SCORE")
NO_SCORE // No credit history
}
/**
* Default constructor
*/
public CreditScore() {
this.id = UUID.randomUUID().toString();
this.calculatedAt = LocalDateTime.now();
}
/**
* Parameterized constructor
* @param applicationId Application ID
* @param customerId Customer ID
* @param score Credit score value
* @param calculationMethod Calculation method used
*/
public CreditScore(String applicationId, String customerId, int score, String calculationMethod) {
this();
this.applicationId = applicationId;
this.customerId = customerId;
this.score = score;
this.calculationMethod = calculationMethod;
this.scoreBand = determineScoreBand(score);
this.rating = determineRating(score);
this.recommendation = generateRecommendation(score);
// Set expiration to 30 days from calculation
this.expiresAt = calculatedAt.plusDays(30);
}
/**
* Determine score band based on score value
* @param score Credit score value
* @return Score band description
*/
private String determineScoreBand(int score) {
if (score >= 800) return "800-850";
if (score >= 740) return "740-799";
if (score >= 670) return "670-739";
if (score >= 580) return "580-669";
if (score >= 300) return "300-579";
return "No Score";
}
/**
* Determine credit rating based on score value
* @param score Credit score value
* @return Credit rating
*/
private CreditRating determineRating(int score) {
if (score >= 800) return CreditRating.EXCELLENT;
if (score >= 740) return CreditRating.VERY_GOOD;
if (score >= 670) return CreditRating.GOOD;
if (score >= 580) return CreditRating.FAIR;
if (score >= 300) return CreditRating.POOR;
return CreditRating.NO_SCORE;
}
/**
* Generate recommendation based on credit score
* @param score Credit score value
* @return Recommendation text
*/
private String generateRecommendation(int score) {
if (score >= 740) {
return "Excellent credit. Eligible for best loan terms and interest rates.";
} else if (score >= 670) {
return "Good credit. Eligible for competitive loan terms.";
} else if (score >= 580) {
return "Fair credit. May qualify for loans but with less favorable terms.";
} else {
return "Poor credit. Consider improving credit score before applying for loans.";
}
}
/**
* Get credit score ID
* @return Credit score ID
*/
public String getId() {
return id;
}
/**
* Set credit score ID
* @param id Credit score ID
*/
public void setId(String id) {
this.id = id;
}
/**
* Get application ID
* @return Application ID
*/
public String getApplicationId() {
return applicationId;
}
/**
* Set application ID
* @param applicationId Application ID
*/
public void setApplicationId(String applicationId) {
this.applicationId = applicationId;
}
/**
* Get customer ID
* @return Customer ID
*/
public String getCustomerId() {
return customerId;
}
/**
* Set customer ID
* @param customerId Customer ID
*/
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
/**
* Get credit score value
* @return Credit score value
*/
public int getScore() {
return score;
}
/**
* Set credit score value
* @param score Credit score value
*/
public void setScore(int score) {
this.score = score;
this.scoreBand = determineScoreBand(score);
this.rating = determineRating(score);
this.recommendation = generateRecommendation(score);
}
/**
* Get score band
* @return Score band
*/
public String getScoreBand() {
return scoreBand;
}
/**
* Set score band
* @param scoreBand Score band
*/
public void setScoreBand(String scoreBand) {
this.scoreBand = scoreBand;
}
/**
* Get credit rating
* @return Credit rating
*/
public CreditRating getRating() {
return rating;
}
/**
* Set credit rating
* @param rating Credit rating
*/
public void setRating(CreditRating rating) {
this.rating = rating;
}
/**
* Get calculation timestamp
* @return Calculation timestamp
*/
public LocalDateTime getCalculatedAt() {
return calculatedAt;
}
/**
* Set calculation timestamp
* @param calculatedAt Calculation timestamp
*/
public void setCalculatedAt(LocalDateTime calculatedAt) {
this.calculatedAt = calculatedAt;
}
/**
* Get expiration timestamp
* @return Expiration timestamp
*/
public LocalDateTime getExpiresAt() {
return expiresAt;
}
/**
* Set expiration timestamp
* @param expiresAt Expiration timestamp
*/
public void setExpiresAt(LocalDateTime expiresAt) {
this.expiresAt = expiresAt;
}
/**
* Get calculation method
* @return Calculation method
*/
public String getCalculationMethod() {
return calculationMethod;
}
/**
* Set calculation method
* @param calculationMethod Calculation method
*/
public void setCalculationMethod(String calculationMethod) {
this.calculationMethod = calculationMethod;
}
/**
* Get factors affecting the score
* @return Factors affecting the score
*/
public String getFactors() {
return factors;
}
/**
* Set factors affecting the score
* @param factors Factors affecting the score
*/
public void setFactors(String factors) {
this.factors = factors;
}
/**
* Get recommendation
* @return Recommendation
*/
public String getRecommendation() {
return recommendation;
}
/**
* Set recommendation
* @param recommendation Recommendation
*/
public void setRecommendation(String recommendation) {
this.recommendation = recommendation;
}
/**
* Check if credit score is expired
* @return true if expired, false otherwise
*/
public boolean isExpired() {
return LocalDateTime.now().isAfter(expiresAt);
}
@Override
public String toString() {
return "CreditScore{" +
"id='" + id + '\'' +
", applicationId='" + applicationId + '\'' +
", customerId='" + customerId + '\'' +
", score=" + score +
", scoreBand='" + scoreBand + '\'' +
", rating=" + rating +
", calculatedAt=" + calculatedAt +
", expiresAt=" + expiresAt +
", calculationMethod='" + calculationMethod + '\'' +
", factors='" + factors + '\'' +
", recommendation='" + recommendation + '\'' +
'}';
}
}

View File

@@ -0,0 +1,330 @@
package com.financial.api.models.wallet;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;
/**
* Transaction model representing a financial transaction in the wallet system.
*/
public class Transaction {
private String id;
private String walletId;
private String referenceId;
private TransactionType type;
private BigDecimal amount;
private String currency;
private String description;
private TransactionStatus status;
private String counterpartyAccount;
private String counterpartyName;
private LocalDateTime createdAt;
private LocalDateTime processedAt;
private String createdBy;
private String processedBy;
public enum TransactionType {
@JsonProperty("DEPOSIT")
DEPOSIT,
@JsonProperty("WITHDRAWAL")
WITHDRAWAL,
@JsonProperty("TRANSFER")
TRANSFER,
@JsonProperty("PAYMENT")
PAYMENT,
@JsonProperty("REFUND")
REFUND
}
public enum TransactionStatus {
@JsonProperty("PENDING")
PENDING,
@JsonProperty("PROCESSING")
PROCESSING,
@JsonProperty("COMPLETED")
COMPLETED,
@JsonProperty("FAILED")
FAILED,
@JsonProperty("CANCELLED")
CANCELLED
}
/**
* Default constructor
*/
public Transaction() {
this.id = UUID.randomUUID().toString();
this.createdAt = LocalDateTime.now();
this.status = TransactionStatus.PENDING;
}
/**
* Parameterized constructor
* @param walletId Wallet ID
* @param type Transaction type
* @param amount Transaction amount
* @param currency Currency code
* @param description Transaction description
*/
public Transaction(String walletId, TransactionType type, BigDecimal amount, String currency, String description) {
this();
this.walletId = walletId;
this.type = type;
this.amount = amount;
this.currency = currency;
this.description = description;
}
/**
* Get transaction ID
* @return Transaction ID
*/
public String getId() {
return id;
}
/**
* Set transaction ID
* @param id Transaction ID
*/
public void setId(String id) {
this.id = id;
}
/**
* Get wallet ID
* @return Wallet ID
*/
public String getWalletId() {
return walletId;
}
/**
* Set wallet ID
* @param walletId Wallet ID
*/
public void setWalletId(String walletId) {
this.walletId = walletId;
}
/**
* Get reference ID
* @return Reference ID
*/
public String getReferenceId() {
return referenceId;
}
/**
* Set reference ID
* @param referenceId Reference ID
*/
public void setReferenceId(String referenceId) {
this.referenceId = referenceId;
}
/**
* Get transaction type
* @return Transaction type
*/
public TransactionType getType() {
return type;
}
/**
* Set transaction type
* @param type Transaction type
*/
public void setType(TransactionType type) {
this.type = type;
}
/**
* Get transaction amount
* @return Transaction amount
*/
public BigDecimal getAmount() {
return amount;
}
/**
* Set transaction amount
* @param amount Transaction amount
*/
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
/**
* Get currency code
* @return Currency code
*/
public String getCurrency() {
return currency;
}
/**
* Set currency code
* @param currency Currency code
*/
public void setCurrency(String currency) {
this.currency = currency;
}
/**
* Get transaction description
* @return Transaction description
*/
public String getDescription() {
return description;
}
/**
* Set transaction description
* @param description Transaction description
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Get transaction status
* @return Transaction status
*/
public TransactionStatus getStatus() {
return status;
}
/**
* Set transaction status
* @param status Transaction status
*/
public void setStatus(TransactionStatus status) {
this.status = status;
}
/**
* Get counterparty account number
* @return Counterparty account number
*/
public String getCounterpartyAccount() {
return counterpartyAccount;
}
/**
* Set counterparty account number
* @param counterpartyAccount Counterparty account number
*/
public void setCounterpartyAccount(String counterpartyAccount) {
this.counterpartyAccount = counterpartyAccount;
}
/**
* Get counterparty name
* @return Counterparty name
*/
public String getCounterpartyName() {
return counterpartyName;
}
/**
* Set counterparty name
* @param counterpartyName Counterparty name
*/
public void setCounterpartyName(String counterpartyName) {
this.counterpartyName = counterpartyName;
}
/**
* Get creation timestamp
* @return Creation timestamp
*/
public LocalDateTime getCreatedAt() {
return createdAt;
}
/**
* Set creation timestamp
* @param createdAt Creation timestamp
*/
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
/**
* Get processing timestamp
* @return Processing timestamp
*/
public LocalDateTime getProcessedAt() {
return processedAt;
}
/**
* Set processing timestamp
* @param processedAt Processing timestamp
*/
public void setProcessedAt(LocalDateTime processedAt) {
this.processedAt = processedAt;
}
/**
* Get creator user ID
* @return Creator user ID
*/
public String getCreatedBy() {
return createdBy;
}
/**
* Set creator user ID
* @param createdBy Creator user ID
*/
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
/**
* Get processor user ID
* @return Processor user ID
*/
public String getProcessedBy() {
return processedBy;
}
/**
* Set processor user ID
* @param processedBy Processor user ID
*/
public void setProcessedBy(String processedBy) {
this.processedBy = processedBy;
}
@Override
public String toString() {
return "Transaction{" +
"id='" + id + '\'' +
", walletId='" + walletId + '\'' +
", referenceId='" + referenceId + '\'' +
", type=" + type +
", amount=" + amount +
", currency='" + currency + '\'' +
", description='" + description + '\'' +
", status=" + status +
", counterpartyAccount='" + counterpartyAccount + '\'' +
", counterpartyName='" + counterpartyName + '\'' +
", createdAt=" + createdAt +
", processedAt=" + processedAt +
", createdBy='" + createdBy + '\'' +
", processedBy='" + processedBy + '\'' +
'}';
}
}

View File

@@ -0,0 +1,237 @@
package com.financial.api.models.wallet;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;
/**
* Wallet model representing a digital wallet in the financial system.
*/
public class Wallet {
private String id;
private String customerId;
private String accountNumber;
private BigDecimal balance;
private String currency;
private WalletStatus status;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private String createdBy;
private String updatedBy;
public enum WalletStatus {
@JsonProperty("ACTIVE")
ACTIVE,
@JsonProperty("INACTIVE")
INACTIVE,
@JsonProperty("SUSPENDED")
SUSPENDED,
@JsonProperty("CLOSED")
CLOSED
}
/**
* Default constructor
*/
public Wallet() {
this.id = UUID.randomUUID().toString();
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
this.status = WalletStatus.ACTIVE;
}
/**
* Parameterized constructor
* @param customerId Customer ID
* @param accountNumber Account number
* @param initialBalance Initial balance
* @param currency Currency code
*/
public Wallet(String customerId, String accountNumber, BigDecimal initialBalance, String currency) {
this();
this.customerId = customerId;
this.accountNumber = accountNumber;
this.balance = initialBalance;
this.currency = currency;
}
/**
* Get wallet ID
* @return Wallet ID
*/
public String getId() {
return id;
}
/**
* Set wallet ID
* @param id Wallet ID
*/
public void setId(String id) {
this.id = id;
}
/**
* Get customer ID
* @return Customer ID
*/
public String getCustomerId() {
return customerId;
}
/**
* Set customer ID
* @param customerId Customer ID
*/
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
/**
* Get account number
* @return Account number
*/
public String getAccountNumber() {
return accountNumber;
}
/**
* Set account number
* @param accountNumber Account number
*/
public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
/**
* Get wallet balance
* @return Wallet balance
*/
public BigDecimal getBalance() {
return balance;
}
/**
* Set wallet balance
* @param balance Wallet balance
*/
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
/**
* Get currency code
* @return Currency code
*/
public String getCurrency() {
return currency;
}
/**
* Set currency code
* @param currency Currency code
*/
public void setCurrency(String currency) {
this.currency = currency;
}
/**
* Get wallet status
* @return Wallet status
*/
public WalletStatus getStatus() {
return status;
}
/**
* Set wallet status
* @param status Wallet status
*/
public void setStatus(WalletStatus status) {
this.status = status;
}
/**
* Get creation timestamp
* @return Creation timestamp
*/
public LocalDateTime getCreatedAt() {
return createdAt;
}
/**
* Set creation timestamp
* @param createdAt Creation timestamp
*/
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
/**
* Get last update timestamp
* @return Last update timestamp
*/
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
/**
* Set last update timestamp
* @param updatedAt Last update timestamp
*/
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
/**
* Get creator user ID
* @return Creator user ID
*/
public String getCreatedBy() {
return createdBy;
}
/**
* Set creator user ID
* @param createdBy Creator user ID
*/
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
/**
* Get updater user ID
* @return Updater user ID
*/
public String getUpdatedBy() {
return updatedBy;
}
/**
* Set updater user ID
* @param updatedBy Updater user ID
*/
public void setUpdatedBy(String updatedBy) {
this.updatedBy = updatedBy;
}
@Override
public String toString() {
return "Wallet{" +
"id='" + id + '\'' +
", customerId='" + customerId + '\'' +
", accountNumber='" + accountNumber + '\'' +
", balance=" + balance +
", currency='" + currency + '\'' +
", status=" + status +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
", createdBy='" + createdBy + '\'' +
", updatedBy='" + updatedBy + '\'' +
'}';
}
}

View File

@@ -0,0 +1,259 @@
package com.financial.api.services;
import com.financial.api.config.ConfigManager;
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.filter.log.RequestLoggingFilter;
import io.restassured.filter.log.ResponseLoggingFilter;
import io.restassured.http.ContentType;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Map;
/**
* Base API client class that provides common functionality for API testing.
*/
public class BaseApiClient {
private static final Logger logger = LogManager.getLogger(BaseApiClient.class);
protected static ConfigManager configManager;
protected RequestSpecification requestSpec;
/**
* Initialize the API client with default configuration
*/
public BaseApiClient() {
configManager = ConfigManager.getInstance();
setupRestAssured();
}
/**
* Set up RestAssured configuration
*/
private void setupRestAssured() {
String baseUrl = configManager.getApiBaseUrl();
String apiVersion = configManager.getApiVersion();
int timeout = configManager.getApiTimeout();
RestAssured.baseURI = baseUrl;
RestAssured.basePath = "/" + apiVersion;
requestSpec = new RequestSpecBuilder()
.setContentType(ContentType.JSON)
.setAccept(ContentType.JSON)
.addFilter(new RequestLoggingFilter())
.addFilter(new ResponseLoggingFilter())
.build();
}
/**
* Send a GET request
* @param endpoint API endpoint
* @return Response object
*/
protected Response get(String endpoint) {
logger.info("Sending GET request to: {}", endpoint);
return RestAssured.given()
.spec(requestSpec)
.when()
.get(endpoint);
}
/**
* Send a GET request with path parameters
* @param endpoint API endpoint with path parameters
* @param pathParams Path parameters
* @return Response object
*/
protected Response get(String endpoint, Map<String, Object> pathParams) {
logger.info("Sending GET request to: {} with path parameters: {}", endpoint, pathParams);
return RestAssured.given()
.spec(requestSpec)
.pathParams(pathParams)
.when()
.get(endpoint);
}
/**
* Send a GET request with query parameters
* @param endpoint API endpoint
* @param queryParams Query parameters
* @return Response object
*/
protected Response getWithQueryParams(String endpoint, Map<String, Object> queryParams) {
logger.info("Sending GET request to: {} with query parameters: {}", endpoint, queryParams);
return RestAssured.given()
.spec(requestSpec)
.queryParams(queryParams)
.when()
.get(endpoint);
}
/**
* Send a POST request
* @param endpoint API endpoint
* @param body Request body
* @return Response object
*/
protected Response post(String endpoint, Object body) {
logger.info("Sending POST request to: {} with body: {}", endpoint, body);
return RestAssured.given()
.spec(requestSpec)
.body(body)
.when()
.post(endpoint);
}
/**
* Send a POST request with path parameters
* @param endpoint API endpoint with path parameters
* @param pathParams Path parameters
* @param body Request body
* @return Response object
*/
protected Response post(String endpoint, Map<String, Object> pathParams, Object body) {
logger.info("Sending POST request to: {} with path parameters: {} and body: {}", endpoint, pathParams, body);
return RestAssured.given()
.spec(requestSpec)
.pathParams(pathParams)
.body(body)
.when()
.post(endpoint);
}
/**
* Send a PUT request
* @param endpoint API endpoint
* @param body Request body
* @return Response object
*/
protected Response put(String endpoint, Object body) {
logger.info("Sending PUT request to: {} with body: {}", endpoint, body);
return RestAssured.given()
.spec(requestSpec)
.body(body)
.when()
.put(endpoint);
}
/**
* Send a PUT request with path parameters
* @param endpoint API endpoint with path parameters
* @param pathParams Path parameters
* @param body Request body
* @return Response object
*/
protected Response put(String endpoint, Map<String, Object> pathParams, Object body) {
logger.info("Sending PUT request to: {} with path parameters: {} and body: {}", endpoint, pathParams, body);
return RestAssured.given()
.spec(requestSpec)
.pathParams(pathParams)
.body(body)
.when()
.put(endpoint);
}
/**
* Send a PATCH request
* @param endpoint API endpoint
* @param body Request body
* @return Response object
*/
protected Response patch(String endpoint, Object body) {
logger.info("Sending PATCH request to: {} with body: {}", endpoint, body);
return RestAssured.given()
.spec(requestSpec)
.body(body)
.when()
.patch(endpoint);
}
/**
* Send a PATCH request with path parameters
* @param endpoint API endpoint with path parameters
* @param pathParams Path parameters
* @param body Request body
* @return Response object
*/
protected Response patch(String endpoint, Map<String, Object> pathParams, Object body) {
logger.info("Sending PATCH request to: {} with path parameters: {} and body: {}", endpoint, pathParams, body);
return RestAssured.given()
.spec(requestSpec)
.pathParams(pathParams)
.body(body)
.when()
.patch(endpoint);
}
/**
* Send a DELETE request
* @param endpoint API endpoint
* @return Response object
*/
protected Response delete(String endpoint) {
logger.info("Sending DELETE request to: {}", endpoint);
return RestAssured.given()
.spec(requestSpec)
.when()
.delete(endpoint);
}
/**
* Send a DELETE request with path parameters
* @param endpoint API endpoint with path parameters
* @param pathParams Path parameters
* @return Response object
*/
protected Response delete(String endpoint, Map<String, Object> pathParams) {
logger.info("Sending DELETE request to: {} with path parameters: {}", endpoint, pathParams);
return RestAssured.given()
.spec(requestSpec)
.pathParams(pathParams)
.when()
.delete(endpoint);
}
/**
* Add headers to the request specification
* @param headers Map of headers
*/
protected void addHeaders(Map<String, String> headers) {
if (headers != null && !headers.isEmpty()) {
requestSpec.headers(headers);
}
}
/**
* Add authentication header to the request specification
* @param authToken Authentication token
*/
protected void addAuthToken(String authToken) {
requestSpec.header("Authorization", "Bearer " + authToken);
}
/**
* Set base URL for the API client
* @param baseUrl Base URL
*/
protected void setBaseUrl(String baseUrl) {
RestAssured.baseURI = baseUrl;
}
/**
* Set base path for the API client
* @param basePath Base path
*/
protected void setBasePath(String basePath) {
RestAssured.basePath = basePath;
}
/**
* Reset RestAssured configuration
*/
protected void resetRestAssured() {
RestAssured.reset();
setupRestAssured();
}
}

View File

@@ -0,0 +1,312 @@
package com.financial.api.services;
import com.financial.api.models.credit.CreditApplication;
import com.financial.api.models.credit.CreditScore;
import io.restassured.response.Response;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashMap;
import java.util.Map;
/**
* Service class for Credit API operations
*/
public class CreditService extends BaseApiClient {
private static final Logger logger = LogManager.getLogger(CreditService.class);
/**
* Submit a new credit application
* @param application CreditApplication object
* @return Response object
*/
public Response submitApplication(CreditApplication application) {
logger.info("Submitting credit application for customer: {}", application.getCustomerId());
return post("/credit/applications", application);
}
/**
* Get credit application by ID
* @param applicationId Application ID
* @return Response object
*/
public Response getApplication(String applicationId) {
logger.info("Getting credit application with ID: {}", applicationId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("applicationId", applicationId);
return get("/credit/applications/{applicationId}", pathParams);
}
/**
* Get all credit applications for a customer
* @param customerId Customer ID
* @return Response object
*/
public Response getCustomerApplications(String customerId) {
logger.info("Getting credit applications for customer: {}", customerId);
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("customerId", customerId);
return getWithQueryParams("/credit/applications", queryParams);
}
/**
* Get credit applications by status
* @param status Application status
* @return Response object
*/
public Response getApplicationsByStatus(CreditApplication.CreditApplicationStatus status) {
logger.info("Getting credit applications with status: {}", status);
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("status", status);
return getWithQueryParams("/credit/applications", queryParams);
}
/**
* Update credit application
* @param applicationId Application ID
* @param application Updated application object
* @return Response object
*/
public Response updateApplication(String applicationId, CreditApplication application) {
logger.info("Updating credit application with ID: {}", applicationId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("applicationId", applicationId);
return put("/credit/applications/{applicationId}", pathParams, application);
}
/**
* Update credit application status
* @param applicationId Application ID
* @param status New status
* @param reviewedBy Reviewer ID
* @param rejectionReason Rejection reason (if applicable)
* @return Response object
*/
public Response updateApplicationStatus(String applicationId, CreditApplication.CreditApplicationStatus status,
String reviewedBy, String rejectionReason) {
logger.info("Updating credit application status for application ID: {} to {}", applicationId, status);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("applicationId", applicationId);
Map<String, Object> body = new HashMap<>();
body.put("status", status);
body.put("reviewedBy", reviewedBy);
if (rejectionReason != null && !rejectionReason.isEmpty()) {
body.put("rejectionReason", rejectionReason);
}
return patch("/credit/applications/{applicationId}/status", pathParams, body);
}
/**
* Delete credit application
* @param applicationId Application ID
* @return Response object
*/
public Response deleteApplication(String applicationId) {
logger.info("Deleting credit application with ID: {}", applicationId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("applicationId", applicationId);
return delete("/credit/applications/{applicationId}", pathParams);
}
/**
* Get credit score for an application
* @param applicationId Application ID
* @return Response object
*/
public Response getCreditScore(String applicationId) {
logger.info("Getting credit score for application ID: {}", applicationId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("applicationId", applicationId);
return get("/credit/applications/{applicationId}/score", pathParams);
}
/**
* Get credit score history for an application
* @param applicationId Application ID
* @return Response object
*/
public Response getCreditScoreHistory(String applicationId) {
logger.info("Getting credit score history for application ID: {}", applicationId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("applicationId", applicationId);
return get("/credit/applications/{applicationId}/score/history", pathParams);
}
/**
* Request credit score calculation
* @param applicationId Application ID
* @param calculationMethod Calculation method
* @return Response object
*/
public Response calculateCreditScore(String applicationId, String calculationMethod) {
logger.info("Requesting credit score calculation for application ID: {}", applicationId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("applicationId", applicationId);
Map<String, Object> body = new HashMap<>();
body.put("calculationMethod", calculationMethod);
return post("/credit/applications/{applicationId}/score/calculate", pathParams, body);
}
/**
* Approve credit application
* @param applicationId Application ID
* @param approvedAmount Approved amount
* @param interestRate Interest rate
* @param reviewedBy Reviewer ID
* @return Response object
*/
public Response approveApplication(String applicationId, double approvedAmount, double interestRate, String reviewedBy) {
logger.info("Approving credit application with ID: {}", applicationId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("applicationId", applicationId);
Map<String, Object> body = new HashMap<>();
body.put("status", CreditApplication.CreditApplicationStatus.APPROVED);
body.put("approvedAmount", approvedAmount);
body.put("interestRate", interestRate);
body.put("reviewedBy", reviewedBy);
return patch("/credit/applications/{applicationId}/status", pathParams, body);
}
/**
* Reject credit application
* @param applicationId Application ID
* @param rejectionReason Rejection reason
* @param reviewedBy Reviewer ID
* @return Response object
*/
public Response rejectApplication(String applicationId, String rejectionReason, String reviewedBy) {
logger.info("Rejecting credit application with ID: {}", applicationId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("applicationId", applicationId);
Map<String, Object> body = new HashMap<>();
body.put("status", CreditApplication.CreditApplicationStatus.REJECTED);
body.put("rejectionReason", rejectionReason);
body.put("reviewedBy", reviewedBy);
return patch("/credit/applications/{applicationId}/status", pathParams, body);
}
/**
* Get customer credit score
* @param customerId Customer ID
* @return Response object
*/
public Response getCustomerCreditScore(String customerId) {
logger.info("Getting credit score for customer: {}", customerId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("customerId", customerId);
return get("/credit/customers/{customerId}/score", pathParams);
}
/**
* Get customer credit score history
* @param customerId Customer ID
* @return Response object
*/
public Response getCustomerCreditScoreHistory(String customerId) {
logger.info("Getting credit score history for customer: {}", customerId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("customerId", customerId);
return get("/credit/customers/{customerId}/score/history", pathParams);
}
/**
* Request customer credit score calculation
* @param customerId Customer ID
* @param calculationMethod Calculation method
* @return Response object
*/
public Response calculateCustomerCreditScore(String customerId, String calculationMethod) {
logger.info("Requesting credit score calculation for customer: {}", customerId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("customerId", customerId);
Map<String, Object> body = new HashMap<>();
body.put("calculationMethod", calculationMethod);
return post("/credit/customers/{customerId}/score/calculate", pathParams, body);
}
/**
* Get credit products
* @return Response object
*/
public Response getCreditProducts() {
logger.info("Getting available credit products");
return get("/credit/products");
}
/**
* Get credit product by ID
* @param productId Product ID
* @return Response object
*/
public Response getCreditProduct(String productId) {
logger.info("Getting credit product with ID: {}", productId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("productId", productId);
return get("/credit/products/{productId}", pathParams);
}
/**
* Get credit eligibility
* @param customerId Customer ID
* @param productId Product ID
* @return Response object
*/
public Response getCreditEligibility(String customerId, String productId) {
logger.info("Getting credit eligibility for customer: {} and product: {}", customerId, productId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("customerId", customerId);
pathParams.put("productId", productId);
return get("/credit/customers/{customerId}/eligibility/{productId}", pathParams);
}
/**
* Get credit offers for a customer
* @param customerId Customer ID
* @return Response object
*/
public Response getCreditOffers(String customerId) {
logger.info("Getting credit offers for customer: {}", customerId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("customerId", customerId);
return get("/credit/customers/{customerId}/offers", pathParams);
}
/**
* Accept credit offer
* @param customerId Customer ID
* @param offerId Offer ID
* @return Response object
*/
public Response acceptCreditOffer(String customerId, String offerId) {
logger.info("Accepting credit offer with ID: {} for customer: {}", offerId, customerId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("customerId", customerId);
pathParams.put("offerId", offerId);
return post("/credit/customers/{customerId}/offers/{offerId}/accept", pathParams, null);
}
/**
* Reject credit offer
* @param customerId Customer ID
* @param offerId Offer ID
* @return Response object
*/
public Response rejectCreditOffer(String customerId, String offerId) {
logger.info("Rejecting credit offer with ID: {} for customer: {}", offerId, customerId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("customerId", customerId);
pathParams.put("offerId", offerId);
return post("/credit/customers/{customerId}/offers/{offerId}/reject", pathParams, null);
}
}

View File

@@ -0,0 +1,279 @@
package com.financial.api.services;
import com.financial.api.models.wallet.Transaction;
import com.financial.api.models.wallet.Wallet;
import io.restassured.response.Response;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Service class for Wallet API operations
*/
public class WalletService extends BaseApiClient {
private static final Logger logger = LogManager.getLogger(WalletService.class);
/**
* Create a new wallet
* @param wallet Wallet object to create
* @return Response object
*/
public Response createWallet(Wallet wallet) {
logger.info("Creating wallet for customer: {}", wallet.getCustomerId());
return post("/wallets", wallet);
}
/**
* Get wallet by ID
* @param walletId Wallet ID
* @return Response object
*/
public Response getWallet(String walletId) {
logger.info("Getting wallet with ID: {}", walletId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("walletId", walletId);
return get("/wallets/{walletId}", pathParams);
}
/**
* Get wallet by account number
* @param accountNumber Account number
* @return Response object
*/
public Response getWalletByAccountNumber(String accountNumber) {
logger.info("Getting wallet with account number: {}", accountNumber);
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("accountNumber", accountNumber);
return getWithQueryParams("/wallets", queryParams);
}
/**
* Get all wallets for a customer
* @param customerId Customer ID
* @return Response object
*/
public Response getCustomerWallets(String customerId) {
logger.info("Getting wallets for customer: {}", customerId);
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("customerId", customerId);
return getWithQueryParams("/wallets", queryParams);
}
/**
* Update wallet information
* @param walletId Wallet ID
* @param wallet Updated wallet object
* @return Response object
*/
public Response updateWallet(String walletId, Wallet wallet) {
logger.info("Updating wallet with ID: {}", walletId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("walletId", walletId);
return put("/wallets/{walletId}", pathParams, wallet);
}
/**
* Update wallet status
* @param walletId Wallet ID
* @param status New status
* @return Response object
*/
public Response updateWalletStatus(String walletId, Wallet.WalletStatus status) {
logger.info("Updating wallet status for wallet ID: {} to {}", walletId, status);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("walletId", walletId);
Map<String, Object> body = new HashMap<>();
body.put("status", status);
return patch("/wallets/{walletId}/status", pathParams, body);
}
/**
* Delete wallet
* @param walletId Wallet ID
* @return Response object
*/
public Response deleteWallet(String walletId) {
logger.info("Deleting wallet with ID: {}", walletId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("walletId", walletId);
return delete("/wallets/{walletId}", pathParams);
}
/**
* Create a transaction
* @param walletId Wallet ID
* @param transaction Transaction object
* @return Response object
*/
public Response createTransaction(String walletId, Transaction transaction) {
logger.info("Creating transaction for wallet ID: {}", walletId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("walletId", walletId);
return post("/wallets/{walletId}/transactions", pathParams, transaction);
}
/**
* Get transaction by ID
* @param walletId Wallet ID
* @param transactionId Transaction ID
* @return Response object
*/
public Response getTransaction(String walletId, String transactionId) {
logger.info("Getting transaction with ID: {} for wallet ID: {}", transactionId, walletId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("walletId", walletId);
pathParams.put("transactionId", transactionId);
return get("/wallets/{walletId}/transactions/{transactionId}", pathParams);
}
/**
* Get all transactions for a wallet
* @param walletId Wallet ID
* @return Response object
*/
public Response getTransactions(String walletId) {
logger.info("Getting transactions for wallet ID: {}", walletId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("walletId", walletId);
return get("/wallets/{walletId}/transactions", pathParams);
}
/**
* Get transactions for a wallet with filters
* @param walletId Wallet ID
* @param transactionType Transaction type filter
* @param status Transaction status filter
* @param startDate Start date filter
* @param endDate End date filter
* @param limit Maximum number of transactions to return
* @param offset Offset for pagination
* @return Response object
*/
public Response getTransactionsWithFilters(String walletId, Transaction.TransactionType transactionType,
Transaction.TransactionStatus status, String startDate,
String endDate, Integer limit, Integer offset) {
logger.info("Getting filtered transactions for wallet ID: {}", walletId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("walletId", walletId);
Map<String, Object> queryParams = new HashMap<>();
if (transactionType != null) {
queryParams.put("type", transactionType);
}
if (status != null) {
queryParams.put("status", status);
}
if (startDate != null) {
queryParams.put("startDate", startDate);
}
if (endDate != null) {
queryParams.put("endDate", endDate);
}
if (limit != null) {
queryParams.put("limit", limit);
}
if (offset != null) {
queryParams.put("offset", offset);
}
return getWithQueryParams("/wallets/{walletId}/transactions", queryParams);
}
/**
* Update transaction status
* @param walletId Wallet ID
* @param transactionId Transaction ID
* @param status New status
* @return Response object
*/
public Response updateTransactionStatus(String walletId, String transactionId, Transaction.TransactionStatus status) {
logger.info("Updating transaction status for transaction ID: {} to {}", transactionId, status);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("walletId", walletId);
pathParams.put("transactionId", transactionId);
Map<String, Object> body = new HashMap<>();
body.put("status", status);
return patch("/wallets/{walletId}/transactions/{transactionId}/status", pathParams, body);
}
/**
* Get wallet balance
* @param walletId Wallet ID
* @return Response object
*/
public Response getWalletBalance(String walletId) {
logger.info("Getting balance for wallet ID: {}", walletId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("walletId", walletId);
return get("/wallets/{walletId}/balance", pathParams);
}
/**
* Deposit funds to wallet
* @param walletId Wallet ID
* @param amount Amount to deposit
* @param description Description of the deposit
* @return Response object
*/
public Response depositFunds(String walletId, double amount, String description) {
logger.info("Depositing {} to wallet ID: {}", amount, walletId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("walletId", walletId);
Map<String, Object> body = new HashMap<>();
body.put("amount", amount);
body.put("description", description);
body.put("type", Transaction.TransactionType.DEPOSIT);
return post("/wallets/{walletId}/deposit", pathParams, body);
}
/**
* Withdraw funds from wallet
* @param walletId Wallet ID
* @param amount Amount to withdraw
* @param description Description of the withdrawal
* @return Response object
*/
public Response withdrawFunds(String walletId, double amount, String description) {
logger.info("Withdrawing {} from wallet ID: {}", amount, walletId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("walletId", walletId);
Map<String, Object> body = new HashMap<>();
body.put("amount", amount);
body.put("description", description);
body.put("type", Transaction.TransactionType.WITHDRAWAL);
return post("/wallets/{walletId}/withdraw", pathParams, body);
}
/**
* Transfer funds between wallets
* @param fromWalletId Source wallet ID
* @param toWalletId Destination wallet ID
* @param amount Amount to transfer
* @param description Description of the transfer
* @return Response object
*/
public Response transferFunds(String fromWalletId, String toWalletId, double amount, String description) {
logger.info("Transferring {} from wallet ID: {} to wallet ID: {}", amount, fromWalletId, toWalletId);
Map<String, Object> pathParams = new HashMap<>();
pathParams.put("walletId", fromWalletId);
Map<String, Object> body = new HashMap<>();
body.put("toWalletId", toWalletId);
body.put("amount", amount);
body.put("description", description);
body.put("type", Transaction.TransactionType.TRANSFER);
return post("/wallets/{walletId}/transfer", pathParams, body);
}
}

View File

@@ -0,0 +1,271 @@
package com.financial.api.utils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.testng.IAnnotationTransformer;
import org.testng.annotations.ITestAnnotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Annotation transformer for dynamically modifying test annotations
*/
public class AnnotationTransformer implements IAnnotationTransformer {
private static final Logger logger = LogManager.getLogger(AnnotationTransformer.class);
// Configuration for test groups
private static final Map<String, List<String>> TEST_GROUPS_CONFIG = new HashMap<>();
static {
// Define which test methods belong to which groups
TEST_GROUPS_CONFIG.put("smoke", Arrays.asList(
"testCreateWallet",
"testGetWallet",
"testSubmitApplication",
"testGetApplication"
));
TEST_GROUPS_CONFIG.put("regression", Arrays.asList(
"testCreateWallet",
"testGetWallet",
"testGetWalletByAccountNumber",
"testUpdateWallet",
"testGetWalletBalance",
"testCreateTransaction",
"testGetTransaction",
"testGetTransactions",
"testUpdateTransactionStatus",
"testDepositFunds",
"testWithdrawFunds",
"testSubmitApplication",
"testGetApplication",
"testUpdateApplication",
"testGetApplicationsByStatus",
"testCalculateCreditScore",
"testGetCreditScore",
"testGetCreditScoreHistory",
"testApproveApplication",
"testRejectApplication",
"testGetCreditProducts",
"testGetCreditProduct"
));
TEST_GROUPS_CONFIG.put("performance", Arrays.asList(
"testCreateMultipleWalletsPerformance",
"testGetMultipleWalletsPerformance",
"testCreateMultipleTransactionsPerformance",
"testDepositFundsPerformance",
"testGetWalletBalancePerformance",
"testGetTransactionsPerformance",
"testTransferFundsPerformance",
"testSubmitMultipleApplicationsPerformance",
"testGetMultipleApplicationsPerformance",
"testCalculateMultipleCreditScoresPerformance",
"testGetMultipleCreditScoresPerformance",
"testGetMultipleCreditScoreHistoriesPerformance",
"testGetApplicationsByStatusPerformance",
"testGetCreditProductsPerformance"
));
TEST_GROUPS_CONFIG.put("security", Arrays.asList(
"testSqlInjectionInWalletCreation",
"testXssInWalletCreation",
"testAuthenticationBypassInWalletRetrieval",
"testAuthorizationBypassInWalletRetrieval",
"testIdorInWalletRetrieval",
"testSqlInjectionInTransactionCreation",
"testXssInTransactionCreation",
"testNegativeAmountInTransactionCreation",
"testExtremelyLargeAmountInTransactionCreation",
"testAuthenticationBypassInTransactionRetrieval",
"testAuthorizationBypassInTransactionRetrieval",
"testCsrfProtectionInWalletUpdate",
"testRateLimitingInWalletCreation",
"testInputValidationInWalletCreation",
"testSensitiveDataExposureInWalletResponse",
"testSqlInjectionInCreditApplicationSubmission",
"testXssInCreditApplicationSubmission",
"testAuthenticationBypassInCreditApplicationRetrieval",
"testAuthorizationBypassInCreditApplicationRetrieval",
"testIdorInCreditApplicationRetrieval",
"testSqlInjectionInCreditScoreCalculation",
"testXssInCreditScoreCalculation",
"testNegativeLoanAmountInCreditApplicationSubmission",
"testExtremelyLargeLoanAmountInCreditApplicationSubmission",
"testNegativeLoanTermInCreditApplicationSubmission",
"testExtremelyLargeLoanTermInCreditApplicationSubmission",
"testAuthenticationBypassInCreditScoreRetrieval",
"testAuthorizationBypassInCreditScoreRetrieval",
"testCsrfProtectionInCreditApplicationUpdate",
"testRateLimitingInCreditApplicationSubmission",
"testInputValidationInCreditApplicationSubmission",
"testSensitiveDataExposureInCreditApplicationResponse",
"testSensitiveDataExposureInCreditScoreResponse"
));
TEST_GROUPS_CONFIG.put("wallet", Arrays.asList(
"testCreateWallet",
"testGetWallet",
"testGetWalletByAccountNumber",
"testUpdateWallet",
"testGetWalletBalance",
"testCreateTransaction",
"testGetTransaction",
"testGetTransactions",
"testUpdateTransactionStatus",
"testDepositFunds",
"testWithdrawFunds",
"testCreateMultipleWalletsPerformance",
"testGetMultipleWalletsPerformance",
"testCreateMultipleTransactionsPerformance",
"testDepositFundsPerformance",
"testGetWalletBalancePerformance",
"testGetTransactionsPerformance",
"testTransferFundsPerformance",
"testSqlInjectionInWalletCreation",
"testXssInWalletCreation",
"testAuthenticationBypassInWalletRetrieval",
"testAuthorizationBypassInWalletRetrieval",
"testIdorInWalletRetrieval",
"testSqlInjectionInTransactionCreation",
"testXssInTransactionCreation",
"testNegativeAmountInTransactionCreation",
"testExtremelyLargeAmountInTransactionCreation",
"testAuthenticationBypassInTransactionRetrieval",
"testAuthorizationBypassInTransactionRetrieval",
"testCsrfProtectionInWalletUpdate",
"testRateLimitingInWalletCreation",
"testInputValidationInWalletCreation",
"testSensitiveDataExposureInWalletResponse"
));
TEST_GROUPS_CONFIG.put("credit", Arrays.asList(
"testSubmitApplication",
"testGetApplication",
"testUpdateApplication",
"testGetApplicationsByStatus",
"testCalculateCreditScore",
"testGetCreditScore",
"testGetCreditScoreHistory",
"testApproveApplication",
"testRejectApplication",
"testGetCreditProducts",
"testGetCreditProduct",
"testSubmitMultipleApplicationsPerformance",
"testGetMultipleApplicationsPerformance",
"testCalculateMultipleCreditScoresPerformance",
"testGetMultipleCreditScoresPerformance",
"testGetMultipleCreditScoreHistoriesPerformance",
"testGetApplicationsByStatusPerformance",
"testGetCreditProductsPerformance",
"testSqlInjectionInCreditApplicationSubmission",
"testXssInCreditApplicationSubmission",
"testAuthenticationBypassInCreditApplicationRetrieval",
"testAuthorizationBypassInCreditApplicationRetrieval",
"testIdorInCreditApplicationRetrieval",
"testSqlInjectionInCreditScoreCalculation",
"testXssInCreditScoreCalculation",
"testNegativeLoanAmountInCreditApplicationSubmission",
"testExtremelyLargeLoanAmountInCreditApplicationSubmission",
"testNegativeLoanTermInCreditApplicationSubmission",
"testExtremelyLargeLoanTermInCreditApplicationSubmission",
"testAuthenticationBypassInCreditScoreRetrieval",
"testAuthorizationBypassInCreditScoreRetrieval",
"testCsrfProtectionInCreditApplicationUpdate",
"testRateLimitingInCreditApplicationSubmission",
"testInputValidationInCreditApplicationSubmission",
"testSensitiveDataExposureInCreditApplicationResponse",
"testSensitiveDataExposureInCreditScoreResponse"
));
}
@Override
public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
if (testMethod == null) {
return;
}
String methodName = testMethod.getName();
logger.debug("Transforming annotations for method: {}", methodName);
// Get existing groups
String[] existingGroups = annotation.getGroups();
// Determine which groups this test method should belong to
String[] groupsToAdd = determineGroupsForMethod(methodName);
// Merge existing groups with new groups
String[] mergedGroups = mergeGroups(existingGroups, groupsToAdd);
// Set the merged groups
annotation.setGroups(mergedGroups);
// Set default invocation count and thread pool size for performance tests
if (methodName.contains("Performance") || methodName.contains("performance")) {
if (annotation.invocationCount() == -1) { // Default value
annotation.setInvocationCount(1);
}
if (annotation.threadPoolSize() == -1) { // Default value
annotation.setThreadPoolSize(5);
}
}
// Set default data provider for tests that need it
if (methodName.contains("Data") || methodName.contains("data")) {
if (annotation.dataProvider() == null || annotation.dataProvider().isEmpty()) {
annotation.setDataProvider("testDataProvider");
}
}
logger.debug("Method {} will be executed in groups: {}", methodName, Arrays.toString(mergedGroups));
}
/**
* Determine which groups a test method should belong to based on its name
* @param methodName Test method name
* @return Array of group names
*/
private String[] determineGroupsForMethod(String methodName) {
// Check if the method name matches any of the configured groups
for (Map.Entry<String, List<String>> entry : TEST_GROUPS_CONFIG.entrySet()) {
if (entry.getValue().contains(methodName)) {
return new String[]{entry.getKey()};
}
}
// Default group if no match found
return new String[]{"default"};
}
/**
* Merge existing groups with new groups
* @param existingGroups Existing groups array
* @param groupsToAdd Groups to add array
* @return Merged groups array
*/
private String[] mergeGroups(String[] existingGroups, String[] groupsToAdd) {
if (existingGroups == null || existingGroups.length == 0) {
return groupsToAdd;
}
if (groupsToAdd == null || groupsToAdd.length == 0) {
return existingGroups;
}
// Create a new array with combined length
String[] merged = new String[existingGroups.length + groupsToAdd.length];
// Copy existing groups
System.arraycopy(existingGroups, 0, merged, 0, existingGroups.length);
// Copy new groups
System.arraycopy(groupsToAdd, 0, merged, existingGroups.length, groupsToAdd.length);
return merged;
}
}

View File

@@ -0,0 +1,484 @@
package com.financial.api.utils;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.ClusterOptions;
import com.couchbase.client.java.env.ClusterEnvironment;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.kv.GetResult;
import com.couchbase.client.java.kv.InsertOptions;
import com.couchbase.client.java.kv.MutationResult;
import com.couchbase.client.java.kv.ReplaceOptions;
import com.couchbase.client.java.kv.UpsertOptions;
import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryResult;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.financial.api.config.ConfigManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Utility class for Couchbase operations to store and retrieve test data and logs.
*/
public class CouchbaseUtil {
private static final Logger logger = LogManager.getLogger(CouchbaseUtil.class);
private static ConfigManager configManager;
private static Cluster cluster;
private static ObjectMapper objectMapper;
static {
configManager = ConfigManager.getInstance();
objectMapper = new ObjectMapper();
}
/**
* Get Couchbase cluster
* @return Couchbase cluster
*/
public static Cluster getCluster() {
if (cluster == null) {
initializeCluster();
}
return cluster;
}
/**
* Initialize Couchbase cluster
*/
private static void initializeCluster() {
try {
String host = configManager.getCouchbaseHost();
String username = configManager.getCouchbaseUsername();
String password = configManager.getCouchbasePassword();
// Create cluster environment
ClusterEnvironment env = ClusterEnvironment.builder()
.timeoutConfig(timeoutConfig -> timeoutConfig
.connectTimeout(Duration.ofSeconds(10))
.kvTimeout(Duration.ofSeconds(5))
.queryTimeout(Duration.ofSeconds(30)))
.build();
// Create cluster options
ClusterOptions options = ClusterOptions.clusterOptions(username, password)
.environment(env);
// Connect to cluster
cluster = Cluster.connect(host, options);
logger.info("Couchbase cluster connected successfully");
} catch (Exception e) {
logger.error("Error connecting to Couchbase cluster: {}", e.getMessage(), e);
throw new RuntimeException("Failed to connect to Couchbase cluster", e);
}
}
/**
* Check if Couchbase is running
* @return true if Couchbase is running, false otherwise
*/
public static boolean isRunning() {
try {
getCluster().ping();
return true;
} catch (Exception e) {
logger.error("Error pinging Couchbase cluster: {}", e.getMessage(), e);
return false;
}
}
/**
* Get a document by ID
* @param bucketName Bucket name
* @param documentId Document ID
* @param documentClass Class of the document
* @return Document object if found, null otherwise
*/
public static <T> T getDocument(String bucketName, String documentId, Class<T> documentClass) {
try {
GetResult result = getCluster().bucket(bucketName)
.defaultCollection()
.get(documentId);
logger.info("Document found with ID: {} in bucket: {}", documentId, bucketName);
return result.contentAs(documentClass);
} catch (Exception e) {
logger.error("Error getting document from bucket {}: {}", bucketName, e.getMessage(), e);
return null;
}
}
/**
* Get a document as JsonObject
* @param bucketName Bucket name
* @param documentId Document ID
* @return JsonObject if found, null otherwise
*/
public static JsonObject getDocumentAsJson(String bucketName, String documentId) {
try {
GetResult result = getCluster().bucket(bucketName)
.defaultCollection()
.get(documentId);
logger.info("Document found with ID: {} in bucket: {}", documentId, bucketName);
return result.contentAsObject();
} catch (Exception e) {
logger.error("Error getting document from bucket {}: {}", bucketName, e.getMessage(), e);
return null;
}
}
/**
* Insert a document
* @param bucketName Bucket name
* @param documentId Document ID
* @param document Document object
* @return true if document inserted successfully, false otherwise
*/
public static boolean insertDocument(String bucketName, String documentId, Object document) {
try {
MutationResult result = getCluster().bucket(bucketName)
.defaultCollection()
.insert(documentId, document);
logger.info("Document inserted with ID: {} in bucket: {}", documentId, bucketName);
return true;
} catch (Exception e) {
logger.error("Error inserting document in bucket {}: {}", bucketName, e.getMessage(), e);
return false;
}
}
/**
* Insert a document with options
* @param bucketName Bucket name
* @param documentId Document ID
* @param document Document object
* @param options Insert options
* @return true if document inserted successfully, false otherwise
*/
public static boolean insertDocument(String bucketName, String documentId, Object document, InsertOptions options) {
try {
MutationResult result = getCluster().bucket(bucketName)
.defaultCollection()
.insert(documentId, document, options);
logger.info("Document inserted with ID: {} in bucket: {}", documentId, bucketName);
return true;
} catch (Exception e) {
logger.error("Error inserting document in bucket {}: {}", bucketName, e.getMessage(), e);
return false;
}
}
/**
* Upsert a document (insert or update)
* @param bucketName Bucket name
* @param documentId Document ID
* @param document Document object
* @return true if document upserted successfully, false otherwise
*/
public static boolean upsertDocument(String bucketName, String documentId, Object document) {
try {
MutationResult result = getCluster().bucket(bucketName)
.defaultCollection()
.upsert(documentId, document);
logger.info("Document upserted with ID: {} in bucket: {}", documentId, bucketName);
return true;
} catch (Exception e) {
logger.error("Error upserting document in bucket {}: {}", bucketName, e.getMessage(), e);
return false;
}
}
/**
* Upsert a document with options
* @param bucketName Bucket name
* @param documentId Document ID
* @param document Document object
* @param options Upsert options
* @return true if document upserted successfully, false otherwise
*/
public static boolean upsertDocument(String bucketName, String documentId, Object document, UpsertOptions options) {
try {
MutationResult result = getCluster().bucket(bucketName)
.defaultCollection()
.upsert(documentId, document, options);
logger.info("Document upserted with ID: {} in bucket: {}", documentId, bucketName);
return true;
} catch (Exception e) {
logger.error("Error upserting document in bucket {}: {}", bucketName, e.getMessage(), e);
return false;
}
}
/**
* Replace a document
* @param bucketName Bucket name
* @param documentId Document ID
* @param document Document object
* @return true if document replaced successfully, false otherwise
*/
public static boolean replaceDocument(String bucketName, String documentId, Object document) {
try {
MutationResult result = getCluster().bucket(bucketName)
.defaultCollection()
.replace(documentId, document);
logger.info("Document replaced with ID: {} in bucket: {}", documentId, bucketName);
return true;
} catch (Exception e) {
logger.error("Error replacing document in bucket {}: {}", bucketName, e.getMessage(), e);
return false;
}
}
/**
* Replace a document with options
* @param bucketName Bucket name
* @param documentId Document ID
* @param document Document object
* @param options Replace options
* @return true if document replaced successfully, false otherwise
*/
public static boolean replaceDocument(String bucketName, String documentId, Object document, ReplaceOptions options) {
try {
MutationResult result = getCluster().bucket(bucketName)
.defaultCollection()
.replace(documentId, document, options);
logger.info("Document replaced with ID: {} in bucket: {}", documentId, bucketName);
return true;
} catch (Exception e) {
logger.error("Error replacing document in bucket {}: {}", bucketName, e.getMessage(), e);
return false;
}
}
/**
* Delete a document
* @param bucketName Bucket name
* @param documentId Document ID
* @return true if document deleted successfully, false otherwise
*/
public static boolean deleteDocument(String bucketName, String documentId) {
try {
MutationResult result = getCluster().bucket(bucketName)
.defaultCollection()
.remove(documentId);
logger.info("Document deleted with ID: {} in bucket: {}", documentId, bucketName);
return true;
} catch (Exception e) {
logger.error("Error deleting document from bucket {}: {}", bucketName, e.getMessage(), e);
return false;
}
}
/**
* Execute a N1QL query
* @param bucketName Bucket name
* @param query N1QL query
* @return Query result
*/
public static QueryResult executeQuery(String bucketName, String query) {
try {
QueryResult result = getCluster().bucket(bucketName)
.scope("_default")
.query(query);
logger.info("Query executed successfully: {}", query);
return result;
} catch (Exception e) {
logger.error("Error executing query: {} - {}", query, e.getMessage(), e);
throw new RuntimeException("Failed to execute query: " + query, e);
}
}
/**
* Execute a N1QL query with parameters
* @param bucketName Bucket name
* @param query N1QL query
* @param parameters Query parameters
* @return Query result
*/
public static QueryResult executeQuery(String bucketName, String query, Map<String, Object> parameters) {
try {
QueryOptions options = QueryOptions.queryOptions()
.parameters(JsonObject.from(parameters));
QueryResult result = getCluster().bucket(bucketName)
.scope("_default")
.query(query, options);
logger.info("Query executed successfully: {} with parameters: {}", query, parameters);
return result;
} catch (Exception e) {
logger.error("Error executing query: {} with parameters: {} - {}", query, parameters, e.getMessage(), e);
throw new RuntimeException("Failed to execute query: " + query, e);
}
}
/**
* Get all documents from a bucket
* @param bucketName Bucket name
* @param documentClass Class of the documents
* @return List of documents
*/
public static <T> List<T> getAllDocuments(String bucketName, Class<T> documentClass) {
try {
String query = "SELECT META().id as id, `*` FROM `" + bucketName + "`";
QueryResult result = executeQuery(bucketName, query);
List<T> documents = new ArrayList<>();
for (JsonObject row : result.rowsAsObject()) {
// Extract the document content (excluding the id field)
JsonObject content = row.getObject(bucketName);
if (content != null) {
T document = objectMapper.readValue(content.toString(), documentClass);
documents.add(document);
}
}
logger.info("Retrieved {} documents from bucket: {}", documents.size(), bucketName);
return documents;
} catch (Exception e) {
logger.error("Error getting all documents from bucket {}: {}", bucketName, e.getMessage(), e);
return new ArrayList<>();
}
}
/**
* Store test execution results
* @param testName Test name
* @param status Test status
* @param duration Test duration in milliseconds
* @param result Test result details
* @return Document ID if stored successfully, null otherwise
*/
public static String storeTestResult(String testName, String status, long duration, Map<String, Object> result) {
String bucketName = configManager.getCouchbaseBucketName();
// Create test result document
Map<String, Object> testResult = new HashMap<>();
testResult.put("testName", testName);
testResult.put("status", status);
testResult.put("duration", duration);
testResult.put("timestamp", System.currentTimeMillis());
testResult.put("result", result);
testResult.put("type", "test_result");
// Generate document ID
String documentId = "test_result::" + testName + "::" + System.currentTimeMillis();
if (upsertDocument(bucketName, documentId, testResult)) {
return documentId;
}
return null;
}
/**
* Store test logs
* @param testName Test name
* @param level Log level
* @param message Log message
* @param timestamp Log timestamp
* @return Document ID if stored successfully, null otherwise
*/
public static String storeTestLog(String testName, String level, String message, long timestamp) {
String bucketName = configManager.getCouchbaseBucketName();
// Create log document
Map<String, Object> logEntry = new HashMap<>();
logEntry.put("testName", testName);
logEntry.put("level", level);
logEntry.put("message", message);
logEntry.put("timestamp", timestamp);
logEntry.put("type", "test_log");
// Generate document ID
String documentId = "test_log::" + testName + "::" + System.currentTimeMillis();
if (upsertDocument(bucketName, documentId, logEntry)) {
return documentId;
}
return null;
}
/**
* Get test results by test name
* @param testName Test name
* @return List of test results
*/
public static List<Map<String, Object>> getTestResults(String testName) {
String bucketName = configManager.getCouchbaseBucketName();
try {
String query = "SELECT `*` FROM `" + bucketName + "` WHERE type = 'test_result' AND testName = $testName ORDER BY timestamp DESC";
Map<String, Object> parameters = new HashMap<>();
parameters.put("testName", testName);
QueryResult result = executeQuery(bucketName, query, parameters);
List<Map<String, Object>> testResults = new ArrayList<>();
for (JsonObject row : result.rowsAsObject()) {
// Extract the document content
JsonObject content = row.getObject(bucketName);
if (content != null) {
Map<String, Object> testResult = objectMapper.readValue(content.toString(), Map.class);
testResults.add(testResult);
}
}
logger.info("Retrieved {} test results for test: {}", testResults.size(), testName);
return testResults;
} catch (Exception e) {
logger.error("Error getting test results for test {}: {}", testName, e.getMessage(), e);
return new ArrayList<>();
}
}
/**
* Get test logs by test name
* @param testName Test name
* @return List of test logs
*/
public static List<Map<String, Object>> getTestLogs(String testName) {
String bucketName = configManager.getCouchbaseBucketName();
try {
String query = "SELECT `*` FROM `" + bucketName + "` WHERE type = 'test_log' AND testName = $testName ORDER BY timestamp DESC";
Map<String, Object> parameters = new HashMap<>();
parameters.put("testName", testName);
QueryResult result = executeQuery(bucketName, query, parameters);
List<Map<String, Object>> testLogs = new ArrayList<>();
for (JsonObject row : result.rowsAsObject()) {
// Extract the document content
JsonObject content = row.getObject(bucketName);
if (content != null) {
Map<String, Object> logEntry = objectMapper.readValue(content.toString(), Map.class);
testLogs.add(logEntry);
}
}
logger.info("Retrieved {} test logs for test: {}", testLogs.size(), testName);
return testLogs;
} catch (Exception e) {
logger.error("Error getting test logs for test {}: {}", testName, e.getMessage(), e);
return new ArrayList<>();
}
}
}

View File

@@ -0,0 +1,274 @@
package com.financial.api.utils;
import com.financial.api.config.ConfigManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Utility class for database operations to support SQL validation.
*/
public class DatabaseUtil {
private static final Logger logger = LogManager.getLogger(DatabaseUtil.class);
private static ConfigManager configManager;
private static Connection connection;
static {
configManager = ConfigManager.getInstance();
}
/**
* Get database connection
* @return Database connection
* @throws SQLException if a database access error occurs
*/
public static Connection getConnection() throws SQLException {
if (connection == null || connection.isClosed()) {
String url = configManager.getDbUrl();
String username = configManager.getDbUsername();
String password = configManager.getDbPassword();
logger.info("Establishing database connection to: {}", url);
connection = DriverManager.getConnection(url, username, password);
logger.info("Database connection established successfully");
}
return connection;
}
/**
* Close database connection
*/
public static void closeConnection() {
if (connection != null) {
try {
connection.close();
logger.info("Database connection closed");
} catch (SQLException e) {
logger.error("Error closing database connection: {}", e.getMessage(), e);
}
}
}
/**
* Execute a query and return the result as a list of maps
* @param query SQL query
* @return List of maps representing the result set
* @throws SQLException if a database access error occurs
*/
public static List<Map<String, Object>> executeQuery(String query) throws SQLException {
return executeQuery(query, null);
}
/**
* Execute a query with parameters and return the result as a list of maps
* @param query SQL query
* @param parameters Query parameters
* @return List of maps representing the result set
* @throws SQLException if a database access error occurs
*/
public static List<Map<String, Object>> executeQuery(String query, List<Object> parameters) throws SQLException {
List<Map<String, Object>> result = new ArrayList<>();
try (Connection conn = getConnection();
PreparedStatement statement = conn.prepareStatement(query)) {
// Set parameters if any
if (parameters != null) {
for (int i = 0; i < parameters.size(); i++) {
statement.setObject(i + 1, parameters.get(i));
}
}
// Execute query
try (ResultSet resultSet = statement.executeQuery()) {
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
// Process result set
while (resultSet.next()) {
Map<String, Object> row = new HashMap<>();
for (int i = 1; i <= columnCount; i++) {
String columnName = metaData.getColumnName(i);
Object value = resultSet.getObject(i);
row.put(columnName, value);
}
result.add(row);
}
}
}
logger.debug("Executed query: {} with parameters: {}", query, parameters);
logger.debug("Query returned {} rows", result.size());
return result;
}
/**
* Execute an update/insert/delete statement
* @param query SQL statement
* @return Number of rows affected
* @throws SQLException if a database access error occurs
*/
public static int executeUpdate(String query) throws SQLException {
return executeUpdate(query, null);
}
/**
* Execute an update/insert/delete statement with parameters
* @param query SQL statement
* @param parameters Statement parameters
* @return Number of rows affected
* @throws SQLException if a database access error occurs
*/
public static int executeUpdate(String query, List<Object> parameters) throws SQLException {
int rowsAffected;
try (Connection conn = getConnection();
PreparedStatement statement = conn.prepareStatement(query)) {
// Set parameters if any
if (parameters != null) {
for (int i = 0; i < parameters.size(); i++) {
statement.setObject(i + 1, parameters.get(i));
}
}
// Execute update
rowsAffected = statement.executeUpdate();
}
logger.debug("Executed update: {} with parameters: {}", query, parameters);
logger.debug("Update affected {} rows", rowsAffected);
return rowsAffected;
}
/**
* Execute a query and return a single value
* @param query SQL query
* @return Single value result
* @throws SQLException if a database access error occurs
*/
public static Object executeScalar(String query) throws SQLException {
return executeScalar(query, null);
}
/**
* Execute a query with parameters and return a single value
* @param query SQL query
* @param parameters Query parameters
* @return Single value result
* @throws SQLException if a database access error occurs
*/
public static Object executeScalar(String query, List<Object> parameters) throws SQLException {
Object result = null;
try (Connection conn = getConnection();
PreparedStatement statement = conn.prepareStatement(query)) {
// Set parameters if any
if (parameters != null) {
for (int i = 0; i < parameters.size(); i++) {
statement.setObject(i + 1, parameters.get(i));
}
}
// Execute query
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
result = resultSet.getObject(1);
}
}
}
logger.debug("Executed scalar query: {} with parameters: {}", query, parameters);
logger.debug("Scalar query returned: {}", result);
return result;
}
/**
* Check if a table exists in the database
* @param tableName Table name
* @return true if table exists, false otherwise
* @throws SQLException if a database access error occurs
*/
public static boolean tableExists(String tableName) throws SQLException {
String query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_name = ?";
List<Object> parameters = new ArrayList<>();
parameters.add(tableName);
Object result = executeScalar(query, parameters);
return result != null && ((Long) result) > 0;
}
/**
* Get row count from a table
* @param tableName Table name
* @return Number of rows in the table
* @throws SQLException if a database access error occurs
*/
public static int getRowCount(String tableName) throws SQLException {
String query = "SELECT COUNT(*) FROM " + tableName;
Object result = executeScalar(query);
return result != null ? ((Long) result).intValue() : 0;
}
/**
* Validate data consistency between API response and database
* @param query SQL query to validate
* @param expectedValue Expected value
* @return true if validation passes, false otherwise
* @throws SQLException if a database access error occurs
*/
public static boolean validateData(String query, Object expectedValue) throws SQLException {
Object actualValue = executeScalar(query);
return (expectedValue == null && actualValue == null) ||
(expectedValue != null && expectedValue.equals(actualValue));
}
/**
* Validate data consistency between API response and database with parameters
* @param query SQL query to validate
* @param parameters Query parameters
* @param expectedValue Expected value
* @return true if validation passes, false otherwise
* @throws SQLException if a database access error occurs
*/
public static boolean validateData(String query, List<Object> parameters, Object expectedValue) throws SQLException {
Object actualValue = executeScalar(query, parameters);
return (expectedValue == null && actualValue == null) ||
(expectedValue != null && expectedValue.equals(actualValue));
}
/**
* Execute a batch of SQL statements
* @param queries List of SQL statements
* @return Array of update counts for each statement
* @throws SQLException if a database access error occurs
*/
public static int[] executeBatch(List<String> queries) throws SQLException {
int[] result;
try (Connection conn = getConnection();
Statement statement = conn.createStatement()) {
// Add all statements to batch
for (String query : queries) {
statement.addBatch(query);
}
// Execute batch
result = statement.executeBatch();
}
logger.debug("Executed batch of {} statements", queries.size());
return result;
}
}

View File

@@ -0,0 +1,395 @@
package com.financial.api.utils;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.json.JsonData;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.financial.api.config.ConfigManager;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.client.RestClient;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Utility class for Elasticsearch operations to store and retrieve test data and logs.
*/
public class ElasticsearchUtil {
private static final Logger logger = LogManager.getLogger(ElasticsearchUtil.class);
private static ConfigManager configManager;
private static ElasticsearchClient client;
private static ObjectMapper objectMapper;
static {
configManager = ConfigManager.getInstance();
objectMapper = new ObjectMapper();
}
/**
* Get Elasticsearch client
* @return Elasticsearch client
*/
public static ElasticsearchClient getClient() {
if (client == null) {
initializeClient();
}
return client;
}
/**
* Initialize Elasticsearch client
*/
private static void initializeClient() {
try {
String host = configManager.getElasticsearchHost();
int port = configManager.getElasticsearchPort();
// Create the low-level client
RestClient restClient = RestClient.builder(
new HttpHost(host, port, "http"))
.build();
// Create the transport with a Jackson mapper
ElasticsearchTransport transport = new RestClientTransport(
restClient, new JacksonJsonpMapper());
// Create the API client
client = new ElasticsearchClient(transport);
logger.info("Elasticsearch client initialized successfully");
} catch (Exception e) {
logger.error("Error initializing Elasticsearch client: {}", e.getMessage(), e);
throw new RuntimeException("Failed to initialize Elasticsearch client", e);
}
}
/**
* Check if Elasticsearch is running
* @return true if Elasticsearch is running, false otherwise
*/
public static boolean isRunning() {
try {
PingResponse pingResponse = getClient().ping();
return pingResponse.value();
} catch (IOException e) {
logger.error("Error pinging Elasticsearch: {}", e.getMessage(), e);
return false;
}
}
/**
* Create an index
* @param indexName Index name
* @return true if index created successfully, false otherwise
*/
public static boolean createIndex(String indexName) {
try {
CreateIndexResponse createIndexResponse = getClient().indices()
.create(c -> c.index(indexName));
logger.info("Index {} created successfully: {}", indexName, createIndexResponse.acknowledged());
return createIndexResponse.acknowledged();
} catch (IOException e) {
logger.error("Error creating index {}: {}", indexName, e.getMessage(), e);
return false;
}
}
/**
* Check if an index exists
* @param indexName Index name
* @return true if index exists, false otherwise
*/
public static boolean indexExists(String indexName) {
try {
return getClient().indices().exists(e -> e.index(indexName)).value();
} catch (IOException e) {
logger.error("Error checking if index {} exists: {}", indexName, e.getMessage(), e);
return false;
}
}
/**
* Index a document
* @param indexName Index name
* @param documentId Document ID
* @param document Document object
* @return true if document indexed successfully, false otherwise
*/
public static boolean indexDocument(String indexName, String documentId, Object document) {
try {
IndexRequest<Object> request = IndexRequest.of(i -> i
.index(indexName)
.id(documentId)
.document(document)
);
IndexResponse response = getClient().index(request);
logger.info("Document indexed with ID: {} in index: {}", response.id(), response.index());
return true;
} catch (IOException e) {
logger.error("Error indexing document in index {}: {}", indexName, e.getMessage(), e);
return false;
}
}
/**
* Index a document with auto-generated ID
* @param indexName Index name
* @param document Document object
* @return Document ID if indexed successfully, null otherwise
*/
public static String indexDocument(String indexName, Object document) {
try {
IndexRequest<Object> request = IndexRequest.of(i -> i
.index(indexName)
.document(document)
);
IndexResponse response = getClient().index(request);
logger.info("Document indexed with ID: {} in index: {}", response.id(), response.index());
return response.id();
} catch (IOException e) {
logger.error("Error indexing document in index {}: {}", indexName, e.getMessage(), e);
return null;
}
}
/**
* Get a document by ID
* @param indexName Index name
* @param documentId Document ID
* @param documentClass Class of the document
* @return Document object if found, null otherwise
*/
public static <T> T getDocument(String indexName, String documentId, Class<T> documentClass) {
try {
GetRequest getRequest = GetRequest.of(g -> g
.index(indexName)
.id(documentId)
);
GetResponse<T> getResponse = getClient().get(getRequest, documentClass);
if (getResponse.found()) {
logger.info("Document found with ID: {} in index: {}", documentId, indexName);
return getResponse.source();
} else {
logger.info("Document not found with ID: {} in index: {}", documentId, indexName);
return null;
}
} catch (IOException e) {
logger.error("Error getting document from index {}: {}", indexName, e.getMessage(), e);
return null;
}
}
/**
* Search documents
* @param indexName Index name
* @param query Search query
* @param documentClass Class of the documents
* @return List of documents
*/
public static <T> List<T> searchDocuments(String indexName, Query query, Class<T> documentClass) {
try {
SearchRequest searchRequest = SearchRequest.of(s -> s
.index(indexName)
.query(query)
);
SearchResponse<T> searchResponse = getClient().search(searchRequest, documentClass);
List<T> documents = new ArrayList<>();
for (Hit<T> hit : searchResponse.hits().hits()) {
documents.add(hit.source());
}
logger.info("Found {} documents in index: {}", documents.size(), indexName);
return documents;
} catch (IOException e) {
logger.error("Error searching documents in index {}: {}", indexName, e.getMessage(), e);
return new ArrayList<>();
}
}
/**
* Search all documents in an index
* @param indexName Index name
* @param documentClass Class of the documents
* @return List of documents
*/
public static <T> List<T> searchAllDocuments(String indexName, Class<T> documentClass) {
try {
SearchRequest searchRequest = SearchRequest.of(s -> s
.index(indexName)
.query(Query.of(q -> q.matchAll(m -> m)))
);
SearchResponse<T> searchResponse = getClient().search(searchRequest, documentClass);
List<T> documents = new ArrayList<>();
for (Hit<T> hit : searchResponse.hits().hits()) {
documents.add(hit.source());
}
logger.info("Found {} documents in index: {}", documents.size(), indexName);
return documents;
} catch (IOException e) {
logger.error("Error searching all documents in index {}: {}", indexName, e.getMessage(), e);
return new ArrayList<>();
}
}
/**
* Update a document
* @param indexName Index name
* @param documentId Document ID
* @param document Document object with updates
* @return true if document updated successfully, false otherwise
*/
public static boolean updateDocument(String indexName, String documentId, Object document) {
try {
Map<String, Object> documentMap = objectMapper.convertValue(document, Map.class);
UpdateRequest<Map<String, Object>, Object> request = UpdateRequest.of(u -> u
.index(indexName)
.id(documentId)
.doc(documentMap)
);
UpdateResponse<Object> response = getClient().update(request, Object.class);
logger.info("Document updated with ID: {} in index: {}", documentId, indexName);
return true;
} catch (IOException e) {
logger.error("Error updating document in index {}: {}", indexName, e.getMessage(), e);
return false;
}
}
/**
* Delete a document
* @param indexName Index name
* @param documentId Document ID
* @return true if document deleted successfully, false otherwise
*/
public static boolean deleteDocument(String indexName, String documentId) {
try {
DeleteRequest request = DeleteRequest.of(d -> d
.index(indexName)
.id(documentId)
);
DeleteResponse response = getClient().delete(request);
logger.info("Document deleted with ID: {} in index: {}", documentId, indexName);
return true;
} catch (IOException e) {
logger.error("Error deleting document from index {}: {}", indexName, e.getMessage(), e);
return false;
}
}
/**
* Delete an index
* @param indexName Index name
* @return true if index deleted successfully, false otherwise
*/
public static boolean deleteIndex(String indexName) {
try {
DeleteIndexResponse response = getClient().indices().delete(d -> d.index(indexName));
logger.info("Index {} deleted successfully: {}", indexName, response.acknowledged());
return response.acknowledged();
} catch (IOException e) {
logger.error("Error deleting index {}: {}", indexName, e.getMessage(), e);
return false;
}
}
/**
* Count documents in an index
* @param indexName Index name
* @return Number of documents
*/
public static long countDocuments(String indexName) {
try {
CountRequest countRequest = CountRequest.of(c -> c
.index(indexName)
);
CountResponse countResponse = getClient().count(countRequest);
long count = countResponse.count();
logger.info("Counted {} documents in index: {}", count, indexName);
return count;
} catch (IOException e) {
logger.error("Error counting documents in index {}: {}", indexName, e.getMessage(), e);
return 0;
}
}
/**
* Store test execution results
* @param testName Test name
* @param status Test status
* @param duration Test duration in milliseconds
* @param result Test result details
* @return Document ID if stored successfully, null otherwise
*/
public static String storeTestResult(String testName, String status, long duration, Map<String, Object> result) {
String indexName = configManager.getProperty("elasticsearch.index.prefix", "financial_test_") + "results";
// Create index if it doesn't exist
if (!indexExists(indexName)) {
createIndex(indexName);
}
// Create test result document
Map<String, Object> testResult = new HashMap<>();
testResult.put("testName", testName);
testResult.put("status", status);
testResult.put("duration", duration);
testResult.put("timestamp", System.currentTimeMillis());
testResult.put("result", result);
return indexDocument(indexName, testResult);
}
/**
* Store test logs
* @param testName Test name
* @param level Log level
* @param message Log message
* @param timestamp Log timestamp
* @return Document ID if stored successfully, null otherwise
*/
public static String storeTestLog(String testName, String level, String message, long timestamp) {
String indexName = configManager.getProperty("elasticsearch.index.prefix", "financial_test_") + "logs";
// Create index if it doesn't exist
if (!indexExists(indexName)) {
createIndex(indexName);
}
// Create log document
Map<String, Object> logEntry = new HashMap<>();
logEntry.put("testName", testName);
logEntry.put("level", level);
logEntry.put("message", message);
logEntry.put("timestamp", timestamp);
return indexDocument(indexName, logEntry);
}
}

View File

@@ -0,0 +1,445 @@
package com.financial.api.utils;
import com.financial.api.models.credit.CreditApplication;
import com.financial.api.models.credit.CreditScore;
import com.financial.api.models.wallet.Transaction;
import com.financial.api.models.wallet.Wallet;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Year;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
/**
* Factory class to generate test data for wallet and credit scoring APIs.
*/
public class TestDataFactory {
private static final Logger logger = LogManager.getLogger(TestDataFactory.class);
private static final Random random = new Random();
/**
* Generate a random wallet for testing
* @return Wallet object
*/
public static Wallet generateRandomWallet() {
String customerId = UUID.randomUUID().toString();
String accountNumber = generateRandomAccountNumber();
BigDecimal balance = generateRandomBalance();
String currency = generateRandomCurrency();
Wallet wallet = new Wallet(customerId, accountNumber, balance, currency);
wallet.setCreatedBy("test_user");
logger.info("Generated random wallet: {}", wallet);
return wallet;
}
/**
* Generate a wallet with specific parameters
* @param customerId Customer ID
* @param balance Initial balance
* @param currency Currency code
* @return Wallet object
*/
public static Wallet generateWallet(String customerId, BigDecimal balance, String currency) {
String accountNumber = generateRandomAccountNumber();
Wallet wallet = new Wallet(customerId, accountNumber, balance, currency);
wallet.setCreatedBy("test_user");
logger.info("Generated wallet with parameters: {}", wallet);
return wallet;
}
/**
* Generate a random transaction for testing
* @param walletId Wallet ID
* @return Transaction object
*/
public static Transaction generateRandomTransaction(String walletId) {
Transaction.TransactionType type = generateRandomTransactionType();
BigDecimal amount = generateRandomAmount();
String currency = generateRandomCurrency();
String description = generateRandomDescription(type);
Transaction transaction = new Transaction(walletId, type, amount, currency, description);
transaction.setCreatedBy("test_user");
logger.info("Generated random transaction: {}", transaction);
return transaction;
}
/**
* Generate a transaction with specific parameters
* @param walletId Wallet ID
* @param type Transaction type
* @param amount Transaction amount
* @param currency Currency code
* @param description Transaction description
* @return Transaction object
*/
public static Transaction generateTransaction(String walletId, Transaction.TransactionType type,
BigDecimal amount, String currency, String description) {
Transaction transaction = new Transaction(walletId, type, amount, currency, description);
transaction.setCreatedBy("test_user");
logger.info("Generated transaction with parameters: {}", transaction);
return transaction;
}
/**
* Generate a random credit application for testing
* @return CreditApplication object
*/
public static CreditApplication generateRandomCreditApplication() {
String customerId = UUID.randomUUID().toString();
String applicantName = generateRandomName();
String applicantEmail = generateRandomEmail(applicantName);
String applicantPhone = generateRandomPhoneNumber();
LocalDate dateOfBirth = generateRandomDateOfBirth();
BigDecimal monthlyIncome = generateRandomMonthlyIncome();
BigDecimal loanAmount = generateRandomLoanAmount();
int loanTermMonths = generateRandomLoanTerm();
String loanPurpose = generateRandomLoanPurpose();
CreditApplication.EmploymentStatus employmentStatus = generateRandomEmploymentStatus();
CreditApplication application = new CreditApplication(
customerId, applicantName, applicantEmail, applicantPhone, dateOfBirth,
monthlyIncome, loanAmount, loanTermMonths, loanPurpose, employmentStatus
);
logger.info("Generated random credit application: {}", application);
return application;
}
/**
* Generate a credit application with specific parameters
* @param customerId Customer ID
* @param applicantName Applicant name
* @param monthlyIncome Monthly income
* @param loanAmount Loan amount
* @param loanTermMonths Loan term in months
* @param loanPurpose Loan purpose
* @param employmentStatus Employment status
* @return CreditApplication object
*/
public static CreditApplication generateCreditApplication(
String customerId, String applicantName, BigDecimal monthlyIncome,
BigDecimal loanAmount, int loanTermMonths, String loanPurpose,
CreditApplication.EmploymentStatus employmentStatus
) {
String applicantEmail = generateRandomEmail(applicantName);
String applicantPhone = generateRandomPhoneNumber();
LocalDate dateOfBirth = generateRandomDateOfBirth();
CreditApplication application = new CreditApplication(
customerId, applicantName, applicantEmail, applicantPhone, dateOfBirth,
monthlyIncome, loanAmount, loanTermMonths, loanPurpose, employmentStatus
);
logger.info("Generated credit application with parameters: {}", application);
return application;
}
/**
* Generate a random credit score for testing
* @param applicationId Application ID
* @param customerId Customer ID
* @return CreditScore object
*/
public static CreditScore generateRandomCreditScore(String applicationId, String customerId) {
int score = generateRandomCreditScoreValue();
String calculationMethod = generateRandomCalculationMethod();
CreditScore creditScore = new CreditScore(applicationId, customerId, score, calculationMethod);
logger.info("Generated random credit score: {}", creditScore);
return creditScore;
}
/**
* Generate a credit score with specific parameters
* @param applicationId Application ID
* @param customerId Customer ID
* @param score Credit score value
* @param calculationMethod Calculation method
* @return CreditScore object
*/
public static CreditScore generateCreditScore(
String applicationId, String customerId, int score, String calculationMethod
) {
CreditScore creditScore = new CreditScore(applicationId, customerId, score, calculationMethod);
logger.info("Generated credit score with parameters: {}", creditScore);
return creditScore;
}
/**
* Generate a random account number
* @return Random account number
*/
private static String generateRandomAccountNumber() {
return "ACC" + String.format("%010d", random.nextInt(1000000000));
}
/**
* Generate a random balance
* @return Random balance
*/
private static BigDecimal generateRandomBalance() {
double min = 100.0;
double max = 10000.0;
double balance = min + (max - min) * random.nextDouble();
return BigDecimal.valueOf(balance).setScale(2, RoundingMode.HALF_UP);
}
/**
* Generate a random currency
* @return Random currency code
*/
private static String generateRandomCurrency() {
String[] currencies = {"USD", "EUR", "GBP", "JPY", "CAD", "AUD"};
return currencies[random.nextInt(currencies.length)];
}
/**
* Generate a random transaction type
* @return Random transaction type
*/
private static Transaction.TransactionType generateRandomTransactionType() {
Transaction.TransactionType[] types = Transaction.TransactionType.values();
return types[random.nextInt(types.length)];
}
/**
* Generate a random amount
* @return Random amount
*/
private static BigDecimal generateRandomAmount() {
double min = 10.0;
double max = 1000.0;
double amount = min + (max - min) * random.nextDouble();
return BigDecimal.valueOf(amount).setScale(2, RoundingMode.HALF_UP);
}
/**
* Generate a random description based on transaction type
* @param type Transaction type
* @return Random description
*/
private static String generateRandomDescription(Transaction.TransactionType type) {
switch (type) {
case DEPOSIT:
return "Deposit from " + generateRandomName();
case WITHDRAWAL:
return "Withdrawal to " + generateRandomName();
case TRANSFER:
return "Transfer to " + generateRandomName();
case PAYMENT:
return "Payment to " + generateRandomName();
case REFUND:
return "Refund from " + generateRandomName();
default:
return "Transaction description";
}
}
/**
* Generate a random name
* @return Random name
*/
private static String generateRandomName() {
String[] firstNames = {"John", "Jane", "Michael", "Emily", "David", "Sarah", "Robert", "Lisa"};
String[] lastNames = {"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis"};
String firstName = firstNames[random.nextInt(firstNames.length)];
String lastName = lastNames[random.nextInt(lastNames.length)];
return firstName + " " + lastName;
}
/**
* Generate a random email
* @param name Name
* @return Random email
*/
private static String generateRandomEmail(String name) {
String[] domains = {"gmail.com", "yahoo.com", "hotmail.com", "outlook.com"};
String domain = domains[random.nextInt(domains.length)];
// Remove spaces and convert to lowercase
String emailName = name.toLowerCase().replace(" ", ".");
// Add random number to avoid duplicates
int randomNumber = random.nextInt(1000);
return emailName + randomNumber + "@" + domain;
}
/**
* Generate a random phone number
* @return Random phone number
*/
private static String generateRandomPhoneNumber() {
return "+" + (random.nextInt(90) + 10) + " " +
String.format("%03d", random.nextInt(1000)) + " " +
String.format("%03d", random.nextInt(1000)) + " " +
String.format("%04d", random.nextInt(10000));
}
/**
* Generate a random date of birth
* @return Random date of birth
*/
private static LocalDate generateRandomDateOfBirth() {
int minYear = Year.now().getValue() - 70; // 70 years ago
int maxYear = Year.now().getValue() - 18; // 18 years ago
int year = minYear + random.nextInt(maxYear - minYear + 1);
int month = 1 + random.nextInt(12);
int day = 1 + random.nextInt(28); // Simplified to avoid month/day validation
return LocalDate.of(year, month, day);
}
/**
* Generate a random monthly income
* @return Random monthly income
*/
private static BigDecimal generateRandomMonthlyIncome() {
double min = 1000.0;
double max = 20000.0;
double income = min + (max - min) * random.nextDouble();
return BigDecimal.valueOf(income).setScale(2, RoundingMode.HALF_UP);
}
/**
* Generate a random loan amount
* @return Random loan amount
*/
private static BigDecimal generateRandomLoanAmount() {
double min = 1000.0;
double max = 100000.0;
double amount = min + (max - min) * random.nextDouble();
return BigDecimal.valueOf(amount).setScale(2, RoundingMode.HALF_UP);
}
/**
* Generate a random loan term
* @return Random loan term in months
*/
private static int generateRandomLoanTerm() {
int[] terms = {12, 24, 36, 48, 60, 72, 84, 96, 108, 120};
return terms[random.nextInt(terms.length)];
}
/**
* Generate a random loan purpose
* @return Random loan purpose
*/
private static String generateRandomLoanPurpose() {
String[] purposes = {
"Home Purchase", "Home Improvement", "Debt Consolidation",
"Car Purchase", "Education", "Business", "Medical Expenses",
"Vacation", "Wedding", "Other"
};
return purposes[random.nextInt(purposes.length)];
}
/**
* Generate a random employment status
* @return Random employment status
*/
private static CreditApplication.EmploymentStatus generateRandomEmploymentStatus() {
CreditApplication.EmploymentStatus[] statuses = CreditApplication.EmploymentStatus.values();
return statuses[random.nextInt(statuses.length)];
}
/**
* Generate a random credit score value
* @return Random credit score value
*/
private static int generateRandomCreditScoreValue() {
return 300 + random.nextInt(551); // 300-850
}
/**
* Generate a random calculation method
* @return Random calculation method
*/
private static String generateRandomCalculationMethod() {
String[] methods = {"FICO", "VantageScore", "CustomAlgorithm"};
return methods[random.nextInt(methods.length)];
}
/**
* Generate test data map for API requests
* @param dataType Type of test data to generate
* @return Map of test data
*/
public static Map<String, Object> generateTestDataMap(String dataType) {
Map<String, Object> testData = new HashMap<>();
switch (dataType.toLowerCase()) {
case "wallet":
Wallet wallet = generateRandomWallet();
testData.put("customerId", wallet.getCustomerId());
testData.put("accountNumber", wallet.getAccountNumber());
testData.put("balance", wallet.getBalance());
testData.put("currency", wallet.getCurrency());
testData.put("status", wallet.getStatus());
break;
case "transaction":
String walletId = UUID.randomUUID().toString();
Transaction transaction = generateRandomTransaction(walletId);
testData.put("walletId", transaction.getWalletId());
testData.put("type", transaction.getType());
testData.put("amount", transaction.getAmount());
testData.put("currency", transaction.getCurrency());
testData.put("description", transaction.getDescription());
testData.put("status", transaction.getStatus());
break;
case "creditapplication":
CreditApplication application = generateRandomCreditApplication();
testData.put("customerId", application.getCustomerId());
testData.put("applicantName", application.getApplicantName());
testData.put("applicantEmail", application.getApplicantEmail());
testData.put("applicantPhone", application.getApplicantPhone());
testData.put("dateOfBirth", application.getDateOfBirth());
testData.put("monthlyIncome", application.getMonthlyIncome());
testData.put("loanAmount", application.getLoanAmount());
testData.put("loanTermMonths", application.getLoanTermMonths());
testData.put("loanPurpose", application.getLoanPurpose());
testData.put("employmentStatus", application.getEmploymentStatus());
testData.put("status", application.getStatus());
break;
case "creditscore":
String applicationId = UUID.randomUUID().toString();
String customerId = UUID.randomUUID().toString();
CreditScore creditScore = generateRandomCreditScore(applicationId, customerId);
testData.put("applicationId", creditScore.getApplicationId());
testData.put("customerId", creditScore.getCustomerId());
testData.put("score", creditScore.getScore());
testData.put("scoreBand", creditScore.getScoreBand());
testData.put("rating", creditScore.getRating());
testData.put("calculationMethod", creditScore.getCalculationMethod());
break;
default:
logger.warn("Unknown data type: {}", dataType);
break;
}
return testData;
}
}

View File

@@ -0,0 +1,207 @@
package com.financial.api.utils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Test listener for handling test events and generating reports
*/
public class TestListener implements ITestListener {
private static final Logger logger = LogManager.getLogger(TestListener.class);
private static final String PROJECT_NAME = "Dracarys";
// Test statistics
private final ConcurrentHashMap<String, AtomicInteger> testStats = new ConcurrentHashMap<>();
private final AtomicInteger passedTests = new AtomicInteger(0);
private final AtomicInteger failedTests = new AtomicInteger(0);
private final AtomicInteger skippedTests = new AtomicInteger(0);
private final AtomicInteger totalTests = new AtomicInteger(0);
// Test timing
private final ConcurrentHashMap<String, Long> testStartTimes = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Long> testEndTimes = new ConcurrentHashMap<>();
@Override
public void onStart(ITestContext context) {
logger.info("======== {} Test Suite: {} started ========", PROJECT_NAME, context.getName());
logger.info("Total test count: {}", context.getAllTestMethods().length);
// Reset statistics for new test suite
passedTests.set(0);
failedTests.set(0);
skippedTests.set(0);
totalTests.set(context.getAllTestMethods().length);
}
@Override
public void onFinish(ITestContext context) {
logger.info("======== {} Test Suite: {} finished ========", PROJECT_NAME, context.getName());
// Print test statistics
logger.info("Test Statistics:");
logger.info(" Total tests: {}", totalTests.get());
logger.info(" Passed tests: {}", passedTests.get());
logger.info(" Failed tests: {}", failedTests.get());
logger.info(" Skipped tests: {}", skippedTests.get());
// Calculate success rate
double successRate = totalTests.get() > 0 ?
(double) passedTests.get() / totalTests.get() * 100 : 0;
logger.info(" Success rate: {:.2f}%", successRate);
// Print test group statistics
logger.info("Test Group Statistics:");
testStats.forEach((group, count) ->
logger.info(" {}: {}", group, count.get()));
// Generate HTML report (in a real implementation, this would generate a detailed HTML report)
generateHtmlReport(context);
}
@Override
public void onTestStart(ITestResult result) {
String testName = result.getMethod().getMethodName();
String className = result.getMethod().getTestClass().getName();
String testDescription = result.getMethod().getDescription() != null ?
result.getMethod().getDescription() : "No description";
logger.info("Starting test: {}.{} - {}", className, testName, testDescription);
// Record test start time
testStartTimes.put(testName, System.currentTimeMillis());
}
@Override
public void onTestSuccess(ITestResult result) {
String testName = result.getMethod().getMethodName();
String className = result.getMethod().getTestClass().getName();
// Record test end time
testEndTimes.put(testName, System.currentTimeMillis());
// Calculate test duration
long duration = testEndTimes.get(testName) - testStartTimes.get(testName);
logger.info("Test passed: {}.{} - Duration: {} ms", className, testName, duration);
// Update statistics
passedTests.incrementAndGet();
updateTestGroupStats(result);
// Log test parameters if any
if (result.getParameters() != null && result.getParameters().length > 0) {
logger.info("Test parameters:");
for (Object param : result.getParameters()) {
logger.info(" {}", param);
}
}
}
@Override
public void onTestFailure(ITestResult result) {
String testName = result.getMethod().getMethodName();
String className = result.getMethod().getTestClass().getName();
// Record test end time
testEndTimes.put(testName, System.currentTimeMillis());
// Calculate test duration
long duration = testEndTimes.get(testName) - testStartTimes.get(testName);
logger.error("Test failed: {}.{} - Duration: {} ms", className, testName, duration);
// Log exception
if (result.getThrowable() != null) {
logger.error("Exception: ", result.getThrowable());
}
// Update statistics
failedTests.incrementAndGet();
updateTestGroupStats(result);
// Log test parameters if any
if (result.getParameters() != null && result.getParameters().length > 0) {
logger.error("Test parameters:");
for (Object param : result.getParameters()) {
logger.error(" {}", param);
}
}
}
@Override
public void onTestSkipped(ITestResult result) {
String testName = result.getMethod().getMethodName();
String className = result.getMethod().getTestClass().getName();
logger.warn("Test skipped: {}.{}", className, testName);
// Update statistics
skippedTests.incrementAndGet();
updateTestGroupStats(result);
// Log skip reason if available
if (result.getThrowable() != null) {
logger.warn("Skip reason: {}", result.getThrowable().getMessage());
}
}
@Override
public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
String testName = result.getMethod().getMethodName();
String className = result.getMethod().getTestClass().getName();
logger.warn("Test failed but within success percentage: {}.{}", className, testName);
// Update statistics
failedTests.incrementAndGet();
updateTestGroupStats(result);
}
/**
* Update test group statistics
* @param result Test result
*/
private void updateTestGroupStats(ITestResult result) {
String[] groups = result.getMethod().getGroups();
if (groups != null && groups.length > 0) {
for (String group : groups) {
testStats.computeIfAbsent(group, k -> new AtomicInteger(0)).incrementAndGet();
}
} else {
testStats.computeIfAbsent("default", k -> new AtomicInteger(0)).incrementAndGet();
}
}
/**
* Generate HTML report
* @param context Test context
*/
private void generateHtmlReport(ITestContext context) {
// In a real implementation, this would generate a detailed HTML report
// For now, we'll just log that the report would be generated
logger.info("Generating {} HTML report...", PROJECT_NAME);
// Report file path
String reportPath = "test-output/" + context.getName() + "_report.html";
logger.info("{} HTML report would be generated at: {}", PROJECT_NAME, reportPath);
// In a real implementation, you would:
// 1. Create an HTML file
// 2. Add CSS styling
// 3. Add test suite information
// 4. Add test statistics
// 5. Add test results with pass/fail status
// 6. Add test execution times
// 7. Add any screenshots or logs
// 8. Add charts for visual representation of results
}
}

View File

@@ -0,0 +1,44 @@
# API Configuration
api.base.url=https://api.example.com
api.version=v1
api.timeout=10000
api.retry.count=3
# Database Configuration
db.url=jdbc:mysql://localhost:3306/financial_db
db.username=test_user
db.password=test_password
db.driver=com.mysql.cj.jdbc.Driver
# Elasticsearch Configuration
elasticsearch.host=localhost
elasticsearch.port=9200
elasticsearch.scheme=http
elasticsearch.index.prefix=financial_test_
# Couchbase Configuration
couchbase.host=localhost
couchbase.username=test_user
couchbase.password=test_password
couchbase.bucket.name=financial_test_data
couchbase.bucket.password=
# Test Configuration
test.data.dir=src/test/resources/testdata
test.report.dir=target/test-reports
test.log.level=INFO
# Performance Test Configuration
performance.test.users=100
performance.test.ramp-up=30
performance.test.duration=300
performance.test.throughput=10
# Environment Configuration (dev, staging, prod)
env=dev
# Logging Configuration
log.dir=target/logs
log.file.name=api-test-framework
log.max.size=10MB
log.max.history=30

View File

@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Properties>
<Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Property>
<Property name="LOG_DIR">target/logs</Property>
</Properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>
<RollingFile name="FileAppender"
fileName="${LOG_DIR}/api-test-framework.log"
filePattern="${LOG_DIR}/api-test-framework-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<DefaultRolloverStrategy max="30"/>
</RollingFile>
<RollingFile name="TestReportAppender"
fileName="${LOG_DIR}/test-reports.log"
filePattern="${LOG_DIR}/test-reports-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<DefaultRolloverStrategy max="30"/>
</RollingFile>
<RollingFile name="PerformanceAppender"
fileName="${LOG_DIR}/performance-tests.log"
filePattern="${LOG_DIR}/performance-tests-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<DefaultRolloverStrategy max="30"/>
</RollingFile>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="FileAppender"/>
</Root>
<Logger name="com.financial.api.tests" level="debug" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="FileAppender"/>
<AppenderRef ref="TestReportAppender"/>
</Logger>
<Logger name="com.financial.api.performance" level="info" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="FileAppender"/>
<AppenderRef ref="PerformanceAppender"/>
</Logger>
<Logger name="io.restassured" level="warn" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="FileAppender"/>
</Logger>
<Logger name="org.elasticsearch" level="warn" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="FileAppender"/>
</Logger>
<Logger name="com.couchbase" level="warn" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="FileAppender"/>
</Logger>
<Logger name="com.mysql" level="warn" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="FileAppender"/>
</Logger>
</Loggers>
</Configuration>

View File

@@ -0,0 +1,486 @@
package com.financial.api.tests;
import com.financial.api.models.credit.CreditApplication;
import com.financial.api.models.credit.CreditScore;
import com.financial.api.services.CreditService;
import com.financial.api.utils.DatabaseUtil;
import com.financial.api.utils.TestDataFactory;
import io.restassured.response.Response;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
/**
* Test class for Credit API operations
*/
public class CreditApiTest {
private static final Logger logger = LogManager.getLogger(CreditApiTest.class);
private CreditService creditService;
private CreditApplication testApplication;
private CreditScore testCreditScore;
@BeforeClass
public void setUp() {
logger.info("Setting up CreditApiTest");
creditService = new CreditService();
// Create test credit application
testApplication = TestDataFactory.generateRandomCreditApplication();
// Create test credit score
testCreditScore = TestDataFactory.generateRandomCreditScore(testApplication.getId(), testApplication.getCustomerId());
}
@AfterClass
public void tearDown() {
logger.info("Tearing down CreditApiTest");
// Clean up test data if needed
try {
// Close database connection
DatabaseUtil.closeConnection();
} catch (Exception e) {
logger.error("Error during tear down: {}", e.getMessage(), e);
}
}
@Test(description = "Submit a new credit application")
public void testSubmitApplication() {
logger.info("Executing testSubmitApplication");
// Submit application via API
Response response = creditService.submitApplication(testApplication);
// Validate response
Assert.assertEquals(response.getStatusCode(), 201, "Expected status code 201 for application submission");
// Parse response
CreditApplication submittedApplication = response.as(CreditApplication.class);
// Validate application data
Assert.assertNotNull(submittedApplication.getId(), "Application ID should not be null");
Assert.assertEquals(submittedApplication.getCustomerId(), testApplication.getCustomerId(), "Customer ID should match");
Assert.assertEquals(submittedApplication.getApplicantName(), testApplication.getApplicantName(), "Applicant name should match");
Assert.assertEquals(submittedApplication.getApplicantEmail(), testApplication.getApplicantEmail(), "Applicant email should match");
Assert.assertEquals(submittedApplication.getApplicantPhone(), testApplication.getApplicantPhone(), "Applicant phone should match");
Assert.assertEquals(submittedApplication.getDateOfBirth(), testApplication.getDateOfBirth(), "Date of birth should match");
Assert.assertEquals(submittedApplication.getMonthlyIncome(), testApplication.getMonthlyIncome(), "Monthly income should match");
Assert.assertEquals(submittedApplication.getLoanAmount(), testApplication.getLoanAmount(), "Loan amount should match");
Assert.assertEquals(submittedApplication.getLoanTermMonths(), testApplication.getLoanTermMonths(), "Loan term should match");
Assert.assertEquals(submittedApplication.getLoanPurpose(), testApplication.getLoanPurpose(), "Loan purpose should match");
Assert.assertEquals(submittedApplication.getEmploymentStatus(), testApplication.getEmploymentStatus(), "Employment status should match");
Assert.assertEquals(submittedApplication.getStatus(), CreditApplication.CreditApplicationStatus.SUBMITTED, "Application status should be SUBMITTED");
// Update test application with generated ID
testApplication.setId(submittedApplication.getId());
// Validate application data in database
validateApplicationInDatabase(submittedApplication);
logger.info("testSubmitApplication completed successfully");
}
@Test(description = "Get credit application by ID", dependsOnMethods = "testSubmitApplication")
public void testGetApplication() {
logger.info("Executing testGetApplication");
// Get application via API
Response response = creditService.getApplication(testApplication.getId());
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for getting application");
// Parse response
CreditApplication retrievedApplication = response.as(CreditApplication.class);
// Validate application data
Assert.assertEquals(retrievedApplication.getId(), testApplication.getId(), "Application ID should match");
Assert.assertEquals(retrievedApplication.getCustomerId(), testApplication.getCustomerId(), "Customer ID should match");
Assert.assertEquals(retrievedApplication.getApplicantName(), testApplication.getApplicantName(), "Applicant name should match");
Assert.assertEquals(retrievedApplication.getMonthlyIncome(), testApplication.getMonthlyIncome(), "Monthly income should match");
Assert.assertEquals(retrievedApplication.getLoanAmount(), testApplication.getLoanAmount(), "Loan amount should match");
Assert.assertEquals(retrievedApplication.getStatus(), CreditApplication.CreditApplicationStatus.SUBMITTED, "Application status should be SUBMITTED");
logger.info("testGetApplication completed successfully");
}
@Test(description = "Update credit application", dependsOnMethods = "testSubmitApplication")
public void testUpdateApplication() {
logger.info("Executing testUpdateApplication");
// Update application data
testApplication.setLoanAmount(new BigDecimal("25000.00"));
testApplication.setLoanTermMonths(48);
// Update application via API
Response response = creditService.updateApplication(testApplication.getId(), testApplication);
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for updating application");
// Parse response
CreditApplication updatedApplication = response.as(CreditApplication.class);
// Validate application data
Assert.assertEquals(updatedApplication.getId(), testApplication.getId(), "Application ID should match");
Assert.assertEquals(updatedApplication.getLoanAmount(), testApplication.getLoanAmount(), "Loan amount should match");
Assert.assertEquals(updatedApplication.getLoanTermMonths(), testApplication.getLoanTermMonths(), "Loan term should match");
// Validate application data in database
validateApplicationInDatabase(updatedApplication);
logger.info("testUpdateApplication completed successfully");
}
@Test(description = "Get credit applications by status", dependsOnMethods = "testSubmitApplication")
public void testGetApplicationsByStatus() {
logger.info("Executing testGetApplicationsByStatus");
// Get applications by status via API
Response response = creditService.getApplicationsByStatus(CreditApplication.CreditApplicationStatus.SUBMITTED);
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for getting applications by status");
// Parse response
List<Map<String, Object>> applications = response.jsonPath().getList("$");
// Validate applications
Assert.assertTrue(applications.size() > 0, "Should have at least one application with status SUBMITTED");
// Find our test application
boolean found = false;
for (Map<String, Object> applicationData : applications) {
if (applicationData.get("id").equals(testApplication.getId())) {
found = true;
break;
}
}
Assert.assertTrue(found, "Test application should be in the list");
logger.info("testGetApplicationsByStatus completed successfully");
}
@Test(description = "Request credit score calculation", dependsOnMethods = "testSubmitApplication")
public void testCalculateCreditScore() {
logger.info("Executing testCalculateCreditScore");
// Request credit score calculation via API
String calculationMethod = "FICO";
Response response = creditService.calculateCreditScore(testApplication.getId(), calculationMethod);
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for credit score calculation");
// Parse response
CreditScore calculatedScore = response.as(CreditScore.class);
// Validate credit score data
Assert.assertNotNull(calculatedScore.getId(), "Credit score ID should not be null");
Assert.assertEquals(calculatedScore.getApplicationId(), testApplication.getId(), "Application ID should match");
Assert.assertEquals(calculatedScore.getCustomerId(), testApplication.getCustomerId(), "Customer ID should match");
Assert.assertTrue(calculatedScore.getScore() >= 300 && calculatedScore.getScore() <= 850, "Credit score should be between 300 and 850");
Assert.assertEquals(calculatedScore.getCalculationMethod(), calculationMethod, "Calculation method should match");
// Update test credit score with generated ID
testCreditScore.setId(calculatedScore.getId());
testCreditScore.setScore(calculatedScore.getScore());
testCreditScore.setScoreBand(calculatedScore.getScoreBand());
testCreditScore.setRating(calculatedScore.getRating());
// Validate credit score data in database
validateCreditScoreInDatabase(calculatedScore);
logger.info("testCalculateCreditScore completed successfully");
}
@Test(description = "Get credit score for an application", dependsOnMethods = "testCalculateCreditScore")
public void testGetCreditScore() {
logger.info("Executing testGetCreditScore");
// Get credit score via API
Response response = creditService.getCreditScore(testApplication.getId());
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for getting credit score");
// Parse response
CreditScore retrievedScore = response.as(CreditScore.class);
// Validate credit score data
Assert.assertEquals(retrievedScore.getId(), testCreditScore.getId(), "Credit score ID should match");
Assert.assertEquals(retrievedScore.getApplicationId(), testApplication.getId(), "Application ID should match");
Assert.assertEquals(retrievedScore.getCustomerId(), testApplication.getCustomerId(), "Customer ID should match");
Assert.assertEquals(retrievedScore.getScore(), testCreditScore.getScore(), "Score should match");
Assert.assertEquals(retrievedScore.getScoreBand(), testCreditScore.getScoreBand(), "Score band should match");
Assert.assertEquals(retrievedScore.getRating(), testCreditScore.getRating(), "Rating should match");
Assert.assertEquals(retrievedScore.getCalculationMethod(), testCreditScore.getCalculationMethod(), "Calculation method should match");
logger.info("testGetCreditScore completed successfully");
}
@Test(description = "Get credit score history for an application", dependsOnMethods = "testCalculateCreditScore")
public void testGetCreditScoreHistory() {
logger.info("Executing testGetCreditScoreHistory");
// Get credit score history via API
Response response = creditService.getCreditScoreHistory(testApplication.getId());
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for getting credit score history");
// Parse response
List<Map<String, Object>> scoreHistory = response.jsonPath().getList("$");
// Validate score history
Assert.assertTrue(scoreHistory.size() > 0, "Should have at least one credit score in history");
// Find our test credit score
boolean found = false;
for (Map<String, Object> scoreData : scoreHistory) {
if (scoreData.get("id").equals(testCreditScore.getId())) {
found = true;
break;
}
}
Assert.assertTrue(found, "Test credit score should be in the history");
logger.info("testGetCreditScoreHistory completed successfully");
}
@Test(description = "Approve credit application", dependsOnMethods = "testCalculateCreditScore")
public void testApproveApplication() {
logger.info("Executing testApproveApplication");
// Only approve if credit score is good enough
if (testCreditScore.getScore() >= 670) {
// Approve application via API
double approvedAmount = testApplication.getLoanAmount().doubleValue();
double interestRate = 5.5; // Example interest rate
String reviewedBy = "test_reviewer";
Response response = creditService.approveApplication(
testApplication.getId(),
approvedAmount,
interestRate,
reviewedBy
);
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for approving application");
// Parse response
CreditApplication approvedApplication = response.as(CreditApplication.class);
// Validate application data
Assert.assertEquals(approvedApplication.getId(), testApplication.getId(), "Application ID should match");
Assert.assertEquals(approvedApplication.getStatus(), CreditApplication.CreditApplicationStatus.APPROVED, "Application status should be APPROVED");
Assert.assertEquals(approvedApplication.getApprovedAmount(), new BigDecimal(approvedAmount), "Approved amount should match");
Assert.assertEquals(approvedApplication.getInterestRate(), new BigDecimal(interestRate), "Interest rate should match");
Assert.assertEquals(approvedApplication.getReviewedBy(), reviewedBy, "Reviewed by should match");
// Update test application
testApplication.setStatus(approvedApplication.getStatus());
testApplication.setApprovedAmount(approvedApplication.getApprovedAmount());
testApplication.setInterestRate(approvedApplication.getInterestRate());
testApplication.setReviewedBy(approvedApplication.getReviewedBy());
// Validate application data in database
validateApplicationInDatabase(approvedApplication);
logger.info("testApproveApplication completed successfully");
} else {
logger.info("Skipping testApproveApplication due to low credit score: {}", testCreditScore.getScore());
}
}
@Test(description = "Reject credit application", dependsOnMethods = "testCalculateCreditScore")
public void testRejectApplication() {
logger.info("Executing testRejectApplication");
// Only reject if credit score is low enough and application is not already approved
if (testCreditScore.getScore() < 670 && testApplication.getStatus() != CreditApplication.CreditApplicationStatus.APPROVED) {
// Reject application via API
String rejectionReason = "Low credit score";
String reviewedBy = "test_reviewer";
Response response = creditService.rejectApplication(
testApplication.getId(),
rejectionReason,
reviewedBy
);
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for rejecting application");
// Parse response
CreditApplication rejectedApplication = response.as(CreditApplication.class);
// Validate application data
Assert.assertEquals(rejectedApplication.getId(), testApplication.getId(), "Application ID should match");
Assert.assertEquals(rejectedApplication.getStatus(), CreditApplication.CreditApplicationStatus.REJECTED, "Application status should be REJECTED");
Assert.assertEquals(rejectedApplication.getRejectionReason(), rejectionReason, "Rejection reason should match");
Assert.assertEquals(rejectedApplication.getReviewedBy(), reviewedBy, "Reviewed by should match");
// Update test application
testApplication.setStatus(rejectedApplication.getStatus());
testApplication.setRejectionReason(rejectedApplication.getRejectionReason());
testApplication.setReviewedBy(rejectedApplication.getReviewedBy());
// Validate application data in database
validateApplicationInDatabase(rejectedApplication);
logger.info("testRejectApplication completed successfully");
} else {
logger.info("Skipping testRejectApplication due to high credit score or already approved application");
}
}
@Test(description = "Get credit products")
public void testGetCreditProducts() {
logger.info("Executing testGetCreditProducts");
// Get credit products via API
Response response = creditService.getCreditProducts();
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for getting credit products");
// Parse response
List<Map<String, Object>> products = response.jsonPath().getList("$");
// Validate products
Assert.assertTrue(products.size() > 0, "Should have at least one credit product");
// Validate product structure
for (Map<String, Object> product : products) {
Assert.assertTrue(product.containsKey("id"), "Product should have an ID");
Assert.assertTrue(product.containsKey("name"), "Product should have a name");
Assert.assertTrue(product.containsKey("description"), "Product should have a description");
Assert.assertTrue(product.containsKey("minAmount"), "Product should have a minimum amount");
Assert.assertTrue(product.containsKey("maxAmount"), "Product should have a maximum amount");
Assert.assertTrue(product.containsKey("minTerm"), "Product should have a minimum term");
Assert.assertTrue(product.containsKey("maxTerm"), "Product should have a maximum term");
Assert.assertTrue(product.containsKey("interestRate"), "Product should have an interest rate");
}
logger.info("testGetCreditProducts completed successfully");
}
@Test(description = "Get credit product by ID")
public void testGetCreditProduct() {
logger.info("Executing testGetCreditProduct");
// First get all products to find a valid ID
Response productsResponse = creditService.getCreditProducts();
List<Map<String, Object>> products = productsResponse.jsonPath().getList("$");
Assert.assertTrue(products.size() > 0, "Should have at least one credit product");
String productId = (String) products.get(0).get("id");
// Get product by ID via API
Response response = creditService.getCreditProduct(productId);
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for getting credit product");
// Parse response
Map<String, Object> product = response.as(Map.class);
// Validate product data
Assert.assertEquals(product.get("id"), productId, "Product ID should match");
Assert.assertTrue(product.containsKey("name"), "Product should have a name");
Assert.assertTrue(product.containsKey("description"), "Product should have a description");
Assert.assertTrue(product.containsKey("minAmount"), "Product should have a minimum amount");
Assert.assertTrue(product.containsKey("maxAmount"), "Product should have a maximum amount");
Assert.assertTrue(product.containsKey("minTerm"), "Product should have a minimum term");
Assert.assertTrue(product.containsKey("maxTerm"), "Product should have a maximum term");
Assert.assertTrue(product.containsKey("interestRate"), "Product should have an interest rate");
logger.info("testGetCreditProduct completed successfully");
}
/**
* Validate application data in database
* @param application Application to validate
*/
private void validateApplicationInDatabase(CreditApplication application) {
try {
String query = "SELECT * FROM credit_applications WHERE id = ?";
List<Object> parameters = List.of(application.getId());
List<Map<String, Object>> results = DatabaseUtil.executeQuery(query, parameters);
Assert.assertEquals(results.size(), 1, "Should find exactly one application with ID: " + application.getId());
Map<String, Object> applicationData = results.get(0);
Assert.assertEquals(applicationData.get("customer_id"), application.getCustomerId(), "Customer ID should match in database");
Assert.assertEquals(applicationData.get("applicant_name"), application.getApplicantName(), "Applicant name should match in database");
Assert.assertEquals(applicationData.get("applicant_email"), application.getApplicantEmail(), "Applicant email should match in database");
Assert.assertEquals(applicationData.get("monthly_income"), application.getMonthlyIncome(), "Monthly income should match in database");
Assert.assertEquals(applicationData.get("loan_amount"), application.getLoanAmount(), "Loan amount should match in database");
Assert.assertEquals(applicationData.get("loan_term_months"), application.getLoanTermMonths(), "Loan term should match in database");
Assert.assertEquals(applicationData.get("loan_purpose"), application.getLoanPurpose(), "Loan purpose should match in database");
Assert.assertEquals(applicationData.get("employment_status"), application.getEmploymentStatus().toString(), "Employment status should match in database");
Assert.assertEquals(applicationData.get("status"), application.getStatus().toString(), "Status should match in database");
if (application.getApprovedAmount() != null) {
Assert.assertEquals(new BigDecimal(applicationData.get("approved_amount").toString()), application.getApprovedAmount(), "Approved amount should match in database");
}
if (application.getInterestRate() != null) {
Assert.assertEquals(new BigDecimal(applicationData.get("interest_rate").toString()), application.getInterestRate(), "Interest rate should match in database");
}
if (application.getRejectionReason() != null) {
Assert.assertEquals(applicationData.get("rejection_reason"), application.getRejectionReason(), "Rejection reason should match in database");
}
if (application.getReviewedBy() != null) {
Assert.assertEquals(applicationData.get("reviewed_by"), application.getReviewedBy(), "Reviewed by should match in database");
}
} catch (SQLException e) {
logger.error("Error validating application in database: {}", e.getMessage(), e);
Assert.fail("Error validating application in database: " + e.getMessage());
}
}
/**
* Validate credit score data in database
* @param creditScore Credit score to validate
*/
private void validateCreditScoreInDatabase(CreditScore creditScore) {
try {
String query = "SELECT * FROM credit_scores WHERE id = ?";
List<Object> parameters = List.of(creditScore.getId());
List<Map<String, Object>> results = DatabaseUtil.executeQuery(query, parameters);
Assert.assertEquals(results.size(), 1, "Should find exactly one credit score with ID: " + creditScore.getId());
Map<String, Object> scoreData = results.get(0);
Assert.assertEquals(scoreData.get("application_id"), creditScore.getApplicationId(), "Application ID should match in database");
Assert.assertEquals(scoreData.get("customer_id"), creditScore.getCustomerId(), "Customer ID should match in database");
Assert.assertEquals(scoreData.get("score"), creditScore.getScore(), "Score should match in database");
Assert.assertEquals(scoreData.get("score_band"), creditScore.getScoreBand(), "Score band should match in database");
Assert.assertEquals(scoreData.get("rating"), creditScore.getRating().toString(), "Rating should match in database");
Assert.assertEquals(scoreData.get("calculation_method"), creditScore.getCalculationMethod(), "Calculation method should match in database");
} catch (SQLException e) {
logger.error("Error validating credit score in database: {}", e.getMessage(), e);
Assert.fail("Error validating credit score in database: " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,469 @@
package com.financial.api.tests;
import com.financial.api.models.credit.CreditApplication;
import com.financial.api.models.credit.CreditScore;
import com.financial.api.services.CreditService;
import com.financial.api.utils.TestDataFactory;
import io.restassured.response.Response;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.util.ArrayList;
import java.util.List;
/**
* Performance test class for Credit API operations
*/
public class CreditPerformanceTest {
private static final Logger logger = LogManager.getLogger(CreditPerformanceTest.class);
private CreditService creditService;
private List<CreditApplication> testApplications = new ArrayList<>();
private List<CreditScore> testCreditScores = new ArrayList<>();
private static final int APPLICATIONS_TO_CREATE = 10;
private static final int MAX_RESPONSE_TIME_MS = 10000; // Maximum acceptable response time in milliseconds
@BeforeClass
public void setUp() {
logger.info("Setting up CreditPerformanceTest");
creditService = new CreditService();
// Create test credit applications
for (int i = 0; i < APPLICATIONS_TO_CREATE; i++) {
CreditApplication application = TestDataFactory.generateRandomCreditApplication();
testApplications.add(application);
}
}
@AfterClass
public void tearDown() {
logger.info("Tearing down CreditPerformanceTest");
// Clean up test data if needed
// Note: In a real scenario, you might want to delete the created applications and credit scores
}
@Test(description = "Performance test for submitting multiple credit applications", invocationCount = 1, threadPoolSize = 5)
public void testSubmitMultipleApplicationsPerformance() {
logger.info("Executing testSubmitMultipleApplicationsPerformance");
long startTime = System.currentTimeMillis();
List<Long> responseTimes = new ArrayList<>();
int successCount = 0;
for (CreditApplication application : testApplications) {
long requestStartTime = System.currentTimeMillis();
// Submit application via API
Response response = creditService.submitApplication(application);
long requestEndTime = System.currentTimeMillis();
long responseTime = requestEndTime - requestStartTime;
responseTimes.add(responseTime);
// Validate response
if (response.getStatusCode() == 201) {
successCount++;
// Parse response
CreditApplication submittedApplication = response.as(CreditApplication.class);
// Update application with generated ID
application.setId(submittedApplication.getId());
// Validate response time
Assert.assertTrue(responseTime <= MAX_RESPONSE_TIME_MS,
"Response time for submitting application should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + responseTime + "ms");
} else {
logger.error("Failed to submit application. Status code: {}", response.getStatusCode());
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
// Calculate statistics
double averageResponseTime = responseTimes.stream().mapToLong(Long::longValue).average().orElse(0);
long maxResponseTime = responseTimes.stream().mapToLong(Long::longValue).max().orElse(0);
long minResponseTime = responseTimes.stream().mapToLong(Long::longValue).min().orElse(0);
// Log performance metrics
logger.info("Performance metrics for submitting credit applications:");
logger.info("Total applications submitted: {}/{}", successCount, testApplications.size());
logger.info("Total time: {} ms", totalTime);
logger.info("Average response time: {} ms", averageResponseTime);
logger.info("Min response time: {} ms", minResponseTime);
logger.info("Max response time: {} ms", maxResponseTime);
// Validate performance
Assert.assertEquals(successCount, testApplications.size(),
"All applications should be submitted successfully. Submitted: " + successCount + ", Expected: " + testApplications.size());
Assert.assertTrue(averageResponseTime <= MAX_RESPONSE_TIME_MS,
"Average response time should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + averageResponseTime + "ms");
logger.info("testSubmitMultipleApplicationsPerformance completed successfully");
}
@Test(description = "Performance test for getting multiple credit applications", dependsOnMethods = "testSubmitMultipleApplicationsPerformance", invocationCount = 1, threadPoolSize = 5)
public void testGetMultipleApplicationsPerformance() {
logger.info("Executing testGetMultipleApplicationsPerformance");
long startTime = System.currentTimeMillis();
List<Long> responseTimes = new ArrayList<>();
int successCount = 0;
for (CreditApplication application : testApplications) {
long requestStartTime = System.currentTimeMillis();
// Get application via API
Response response = creditService.getApplication(application.getId());
long requestEndTime = System.currentTimeMillis();
long responseTime = requestEndTime - requestStartTime;
responseTimes.add(responseTime);
// Validate response
if (response.getStatusCode() == 200) {
successCount++;
// Validate response time
Assert.assertTrue(responseTime <= MAX_RESPONSE_TIME_MS,
"Response time for getting application should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + responseTime + "ms");
} else {
logger.error("Failed to get application with ID: {}. Status code: {}", application.getId(), response.getStatusCode());
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
// Calculate statistics
double averageResponseTime = responseTimes.stream().mapToLong(Long::longValue).average().orElse(0);
long maxResponseTime = responseTimes.stream().mapToLong(Long::longValue).max().orElse(0);
long minResponseTime = responseTimes.stream().mapToLong(Long::longValue).min().orElse(0);
// Log performance metrics
logger.info("Performance metrics for getting credit applications:");
logger.info("Total applications retrieved: {}/{}", successCount, testApplications.size());
logger.info("Total time: {} ms", totalTime);
logger.info("Average response time: {} ms", averageResponseTime);
logger.info("Min response time: {} ms", minResponseTime);
logger.info("Max response time: {} ms", maxResponseTime);
// Validate performance
Assert.assertEquals(successCount, testApplications.size(),
"All applications should be retrieved successfully. Retrieved: " + successCount + ", Expected: " + testApplications.size());
Assert.assertTrue(averageResponseTime <= MAX_RESPONSE_TIME_MS,
"Average response time should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + averageResponseTime + "ms");
logger.info("testGetMultipleApplicationsPerformance completed successfully");
}
@Test(description = "Performance test for calculating credit scores for multiple applications", dependsOnMethods = "testSubmitMultipleApplicationsPerformance", invocationCount = 1, threadPoolSize = 5)
public void testCalculateMultipleCreditScoresPerformance() {
logger.info("Executing testCalculateMultipleCreditScoresPerformance");
long startTime = System.currentTimeMillis();
List<Long> responseTimes = new ArrayList<>();
int successCount = 0;
for (CreditApplication application : testApplications) {
long requestStartTime = System.currentTimeMillis();
// Request credit score calculation via API
String calculationMethod = "FICO";
Response response = creditService.calculateCreditScore(application.getId(), calculationMethod);
long requestEndTime = System.currentTimeMillis();
long responseTime = requestEndTime - requestStartTime;
responseTimes.add(responseTime);
// Validate response
if (response.getStatusCode() == 200) {
successCount++;
// Parse response
CreditScore calculatedScore = response.as(CreditScore.class);
// Create test credit score
CreditScore creditScore = new CreditScore(
application.getId(),
application.getCustomerId(),
calculatedScore.getScore(),
calculatedScore.getCalculationMethod()
);
creditScore.setId(calculatedScore.getId());
creditScore.setScoreBand(calculatedScore.getScoreBand());
creditScore.setRating(calculatedScore.getRating());
testCreditScores.add(creditScore);
// Validate response time
Assert.assertTrue(responseTime <= MAX_RESPONSE_TIME_MS,
"Response time for calculating credit score should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + responseTime + "ms");
} else {
logger.error("Failed to calculate credit score for application with ID: {}. Status code: {}",
application.getId(), response.getStatusCode());
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
// Calculate statistics
double averageResponseTime = responseTimes.stream().mapToLong(Long::longValue).average().orElse(0);
long maxResponseTime = responseTimes.stream().mapToLong(Long::longValue).max().orElse(0);
long minResponseTime = responseTimes.stream().mapToLong(Long::longValue).min().orElse(0);
// Log performance metrics
logger.info("Performance metrics for calculating credit scores:");
logger.info("Total credit scores calculated: {}/{}", successCount, testApplications.size());
logger.info("Total time: {} ms", totalTime);
logger.info("Average response time: {} ms", averageResponseTime);
logger.info("Min response time: {} ms", minResponseTime);
logger.info("Max response time: {} ms", maxResponseTime);
// Validate performance
Assert.assertEquals(successCount, testApplications.size(),
"All credit scores should be calculated successfully. Calculated: " + successCount + ", Expected: " + testApplications.size());
Assert.assertTrue(averageResponseTime <= MAX_RESPONSE_TIME_MS,
"Average response time should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + averageResponseTime + "ms");
logger.info("testCalculateMultipleCreditScoresPerformance completed successfully");
}
@Test(description = "Performance test for getting credit scores for multiple applications", dependsOnMethods = "testCalculateMultipleCreditScoresPerformance", invocationCount = 1, threadPoolSize = 5)
public void testGetMultipleCreditScoresPerformance() {
logger.info("Executing testGetMultipleCreditScoresPerformance");
long startTime = System.currentTimeMillis();
List<Long> responseTimes = new ArrayList<>();
int successCount = 0;
for (CreditScore creditScore : testCreditScores) {
long requestStartTime = System.currentTimeMillis();
// Get credit score via API
Response response = creditService.getCreditScore(creditScore.getApplicationId());
long requestEndTime = System.currentTimeMillis();
long responseTime = requestEndTime - requestStartTime;
responseTimes.add(responseTime);
// Validate response
if (response.getStatusCode() == 200) {
successCount++;
// Validate response time
Assert.assertTrue(responseTime <= MAX_RESPONSE_TIME_MS,
"Response time for getting credit score should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + responseTime + "ms");
} else {
logger.error("Failed to get credit score for application with ID: {}. Status code: {}",
creditScore.getApplicationId(), response.getStatusCode());
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
// Calculate statistics
double averageResponseTime = responseTimes.stream().mapToLong(Long::longValue).average().orElse(0);
long maxResponseTime = responseTimes.stream().mapToLong(Long::longValue).max().orElse(0);
long minResponseTime = responseTimes.stream().mapToLong(Long::longValue).min().orElse(0);
// Log performance metrics
logger.info("Performance metrics for getting credit scores:");
logger.info("Total credit scores retrieved: {}/{}", successCount, testCreditScores.size());
logger.info("Total time: {} ms", totalTime);
logger.info("Average response time: {} ms", averageResponseTime);
logger.info("Min response time: {} ms", minResponseTime);
logger.info("Max response time: {} ms", maxResponseTime);
// Validate performance
Assert.assertEquals(successCount, testCreditScores.size(),
"All credit scores should be retrieved successfully. Retrieved: " + successCount + ", Expected: " + testCreditScores.size());
Assert.assertTrue(averageResponseTime <= MAX_RESPONSE_TIME_MS,
"Average response time should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + averageResponseTime + "ms");
logger.info("testGetMultipleCreditScoresPerformance completed successfully");
}
@Test(description = "Performance test for getting credit score history for multiple applications", dependsOnMethods = "testCalculateMultipleCreditScoresPerformance", invocationCount = 1, threadPoolSize = 5)
public void testGetMultipleCreditScoreHistoriesPerformance() {
logger.info("Executing testGetMultipleCreditScoreHistoriesPerformance");
long startTime = System.currentTimeMillis();
List<Long> responseTimes = new ArrayList<>();
int successCount = 0;
for (CreditScore creditScore : testCreditScores) {
long requestStartTime = System.currentTimeMillis();
// Get credit score history via API
Response response = creditService.getCreditScoreHistory(creditScore.getApplicationId());
long requestEndTime = System.currentTimeMillis();
long responseTime = requestEndTime - requestStartTime;
responseTimes.add(responseTime);
// Validate response
if (response.getStatusCode() == 200) {
successCount++;
// Validate response time
Assert.assertTrue(responseTime <= MAX_RESPONSE_TIME_MS,
"Response time for getting credit score history should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + responseTime + "ms");
} else {
logger.error("Failed to get credit score history for application with ID: {}. Status code: {}",
creditScore.getApplicationId(), response.getStatusCode());
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
// Calculate statistics
double averageResponseTime = responseTimes.stream().mapToLong(Long::longValue).average().orElse(0);
long maxResponseTime = responseTimes.stream().mapToLong(Long::longValue).max().orElse(0);
long minResponseTime = responseTimes.stream().mapToLong(Long::longValue).min().orElse(0);
// Log performance metrics
logger.info("Performance metrics for getting credit score histories:");
logger.info("Total credit score histories retrieved: {}/{}", successCount, testCreditScores.size());
logger.info("Total time: {} ms", totalTime);
logger.info("Average response time: {} ms", averageResponseTime);
logger.info("Min response time: {} ms", minResponseTime);
logger.info("Max response time: {} ms", maxResponseTime);
// Validate performance
Assert.assertEquals(successCount, testCreditScores.size(),
"All credit score histories should be retrieved successfully. Retrieved: " + successCount + ", Expected: " + testCreditScores.size());
Assert.assertTrue(averageResponseTime <= MAX_RESPONSE_TIME_MS,
"Average response time should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + averageResponseTime + "ms");
logger.info("testGetMultipleCreditScoreHistoriesPerformance completed successfully");
}
@Test(description = "Performance test for getting applications by status", dependsOnMethods = "testSubmitMultipleApplicationsPerformance", invocationCount = 1, threadPoolSize = 5)
public void testGetApplicationsByStatusPerformance() {
logger.info("Executing testGetApplicationsByStatusPerformance");
long startTime = System.currentTimeMillis();
List<Long> responseTimes = new ArrayList<>();
int successCount = 0;
// Test all possible statuses
CreditApplication.CreditApplicationStatus[] statuses = CreditApplication.CreditApplicationStatus.values();
for (CreditApplication.CreditApplicationStatus status : statuses) {
long requestStartTime = System.currentTimeMillis();
// Get applications by status via API
Response response = creditService.getApplicationsByStatus(status);
long requestEndTime = System.currentTimeMillis();
long responseTime = requestEndTime - requestStartTime;
responseTimes.add(responseTime);
// Validate response
if (response.getStatusCode() == 200) {
successCount++;
// Validate response time
Assert.assertTrue(responseTime <= MAX_RESPONSE_TIME_MS,
"Response time for getting applications by status should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + responseTime + "ms");
} else {
logger.error("Failed to get applications by status: {}. Status code: {}", status, response.getStatusCode());
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
// Calculate statistics
double averageResponseTime = responseTimes.stream().mapToLong(Long::longValue).average().orElse(0);
long maxResponseTime = responseTimes.stream().mapToLong(Long::longValue).max().orElse(0);
long minResponseTime = responseTimes.stream().mapToLong(Long::longValue).min().orElse(0);
// Log performance metrics
logger.info("Performance metrics for getting applications by status:");
logger.info("Total status queries completed: {}/{}", successCount, statuses.length);
logger.info("Total time: {} ms", totalTime);
logger.info("Average response time: {} ms", averageResponseTime);
logger.info("Min response time: {} ms", minResponseTime);
logger.info("Max response time: {} ms", maxResponseTime);
// Validate performance
Assert.assertEquals(successCount, statuses.length,
"All status queries should be completed successfully. Completed: " + successCount + ", Expected: " + statuses.length);
Assert.assertTrue(averageResponseTime <= MAX_RESPONSE_TIME_MS,
"Average response time should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + averageResponseTime + "ms");
logger.info("testGetApplicationsByStatusPerformance completed successfully");
}
@Test(description = "Performance test for getting credit products", invocationCount = 1, threadPoolSize = 5)
public void testGetCreditProductsPerformance() {
logger.info("Executing testGetCreditProductsPerformance");
long startTime = System.currentTimeMillis();
List<Long> responseTimes = new ArrayList<>();
int successCount = 0;
int iterations = 10; // Number of times to call the API
for (int i = 0; i < iterations; i++) {
long requestStartTime = System.currentTimeMillis();
// Get credit products via API
Response response = creditService.getCreditProducts();
long requestEndTime = System.currentTimeMillis();
long responseTime = requestEndTime - requestStartTime;
responseTimes.add(responseTime);
// Validate response
if (response.getStatusCode() == 200) {
successCount++;
// Validate response time
Assert.assertTrue(responseTime <= MAX_RESPONSE_TIME_MS,
"Response time for getting credit products should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + responseTime + "ms");
} else {
logger.error("Failed to get credit products. Status code: {}", response.getStatusCode());
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
// Calculate statistics
double averageResponseTime = responseTimes.stream().mapToLong(Long::longValue).average().orElse(0);
long maxResponseTime = responseTimes.stream().mapToLong(Long::longValue).max().orElse(0);
long minResponseTime = responseTimes.stream().mapToLong(Long::longValue).min().orElse(0);
// Log performance metrics
logger.info("Performance metrics for getting credit products:");
logger.info("Total credit products queries completed: {}/{}", successCount, iterations);
logger.info("Total time: {} ms", totalTime);
logger.info("Average response time: {} ms", averageResponseTime);
logger.info("Min response time: {} ms", minResponseTime);
logger.info("Max response time: {} ms", maxResponseTime);
// Validate performance
Assert.assertEquals(successCount, iterations,
"All credit products queries should be completed successfully. Completed: " + successCount + ", Expected: " + iterations);
Assert.assertTrue(averageResponseTime <= MAX_RESPONSE_TIME_MS,
"Average response time should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + averageResponseTime + "ms");
logger.info("testGetCreditProductsPerformance completed successfully");
}
}

View File

@@ -0,0 +1,496 @@
package com.financial.api.tests;
import com.financial.api.models.credit.CreditApplication;
import com.financial.api.models.credit.CreditScore;
import com.financial.api.services.CreditService;
import com.financial.api.utils.TestDataFactory;
import io.restassured.response.Response;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
/**
* Security test class for Credit API operations
*/
public class CreditSecurityTest {
private static final Logger logger = LogManager.getLogger(CreditSecurityTest.class);
private CreditService creditService;
private CreditApplication testApplication;
@BeforeClass
public void setUp() {
logger.info("Setting up CreditSecurityTest");
creditService = new CreditService();
// Create test credit application
testApplication = TestDataFactory.generateRandomCreditApplication();
// Create application via API for testing
Response response = creditService.submitApplication(testApplication);
if (response.getStatusCode() == 201) {
CreditApplication createdApplication = response.as(CreditApplication.class);
testApplication.setId(createdApplication.getId());
}
}
@Test(description = "Test SQL injection in credit application submission")
public void testSqlInjectionInCreditApplicationSubmission() {
logger.info("Executing testSqlInjectionInCreditApplicationSubmission");
// Create credit application with SQL injection payload
CreditApplication maliciousApplication = new CreditApplication(
"test-customer'; DROP TABLE credit_applications; --",
"Test Applicant",
"test@example.com",
"+1234567890",
java.time.LocalDate.of(1990, 1, 1),
new BigDecimal("5000.00"),
new BigDecimal("10000.00"),
24,
"Test purpose",
CreditApplication.EmploymentStatus.EMPLOYED
);
// Attempt to submit application via API
Response response = creditService.submitApplication(maliciousApplication);
// Validate response
// Should return 400 Bad Request or similar error code, not 201 Created
Assert.assertNotEquals(response.getStatusCode(), 201, "API should not submit application with SQL injection payload");
logger.info("testSqlInjectionInCreditApplicationSubmission completed successfully");
}
@Test(description = "Test XSS in credit application submission")
public void testXssInCreditApplicationSubmission() {
logger.info("Executing testXssInCreditApplicationSubmission");
// Create credit application with XSS payload
CreditApplication maliciousApplication = new CreditApplication(
"test-customer",
"<script>alert('XSS')</script>",
"test@example.com",
"+1234567890",
java.time.LocalDate.of(1990, 1, 1),
new BigDecimal("5000.00"),
new BigDecimal("10000.00"),
24,
"Test purpose",
CreditApplication.EmploymentStatus.EMPLOYED
);
// Attempt to submit application via API
Response response = creditService.submitApplication(maliciousApplication);
// Validate response
// Should return 400 Bad Request or similar error code, not 201 Created
Assert.assertNotEquals(response.getStatusCode(), 201, "API should not submit application with XSS payload");
logger.info("testXssInCreditApplicationSubmission completed successfully");
}
@Test(description = "Test authentication bypass in credit application retrieval")
public void testAuthenticationBypassInCreditApplicationRetrieval() {
logger.info("Executing testAuthenticationBypassInCreditApplicationRetrieval");
// Attempt to get application without authentication
Response response = creditService.getApplicationWithoutAuth(testApplication.getId());
// Validate response
// Should return 401 Unauthorized or 403 Forbidden
Assert.assertTrue(response.getStatusCode() == 401 || response.getStatusCode() == 403,
"API should require authentication for application retrieval");
logger.info("testAuthenticationBypassInCreditApplicationRetrieval completed successfully");
}
@Test(description = "Test authorization bypass in credit application retrieval")
public void testAuthorizationBypassInCreditApplicationRetrieval() {
logger.info("Executing testAuthorizationBypassInCreditApplicationRetrieval");
// Attempt to get application with different user's authentication
Response response = creditService.getApplicationWithDifferentUserAuth(testApplication.getId());
// Validate response
// Should return 403 Forbidden
Assert.assertEquals(response.getStatusCode(), 403, "API should prevent unauthorized access to application");
logger.info("testAuthorizationBypassInCreditApplicationRetrieval completed successfully");
}
@Test(description = "Test IDOR in credit application retrieval")
public void testIdorInCreditApplicationRetrieval() {
logger.info("Executing testIdorInCreditApplicationRetrieval");
// Attempt to get application with different application ID
String differentApplicationId = "different-application-id-12345";
Response response = creditService.getApplication(differentApplicationId);
// Validate response
// Should return 404 Not Found or 403 Forbidden
Assert.assertTrue(response.getStatusCode() == 404 || response.getStatusCode() == 403,
"API should prevent access to non-existent or unauthorized application");
logger.info("testIdorInCreditApplicationRetrieval completed successfully");
}
@Test(description = "Test SQL injection in credit score calculation")
public void testSqlInjectionInCreditScoreCalculation() {
logger.info("Executing testSqlInjectionInCreditScoreCalculation");
// Attempt to calculate credit score with SQL injection payload
String maliciousMethod = "FICO'; DROP TABLE credit_scores; --";
Response response = creditService.calculateCreditScore(testApplication.getId(), maliciousMethod);
// Validate response
// Should return 400 Bad Request or similar error code, not 200 OK
Assert.assertNotEquals(response.getStatusCode(), 200, "API should not calculate credit score with SQL injection payload");
logger.info("testSqlInjectionInCreditScoreCalculation completed successfully");
}
@Test(description = "Test XSS in credit score calculation")
public void testXssInCreditScoreCalculation() {
logger.info("Executing testXssInCreditScoreCalculation");
// Attempt to calculate credit score with XSS payload
String maliciousMethod = "<script>alert('XSS')</script>";
Response response = creditService.calculateCreditScore(testApplication.getId(), maliciousMethod);
// Validate response
// Should return 400 Bad Request or similar error code, not 200 OK
Assert.assertNotEquals(response.getStatusCode(), 200, "API should not calculate credit score with XSS payload");
logger.info("testXssInCreditScoreCalculation completed successfully");
}
@Test(description = "Test negative loan amount in credit application submission")
public void testNegativeLoanAmountInCreditApplicationSubmission() {
logger.info("Executing testNegativeLoanAmountInCreditApplicationSubmission");
// Create credit application with negative loan amount
CreditApplication maliciousApplication = new CreditApplication(
"test-customer",
"Test Applicant",
"test@example.com",
"+1234567890",
java.time.LocalDate.of(1990, 1, 1),
new BigDecimal("5000.00"),
new BigDecimal("-10000.00"),
24,
"Test purpose",
CreditApplication.EmploymentStatus.EMPLOYED
);
// Attempt to submit application via API
Response response = creditService.submitApplication(maliciousApplication);
// Validate response
// Should return 400 Bad Request or similar error code, not 201 Created
Assert.assertNotEquals(response.getStatusCode(), 201, "API should not submit application with negative loan amount");
logger.info("testNegativeLoanAmountInCreditApplicationSubmission completed successfully");
}
@Test(description = "Test extremely large loan amount in credit application submission")
public void testExtremelyLargeLoanAmountInCreditApplicationSubmission() {
logger.info("Executing testExtremelyLargeLoanAmountInCreditApplicationSubmission");
// Create credit application with extremely large loan amount
CreditApplication maliciousApplication = new CreditApplication(
"test-customer",
"Test Applicant",
"test@example.com",
"+1234567890",
java.time.LocalDate.of(1990, 1, 1),
new BigDecimal("5000.00"),
new BigDecimal("99999999999999999999.99"),
24,
"Test purpose",
CreditApplication.EmploymentStatus.EMPLOYED
);
// Attempt to submit application via API
Response response = creditService.submitApplication(maliciousApplication);
// Validate response
// Should return 400 Bad Request or similar error code, not 201 Created
Assert.assertNotEquals(response.getStatusCode(), 201, "API should not submit application with extremely large loan amount");
logger.info("testExtremelyLargeLoanAmountInCreditApplicationSubmission completed successfully");
}
@Test(description = "Test negative loan term in credit application submission")
public void testNegativeLoanTermInCreditApplicationSubmission() {
logger.info("Executing testNegativeLoanTermInCreditApplicationSubmission");
// Create credit application with negative loan term
CreditApplication maliciousApplication = new CreditApplication(
"test-customer",
"Test Applicant",
"test@example.com",
"+1234567890",
java.time.LocalDate.of(1990, 1, 1),
new BigDecimal("5000.00"),
new BigDecimal("10000.00"),
-24,
"Test purpose",
CreditApplication.EmploymentStatus.EMPLOYED
);
// Attempt to submit application via API
Response response = creditService.submitApplication(maliciousApplication);
// Validate response
// Should return 400 Bad Request or similar error code, not 201 Created
Assert.assertNotEquals(response.getStatusCode(), 201, "API should not submit application with negative loan term");
logger.info("testNegativeLoanTermInCreditApplicationSubmission completed successfully");
}
@Test(description = "Test extremely large loan term in credit application submission")
public void testExtremelyLargeLoanTermInCreditApplicationSubmission() {
logger.info("Executing testExtremelyLargeLoanTermInCreditApplicationSubmission");
// Create credit application with extremely large loan term
CreditApplication maliciousApplication = new CreditApplication(
"test-customer",
"Test Applicant",
"test@example.com",
"+1234567890",
java.time.LocalDate.of(1990, 1, 1),
new BigDecimal("5000.00"),
new BigDecimal("10000.00"),
999999,
"Test purpose",
CreditApplication.EmploymentStatus.EMPLOYED
);
// Attempt to submit application via API
Response response = creditService.submitApplication(maliciousApplication);
// Validate response
// Should return 400 Bad Request or similar error code, not 201 Created
Assert.assertNotEquals(response.getStatusCode(), 201, "API should not submit application with extremely large loan term");
logger.info("testExtremelyLargeLoanTermInCreditApplicationSubmission completed successfully");
}
@Test(description = "Test authentication bypass in credit score retrieval")
public void testAuthenticationBypassInCreditScoreRetrieval() {
logger.info("Executing testAuthenticationBypassInCreditScoreRetrieval");
// Attempt to get credit score without authentication
Response response = creditService.getCreditScoreWithoutAuth(testApplication.getId());
// Validate response
// Should return 401 Unauthorized or 403 Forbidden
Assert.assertTrue(response.getStatusCode() == 401 || response.getStatusCode() == 403,
"API should require authentication for credit score retrieval");
logger.info("testAuthenticationBypassInCreditScoreRetrieval completed successfully");
}
@Test(description = "Test authorization bypass in credit score retrieval")
public void testAuthorizationBypassInCreditScoreRetrieval() {
logger.info("Executing testAuthorizationBypassInCreditScoreRetrieval");
// Attempt to get credit score with different user's authentication
Response response = creditService.getCreditScoreWithDifferentUserAuth(testApplication.getId());
// Validate response
// Should return 403 Forbidden
Assert.assertEquals(response.getStatusCode(), 403, "API should prevent unauthorized access to credit score");
logger.info("testAuthorizationBypassInCreditScoreRetrieval completed successfully");
}
@Test(description = "Test CSRF protection in credit application update")
public void testCsrfProtectionInCreditApplicationUpdate() {
logger.info("Executing testCsrfProtectionInCreditApplicationUpdate");
// Attempt to update application without CSRF token
Map<String, Object> updateData = new HashMap<>();
updateData.put("loan_amount", new BigDecimal("15000.00"));
Response response = creditService.updateApplicationWithoutCsrfToken(testApplication.getId(), updateData);
// Validate response
// Should return 400 Bad Request or 403 Forbidden if CSRF protection is enabled
Assert.assertTrue(response.getStatusCode() == 400 || response.getStatusCode() == 403,
"API should require CSRF token for application update");
logger.info("testCsrfProtectionInCreditApplicationUpdate completed successfully");
}
@Test(description = "Test rate limiting in credit application submission")
public void testRateLimitingInCreditApplicationSubmission() {
logger.info("Executing testRateLimitingInCreditApplicationSubmission");
int requestCount = 10;
int tooManyRequestsCount = 0;
// Send multiple requests in quick succession
for (int i = 0; i < requestCount; i++) {
CreditApplication application = TestDataFactory.generateRandomCreditApplication();
Response response = creditService.submitApplication(application);
if (response.getStatusCode() == 429) { // Too Many Requests
tooManyRequestsCount++;
}
}
// Validate response
// Should receive at least one 429 Too Many Requests response if rate limiting is enabled
Assert.assertTrue(tooManyRequestsCount > 0, "API should implement rate limiting for application submission");
logger.info("testRateLimitingInCreditApplicationSubmission completed successfully");
}
@Test(description = "Test input validation in credit application submission")
public void testInputValidationInCreditApplicationSubmission() {
logger.info("Executing testInputValidationInCreditApplicationSubmission");
// Test cases for input validation
Map<String, CreditApplication> testCases = new HashMap<>();
// Empty customer ID
testCases.put("Empty customer ID", new CreditApplication(
"", "Test Applicant", "test@example.com", "+1234567890",
java.time.LocalDate.of(1990, 1, 1), new BigDecimal("5000.00"),
new BigDecimal("10000.00"), 24, "Test purpose",
CreditApplication.EmploymentStatus.EMPLOYED
));
// Empty applicant name
testCases.put("Empty applicant name", new CreditApplication(
"test-customer", "", "test@example.com", "+1234567890",
java.time.LocalDate.of(1990, 1, 1), new BigDecimal("5000.00"),
new BigDecimal("10000.00"), 24, "Test purpose",
CreditApplication.EmploymentStatus.EMPLOYED
));
// Invalid email
testCases.put("Invalid email", new CreditApplication(
"test-customer", "Test Applicant", "invalid-email", "+1234567890",
java.time.LocalDate.of(1990, 1, 1), new BigDecimal("5000.00"),
new BigDecimal("10000.00"), 24, "Test purpose",
CreditApplication.EmploymentStatus.EMPLOYED
));
// Invalid phone number
testCases.put("Invalid phone number", new CreditApplication(
"test-customer", "Test Applicant", "test@example.com", "invalid-phone",
java.time.LocalDate.of(1990, 1, 1), new BigDecimal("5000.00"),
new BigDecimal("10000.00"), 24, "Test purpose",
CreditApplication.EmploymentStatus.EMPLOYED
));
// Null monthly income
testCases.put("Null monthly income", new CreditApplication(
"test-customer", "Test Applicant", "test@example.com", "+1234567890",
java.time.LocalDate.of(1990, 1, 1), null,
new BigDecimal("10000.00"), 24, "Test purpose",
CreditApplication.EmploymentStatus.EMPLOYED
));
// Null loan amount
testCases.put("Null loan amount", new CreditApplication(
"test-customer", "Test Applicant", "test@example.com", "+1234567890",
java.time.LocalDate.of(1990, 1, 1), new BigDecimal("5000.00"),
null, 24, "Test purpose",
CreditApplication.EmploymentStatus.EMPLOYED
));
// Empty loan purpose
testCases.put("Empty loan purpose", new CreditApplication(
"test-customer", "Test Applicant", "test@example.com", "+1234567890",
java.time.LocalDate.of(1990, 1, 1), new BigDecimal("5000.00"),
new BigDecimal("10000.00"), 24, "",
CreditApplication.EmploymentStatus.EMPLOYED
));
// Null employment status
testCases.put("Null employment status", new CreditApplication(
"test-customer", "Test Applicant", "test@example.com", "+1234567890",
java.time.LocalDate.of(1990, 1, 1), new BigDecimal("5000.00"),
new BigDecimal("10000.00"), 24, "Test purpose",
null
));
// Test each case
for (Map.Entry<String, CreditApplication> entry : testCases.entrySet()) {
String testCase = entry.getKey();
CreditApplication application = entry.getValue();
Response response = creditService.submitApplication(application);
// Validate response
// Should return 400 Bad Request or similar error code, not 201 Created
Assert.assertNotEquals(response.getStatusCode(), 201,
"API should validate input for " + testCase + " and not submit application");
logger.info("Input validation test passed for: {}", testCase);
}
logger.info("testInputValidationInCreditApplicationSubmission completed successfully");
}
@Test(description = "Test sensitive data exposure in credit application response")
public void testSensitiveDataExposureInCreditApplicationResponse() {
logger.info("Executing testSensitiveDataExposureInCreditApplicationResponse");
// Get application via API
Response response = creditService.getApplication(testApplication.getId());
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for getting application");
// Parse response
Map<String, Object> applicationData = response.as(Map.class);
// Check for sensitive data that should not be exposed
Assert.assertFalse(applicationData.containsKey("password"), "Application response should not contain password");
Assert.assertFalse(applicationData.containsKey("pin"), "Application response should not contain PIN");
Assert.assertFalse(applicationData.containsKey("ssn"), "Application response should not contain SSN");
Assert.assertFalse(applicationData.containsKey("credit_card_number"), "Application response should not contain credit card number");
Assert.assertFalse(applicationData.containsKey("bank_account_number"), "Application response should not contain bank account number");
logger.info("testSensitiveDataExposureInCreditApplicationResponse completed successfully");
}
@Test(description = "Test sensitive data exposure in credit score response")
public void testSensitiveDataExposureInCreditScoreResponse() {
logger.info("Executing testSensitiveDataExposureInCreditScoreResponse");
// First calculate a credit score
Response scoreResponse = creditService.calculateCreditScore(testApplication.getId(), "FICO");
Assert.assertEquals(scoreResponse.getStatusCode(), 200, "Expected status code 200 for calculating credit score");
CreditScore creditScore = scoreResponse.as(CreditScore.class);
// Get credit score via API
Response response = creditService.getCreditScore(testApplication.getId());
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for getting credit score");
// Parse response
Map<String, Object> scoreData = response.as(Map.class);
// Check for sensitive data that should not be exposed
Assert.assertFalse(scoreData.containsKey("password"), "Credit score response should not contain password");
Assert.assertFalse(scoreData.containsKey("pin"), "Credit score response should not contain PIN");
Assert.assertFalse(scoreData.containsKey("ssn"), "Credit score response should not contain SSN");
Assert.assertFalse(scoreData.containsKey("credit_card_number"), "Credit score response should not contain credit card number");
Assert.assertFalse(scoreData.containsKey("bank_account_number"), "Credit score response should not contain bank account number");
logger.info("testSensitiveDataExposureInCreditScoreResponse completed successfully");
}
}

View File

@@ -0,0 +1,420 @@
package com.financial.api.tests;
import com.financial.api.models.wallet.Transaction;
import com.financial.api.models.wallet.Wallet;
import com.financial.api.services.WalletService;
import com.financial.api.utils.DatabaseUtil;
import com.financial.api.utils.TestDataFactory;
import io.restassured.response.Response;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Test class for Wallet API operations
*/
public class WalletApiTest {
private static final Logger logger = LogManager.getLogger(WalletApiTest.class);
private WalletService walletService;
private Wallet testWallet;
private Transaction testTransaction;
@BeforeClass
public void setUp() {
logger.info("Setting up WalletApiTest");
walletService = new WalletService();
// Create test wallet
testWallet = TestDataFactory.generateRandomWallet();
// Create test transaction
testTransaction = TestDataFactory.generateRandomTransaction(testWallet.getId());
}
@AfterClass
public void tearDown() {
logger.info("Tearing down WalletApiTest");
// Clean up test data if needed
try {
// Close database connection
DatabaseUtil.closeConnection();
} catch (Exception e) {
logger.error("Error during tear down: {}", e.getMessage(), e);
}
}
@Test(description = "Create a new wallet")
public void testCreateWallet() {
logger.info("Executing testCreateWallet");
// Create wallet via API
Response response = walletService.createWallet(testWallet);
// Validate response
Assert.assertEquals(response.getStatusCode(), 201, "Expected status code 201 for wallet creation");
// Parse response
Wallet createdWallet = response.as(Wallet.class);
// Validate wallet data
Assert.assertNotNull(createdWallet.getId(), "Wallet ID should not be null");
Assert.assertEquals(createdWallet.getCustomerId(), testWallet.getCustomerId(), "Customer ID should match");
Assert.assertEquals(createdWallet.getAccountNumber(), testWallet.getAccountNumber(), "Account number should match");
Assert.assertEquals(createdWallet.getBalance(), testWallet.getBalance(), "Balance should match");
Assert.assertEquals(createdWallet.getCurrency(), testWallet.getCurrency(), "Currency should match");
Assert.assertEquals(createdWallet.getStatus(), Wallet.WalletStatus.ACTIVE, "Wallet status should be ACTIVE");
// Update test wallet with generated ID
testWallet.setId(createdWallet.getId());
// Validate wallet data in database
validateWalletInDatabase(createdWallet);
logger.info("testCreateWallet completed successfully");
}
@Test(description = "Get wallet by ID", dependsOnMethods = "testCreateWallet")
public void testGetWallet() {
logger.info("Executing testGetWallet");
// Get wallet via API
Response response = walletService.getWallet(testWallet.getId());
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for getting wallet");
// Parse response
Wallet retrievedWallet = response.as(Wallet.class);
// Validate wallet data
Assert.assertEquals(retrievedWallet.getId(), testWallet.getId(), "Wallet ID should match");
Assert.assertEquals(retrievedWallet.getCustomerId(), testWallet.getCustomerId(), "Customer ID should match");
Assert.assertEquals(retrievedWallet.getAccountNumber(), testWallet.getAccountNumber(), "Account number should match");
Assert.assertEquals(retrievedWallet.getBalance(), testWallet.getBalance(), "Balance should match");
Assert.assertEquals(retrievedWallet.getCurrency(), testWallet.getCurrency(), "Currency should match");
logger.info("testGetWallet completed successfully");
}
@Test(description = "Get wallet by account number", dependsOnMethods = "testCreateWallet")
public void testGetWalletByAccountNumber() {
logger.info("Executing testGetWalletByAccountNumber");
// Get wallet via API
Response response = walletService.getWalletByAccountNumber(testWallet.getAccountNumber());
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for getting wallet by account number");
// Parse response
Wallet retrievedWallet = response.as(Wallet.class);
// Validate wallet data
Assert.assertEquals(retrievedWallet.getId(), testWallet.getId(), "Wallet ID should match");
Assert.assertEquals(retrievedWallet.getCustomerId(), testWallet.getCustomerId(), "Customer ID should match");
Assert.assertEquals(retrievedWallet.getAccountNumber(), testWallet.getAccountNumber(), "Account number should match");
Assert.assertEquals(retrievedWallet.getBalance(), testWallet.getBalance(), "Balance should match");
Assert.assertEquals(retrievedWallet.getCurrency(), testWallet.getCurrency(), "Currency should match");
logger.info("testGetWalletByAccountNumber completed successfully");
}
@Test(description = "Update wallet information", dependsOnMethods = "testCreateWallet")
public void testUpdateWallet() {
logger.info("Executing testUpdateWallet");
// Update wallet data
testWallet.setBalance(new BigDecimal("5000.00"));
// Update wallet via API
Response response = walletService.updateWallet(testWallet.getId(), testWallet);
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for updating wallet");
// Parse response
Wallet updatedWallet = response.as(Wallet.class);
// Validate wallet data
Assert.assertEquals(updatedWallet.getId(), testWallet.getId(), "Wallet ID should match");
Assert.assertEquals(updatedWallet.getBalance(), testWallet.getBalance(), "Balance should match");
// Validate wallet data in database
validateWalletInDatabase(updatedWallet);
logger.info("testUpdateWallet completed successfully");
}
@Test(description = "Get wallet balance", dependsOnMethods = "testCreateWallet")
public void testGetWalletBalance() {
logger.info("Executing testGetWalletBalance");
// Get wallet balance via API
Response response = walletService.getWalletBalance(testWallet.getId());
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for getting wallet balance");
// Parse response
Map<String, Object> balanceData = response.as(Map.class);
// Validate balance data
Assert.assertTrue(balanceData.containsKey("balance"), "Response should contain balance");
Assert.assertTrue(balanceData.containsKey("currency"), "Response should contain currency");
BigDecimal balance = new BigDecimal(balanceData.get("balance").toString());
String currency = (String) balanceData.get("currency");
Assert.assertEquals(balance, testWallet.getBalance(), "Balance should match");
Assert.assertEquals(currency, testWallet.getCurrency(), "Currency should match");
logger.info("testGetWalletBalance completed successfully");
}
@Test(description = "Create a transaction", dependsOnMethods = "testCreateWallet")
public void testCreateTransaction() {
logger.info("Executing testCreateTransaction");
// Create transaction via API
Response response = walletService.createTransaction(testWallet.getId(), testTransaction);
// Validate response
Assert.assertEquals(response.getStatusCode(), 201, "Expected status code 201 for transaction creation");
// Parse response
Transaction createdTransaction = response.as(Transaction.class);
// Validate transaction data
Assert.assertNotNull(createdTransaction.getId(), "Transaction ID should not be null");
Assert.assertEquals(createdTransaction.getWalletId(), testWallet.getId(), "Wallet ID should match");
Assert.assertEquals(createdTransaction.getType(), testTransaction.getType(), "Transaction type should match");
Assert.assertEquals(createdTransaction.getAmount(), testTransaction.getAmount(), "Amount should match");
Assert.assertEquals(createdTransaction.getCurrency(), testTransaction.getCurrency(), "Currency should match");
Assert.assertEquals(createdTransaction.getDescription(), testTransaction.getDescription(), "Description should match");
Assert.assertEquals(createdTransaction.getStatus(), Transaction.TransactionStatus.PENDING, "Transaction status should be PENDING");
// Update test transaction with generated ID
testTransaction.setId(createdTransaction.getId());
// Validate transaction data in database
validateTransactionInDatabase(createdTransaction);
logger.info("testCreateTransaction completed successfully");
}
@Test(description = "Get transaction by ID", dependsOnMethods = "testCreateTransaction")
public void testGetTransaction() {
logger.info("Executing testGetTransaction");
// Get transaction via API
Response response = walletService.getTransaction(testWallet.getId(), testTransaction.getId());
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for getting transaction");
// Parse response
Transaction retrievedTransaction = response.as(Transaction.class);
// Validate transaction data
Assert.assertEquals(retrievedTransaction.getId(), testTransaction.getId(), "Transaction ID should match");
Assert.assertEquals(retrievedTransaction.getWalletId(), testWallet.getId(), "Wallet ID should match");
Assert.assertEquals(retrievedTransaction.getType(), testTransaction.getType(), "Transaction type should match");
Assert.assertEquals(retrievedTransaction.getAmount(), testTransaction.getAmount(), "Amount should match");
Assert.assertEquals(retrievedTransaction.getCurrency(), testTransaction.getCurrency(), "Currency should match");
Assert.assertEquals(retrievedTransaction.getDescription(), testTransaction.getDescription(), "Description should match");
logger.info("testGetTransaction completed successfully");
}
@Test(description = "Get all transactions for a wallet", dependsOnMethods = "testCreateTransaction")
public void testGetTransactions() {
logger.info("Executing testGetTransactions");
// Get transactions via API
Response response = walletService.getTransactions(testWallet.getId());
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for getting transactions");
// Parse response
List<Map<String, Object>> transactions = response.jsonPath().getList("$");
// Validate transactions
Assert.assertTrue(transactions.size() > 0, "Should have at least one transaction");
// Find our test transaction
boolean found = false;
for (Map<String, Object> transactionData : transactions) {
if (transactionData.get("id").equals(testTransaction.getId())) {
found = true;
break;
}
}
Assert.assertTrue(found, "Test transaction should be in the list");
logger.info("testGetTransactions completed successfully");
}
@Test(description = "Update transaction status", dependsOnMethods = "testCreateTransaction")
public void testUpdateTransactionStatus() {
logger.info("Executing testUpdateTransactionStatus");
// Update transaction status via API
Response response = walletService.updateTransactionStatus(
testWallet.getId(),
testTransaction.getId(),
Transaction.TransactionStatus.COMPLETED
);
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for updating transaction status");
// Parse response
Transaction updatedTransaction = response.as(Transaction.class);
// Validate transaction data
Assert.assertEquals(updatedTransaction.getId(), testTransaction.getId(), "Transaction ID should match");
Assert.assertEquals(updatedTransaction.getStatus(), Transaction.TransactionStatus.COMPLETED, "Transaction status should be COMPLETED");
// Validate transaction data in database
validateTransactionInDatabase(updatedTransaction);
logger.info("testUpdateTransactionStatus completed successfully");
}
@Test(description = "Deposit funds to wallet", dependsOnMethods = "testCreateWallet")
public void testDepositFunds() {
logger.info("Executing testDepositFunds");
// Deposit funds via API
double depositAmount = 1000.00;
String description = "Test deposit";
Response response = walletService.depositFunds(testWallet.getId(), depositAmount, description);
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for depositing funds");
// Parse response
Transaction depositTransaction = response.as(Transaction.class);
// Validate transaction data
Assert.assertNotNull(depositTransaction.getId(), "Transaction ID should not be null");
Assert.assertEquals(depositTransaction.getWalletId(), testWallet.getId(), "Wallet ID should match");
Assert.assertEquals(depositTransaction.getType(), Transaction.TransactionType.DEPOSIT, "Transaction type should be DEPOSIT");
Assert.assertEquals(depositTransaction.getAmount(), new BigDecimal(depositAmount), "Amount should match");
Assert.assertEquals(depositTransaction.getDescription(), description, "Description should match");
// Get updated wallet balance
Response balanceResponse = walletService.getWalletBalance(testWallet.getId());
Map<String, Object> balanceData = balanceResponse.as(Map.class);
BigDecimal newBalance = new BigDecimal(balanceData.get("balance").toString());
// Validate balance increased
Assert.assertTrue(newBalance.compareTo(testWallet.getBalance()) > 0, "Balance should increase after deposit");
// Update test wallet balance
testWallet.setBalance(newBalance);
logger.info("testDepositFunds completed successfully");
}
@Test(description = "Withdraw funds from wallet", dependsOnMethods = "testDepositFunds")
public void testWithdrawFunds() {
logger.info("Executing testWithdrawFunds");
// Withdraw funds via API
double withdrawAmount = 500.00;
String description = "Test withdrawal";
Response response = walletService.withdrawFunds(testWallet.getId(), withdrawAmount, description);
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for withdrawing funds");
// Parse response
Transaction withdrawTransaction = response.as(Transaction.class);
// Validate transaction data
Assert.assertNotNull(withdrawTransaction.getId(), "Transaction ID should not be null");
Assert.assertEquals(withdrawTransaction.getWalletId(), testWallet.getId(), "Wallet ID should match");
Assert.assertEquals(withdrawTransaction.getType(), Transaction.TransactionType.WITHDRAWAL, "Transaction type should be WITHDRAWAL");
Assert.assertEquals(withdrawTransaction.getAmount(), new BigDecimal(withdrawAmount), "Amount should match");
Assert.assertEquals(withdrawTransaction.getDescription(), description, "Description should match");
// Get updated wallet balance
Response balanceResponse = walletService.getWalletBalance(testWallet.getId());
Map<String, Object> balanceData = balanceResponse.as(Map.class);
BigDecimal newBalance = new BigDecimal(balanceData.get("balance").toString());
// Validate balance decreased
Assert.assertTrue(newBalance.compareTo(testWallet.getBalance()) < 0, "Balance should decrease after withdrawal");
// Update test wallet balance
testWallet.setBalance(newBalance);
logger.info("testWithdrawFunds completed successfully");
}
/**
* Validate wallet data in database
* @param wallet Wallet to validate
*/
private void validateWalletInDatabase(Wallet wallet) {
try {
String query = "SELECT * FROM wallets WHERE id = ?";
List<Object> parameters = List.of(wallet.getId());
List<Map<String, Object>> results = DatabaseUtil.executeQuery(query, parameters);
Assert.assertEquals(results.size(), 1, "Should find exactly one wallet with ID: " + wallet.getId());
Map<String, Object> walletData = results.get(0);
Assert.assertEquals(walletData.get("customer_id"), wallet.getCustomerId(), "Customer ID should match in database");
Assert.assertEquals(walletData.get("account_number"), wallet.getAccountNumber(), "Account number should match in database");
Assert.assertEquals(new BigDecimal(walletData.get("balance").toString()), wallet.getBalance(), "Balance should match in database");
Assert.assertEquals(walletData.get("currency"), wallet.getCurrency(), "Currency should match in database");
Assert.assertEquals(walletData.get("status"), wallet.getStatus().toString(), "Status should match in database");
} catch (SQLException e) {
logger.error("Error validating wallet in database: {}", e.getMessage(), e);
Assert.fail("Error validating wallet in database: " + e.getMessage());
}
}
/**
* Validate transaction data in database
* @param transaction Transaction to validate
*/
private void validateTransactionInDatabase(Transaction transaction) {
try {
String query = "SELECT * FROM transactions WHERE id = ?";
List<Object> parameters = List.of(transaction.getId());
List<Map<String, Object>> results = DatabaseUtil.executeQuery(query, parameters);
Assert.assertEquals(results.size(), 1, "Should find exactly one transaction with ID: " + transaction.getId());
Map<String, Object> transactionData = results.get(0);
Assert.assertEquals(transactionData.get("wallet_id"), transaction.getWalletId(), "Wallet ID should match in database");
Assert.assertEquals(transactionData.get("type"), transaction.getType().toString(), "Type should match in database");
Assert.assertEquals(new BigDecimal(transactionData.get("amount").toString()), transaction.getAmount(), "Amount should match in database");
Assert.assertEquals(transactionData.get("currency"), transaction.getCurrency(), "Currency should match in database");
Assert.assertEquals(transactionData.get("description"), transaction.getDescription(), "Description should match in database");
Assert.assertEquals(transactionData.get("status"), transaction.getStatus().toString(), "Status should match in database");
} catch (SQLException e) {
logger.error("Error validating transaction in database: {}", e.getMessage(), e);
Assert.fail("Error validating transaction in database: " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,472 @@
package com.financial.api.tests;
import com.financial.api.models.wallet.Transaction;
import com.financial.api.models.wallet.Wallet;
import com.financial.api.services.WalletService;
import com.financial.api.utils.TestDataFactory;
import io.restassured.response.Response;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Performance test class for Wallet API operations
*/
public class WalletPerformanceTest {
private static final Logger logger = LogManager.getLogger(WalletPerformanceTest.class);
private WalletService walletService;
private List<Wallet> testWallets = new ArrayList<>();
private List<Transaction> testTransactions = new ArrayList<>();
private static final int WALLETS_TO_CREATE = 10;
private static final int TRANSACTIONS_PER_WALLET = 5;
private static final int MAX_RESPONSE_TIME_MS = 5000; // Maximum acceptable response time in milliseconds
@BeforeClass
public void setUp() {
logger.info("Setting up WalletPerformanceTest");
walletService = new WalletService();
// Create test wallets
for (int i = 0; i < WALLETS_TO_CREATE; i++) {
Wallet wallet = TestDataFactory.generateRandomWallet();
testWallets.add(wallet);
}
// Create test transactions
for (Wallet wallet : testWallets) {
for (int i = 0; i < TRANSACTIONS_PER_WALLET; i++) {
Transaction transaction = TestDataFactory.generateRandomTransaction(wallet.getId());
testTransactions.add(transaction);
}
}
}
@AfterClass
public void tearDown() {
logger.info("Tearing down WalletPerformanceTest");
// Clean up test data if needed
// Note: In a real scenario, you might want to delete the created wallets and transactions
}
@Test(description = "Performance test for creating multiple wallets", invocationCount = 1, threadPoolSize = 5)
public void testCreateMultipleWalletsPerformance() {
logger.info("Executing testCreateMultipleWalletsPerformance");
long startTime = System.currentTimeMillis();
List<Long> responseTimes = new ArrayList<>();
int successCount = 0;
for (Wallet wallet : testWallets) {
long requestStartTime = System.currentTimeMillis();
// Create wallet via API
Response response = walletService.createWallet(wallet);
long requestEndTime = System.currentTimeMillis();
long responseTime = requestEndTime - requestStartTime;
responseTimes.add(responseTime);
// Validate response
if (response.getStatusCode() == 201) {
successCount++;
// Parse response
Wallet createdWallet = response.as(Wallet.class);
// Update wallet with generated ID
wallet.setId(createdWallet.getId());
// Validate response time
Assert.assertTrue(responseTime <= MAX_RESPONSE_TIME_MS,
"Response time for creating wallet should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + responseTime + "ms");
} else {
logger.error("Failed to create wallet. Status code: {}", response.getStatusCode());
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
// Calculate statistics
double averageResponseTime = responseTimes.stream().mapToLong(Long::longValue).average().orElse(0);
long maxResponseTime = responseTimes.stream().mapToLong(Long::longValue).max().orElse(0);
long minResponseTime = responseTimes.stream().mapToLong(Long::longValue).min().orElse(0);
// Log performance metrics
logger.info("Performance metrics for creating wallets:");
logger.info("Total wallets created: {}/{}", successCount, testWallets.size());
logger.info("Total time: {} ms", totalTime);
logger.info("Average response time: {} ms", averageResponseTime);
logger.info("Min response time: {} ms", minResponseTime);
logger.info("Max response time: {} ms", maxResponseTime);
// Validate performance
Assert.assertEquals(successCount, testWallets.size(),
"All wallets should be created successfully. Created: " + successCount + ", Expected: " + testWallets.size());
Assert.assertTrue(averageResponseTime <= MAX_RESPONSE_TIME_MS,
"Average response time should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + averageResponseTime + "ms");
logger.info("testCreateMultipleWalletsPerformance completed successfully");
}
@Test(description = "Performance test for getting multiple wallets", dependsOnMethods = "testCreateMultipleWalletsPerformance", invocationCount = 1, threadPoolSize = 5)
public void testGetMultipleWalletsPerformance() {
logger.info("Executing testGetMultipleWalletsPerformance");
long startTime = System.currentTimeMillis();
List<Long> responseTimes = new ArrayList<>();
int successCount = 0;
for (Wallet wallet : testWallets) {
long requestStartTime = System.currentTimeMillis();
// Get wallet via API
Response response = walletService.getWallet(wallet.getId());
long requestEndTime = System.currentTimeMillis();
long responseTime = requestEndTime - requestStartTime;
responseTimes.add(responseTime);
// Validate response
if (response.getStatusCode() == 200) {
successCount++;
// Validate response time
Assert.assertTrue(responseTime <= MAX_RESPONSE_TIME_MS,
"Response time for getting wallet should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + responseTime + "ms");
} else {
logger.error("Failed to get wallet with ID: {}. Status code: {}", wallet.getId(), response.getStatusCode());
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
// Calculate statistics
double averageResponseTime = responseTimes.stream().mapToLong(Long::longValue).average().orElse(0);
long maxResponseTime = responseTimes.stream().mapToLong(Long::longValue).max().orElse(0);
long minResponseTime = responseTimes.stream().mapToLong(Long::longValue).min().orElse(0);
// Log performance metrics
logger.info("Performance metrics for getting wallets:");
logger.info("Total wallets retrieved: {}/{}", successCount, testWallets.size());
logger.info("Total time: {} ms", totalTime);
logger.info("Average response time: {} ms", averageResponseTime);
logger.info("Min response time: {} ms", minResponseTime);
logger.info("Max response time: {} ms", maxResponseTime);
// Validate performance
Assert.assertEquals(successCount, testWallets.size(),
"All wallets should be retrieved successfully. Retrieved: " + successCount + ", Expected: " + testWallets.size());
Assert.assertTrue(averageResponseTime <= MAX_RESPONSE_TIME_MS,
"Average response time should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + averageResponseTime + "ms");
logger.info("testGetMultipleWalletsPerformance completed successfully");
}
@Test(description = "Performance test for creating multiple transactions", dependsOnMethods = "testCreateMultipleWalletsPerformance", invocationCount = 1, threadPoolSize = 5)
public void testCreateMultipleTransactionsPerformance() {
logger.info("Executing testCreateMultipleTransactionsPerformance");
long startTime = System.currentTimeMillis();
List<Long> responseTimes = new ArrayList<>();
int successCount = 0;
for (Transaction transaction : testTransactions) {
long requestStartTime = System.currentTimeMillis();
// Create transaction via API
Response response = walletService.createTransaction(transaction.getWalletId(), transaction);
long requestEndTime = System.currentTimeMillis();
long responseTime = requestEndTime - requestStartTime;
responseTimes.add(responseTime);
// Validate response
if (response.getStatusCode() == 201) {
successCount++;
// Parse response
Transaction createdTransaction = response.as(Transaction.class);
// Update transaction with generated ID
transaction.setId(createdTransaction.getId());
// Validate response time
Assert.assertTrue(responseTime <= MAX_RESPONSE_TIME_MS,
"Response time for creating transaction should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + responseTime + "ms");
} else {
logger.error("Failed to create transaction. Status code: {}", response.getStatusCode());
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
// Calculate statistics
double averageResponseTime = responseTimes.stream().mapToLong(Long::longValue).average().orElse(0);
long maxResponseTime = responseTimes.stream().mapToLong(Long::longValue).max().orElse(0);
long minResponseTime = responseTimes.stream().mapToLong(Long::longValue).min().orElse(0);
// Log performance metrics
logger.info("Performance metrics for creating transactions:");
logger.info("Total transactions created: {}/{}", successCount, testTransactions.size());
logger.info("Total time: {} ms", totalTime);
logger.info("Average response time: {} ms", averageResponseTime);
logger.info("Min response time: {} ms", minResponseTime);
logger.info("Max response time: {} ms", maxResponseTime);
// Validate performance
Assert.assertEquals(successCount, testTransactions.size(),
"All transactions should be created successfully. Created: " + successCount + ", Expected: " + testTransactions.size());
Assert.assertTrue(averageResponseTime <= MAX_RESPONSE_TIME_MS,
"Average response time should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + averageResponseTime + "ms");
logger.info("testCreateMultipleTransactionsPerformance completed successfully");
}
@Test(description = "Performance test for depositing funds to multiple wallets", dependsOnMethods = "testCreateMultipleWalletsPerformance", invocationCount = 1, threadPoolSize = 5)
public void testDepositFundsPerformance() {
logger.info("Executing testDepositFundsPerformance");
long startTime = System.currentTimeMillis();
List<Long> responseTimes = new ArrayList<>();
int successCount = 0;
for (Wallet wallet : testWallets) {
long requestStartTime = System.currentTimeMillis();
// Deposit funds via API
double depositAmount = 1000.00;
String description = "Performance test deposit";
Response response = walletService.depositFunds(wallet.getId(), depositAmount, description);
long requestEndTime = System.currentTimeMillis();
long responseTime = requestEndTime - requestStartTime;
responseTimes.add(responseTime);
// Validate response
if (response.getStatusCode() == 200) {
successCount++;
// Validate response time
Assert.assertTrue(responseTime <= MAX_RESPONSE_TIME_MS,
"Response time for depositing funds should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + responseTime + "ms");
} else {
logger.error("Failed to deposit funds to wallet with ID: {}. Status code: {}", wallet.getId(), response.getStatusCode());
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
// Calculate statistics
double averageResponseTime = responseTimes.stream().mapToLong(Long::longValue).average().orElse(0);
long maxResponseTime = responseTimes.stream().mapToLong(Long::longValue).max().orElse(0);
long minResponseTime = responseTimes.stream().mapToLong(Long::longValue).min().orElse(0);
// Log performance metrics
logger.info("Performance metrics for depositing funds:");
logger.info("Total deposits completed: {}/{}", successCount, testWallets.size());
logger.info("Total time: {} ms", totalTime);
logger.info("Average response time: {} ms", averageResponseTime);
logger.info("Min response time: {} ms", minResponseTime);
logger.info("Max response time: {} ms", maxResponseTime);
// Validate performance
Assert.assertEquals(successCount, testWallets.size(),
"All deposits should be completed successfully. Completed: " + successCount + ", Expected: " + testWallets.size());
Assert.assertTrue(averageResponseTime <= MAX_RESPONSE_TIME_MS,
"Average response time should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + averageResponseTime + "ms");
logger.info("testDepositFundsPerformance completed successfully");
}
@Test(description = "Performance test for getting wallet balance for multiple wallets", dependsOnMethods = "testDepositFundsPerformance", invocationCount = 1, threadPoolSize = 5)
public void testGetWalletBalancePerformance() {
logger.info("Executing testGetWalletBalancePerformance");
long startTime = System.currentTimeMillis();
List<Long> responseTimes = new ArrayList<>();
int successCount = 0;
for (Wallet wallet : testWallets) {
long requestStartTime = System.currentTimeMillis();
// Get wallet balance via API
Response response = walletService.getWalletBalance(wallet.getId());
long requestEndTime = System.currentTimeMillis();
long responseTime = requestEndTime - requestStartTime;
responseTimes.add(responseTime);
// Validate response
if (response.getStatusCode() == 200) {
successCount++;
// Validate response time
Assert.assertTrue(responseTime <= MAX_RESPONSE_TIME_MS,
"Response time for getting wallet balance should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + responseTime + "ms");
} else {
logger.error("Failed to get wallet balance for wallet with ID: {}. Status code: {}", wallet.getId(), response.getStatusCode());
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
// Calculate statistics
double averageResponseTime = responseTimes.stream().mapToLong(Long::longValue).average().orElse(0);
long maxResponseTime = responseTimes.stream().mapToLong(Long::longValue).max().orElse(0);
long minResponseTime = responseTimes.stream().mapToLong(Long::longValue).min().orElse(0);
// Log performance metrics
logger.info("Performance metrics for getting wallet balance:");
logger.info("Total wallet balances retrieved: {}/{}", successCount, testWallets.size());
logger.info("Total time: {} ms", totalTime);
logger.info("Average response time: {} ms", averageResponseTime);
logger.info("Min response time: {} ms", minResponseTime);
logger.info("Max response time: {} ms", maxResponseTime);
// Validate performance
Assert.assertEquals(successCount, testWallets.size(),
"All wallet balances should be retrieved successfully. Retrieved: " + successCount + ", Expected: " + testWallets.size());
Assert.assertTrue(averageResponseTime <= MAX_RESPONSE_TIME_MS,
"Average response time should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + averageResponseTime + "ms");
logger.info("testGetWalletBalancePerformance completed successfully");
}
@Test(description = "Performance test for getting transactions for multiple wallets", dependsOnMethods = "testCreateMultipleTransactionsPerformance", invocationCount = 1, threadPoolSize = 5)
public void testGetTransactionsPerformance() {
logger.info("Executing testGetTransactionsPerformance");
long startTime = System.currentTimeMillis();
List<Long> responseTimes = new ArrayList<>();
int successCount = 0;
for (Wallet wallet : testWallets) {
long requestStartTime = System.currentTimeMillis();
// Get transactions via API
Response response = walletService.getTransactions(wallet.getId());
long requestEndTime = System.currentTimeMillis();
long responseTime = requestEndTime - requestStartTime;
responseTimes.add(responseTime);
// Validate response
if (response.getStatusCode() == 200) {
successCount++;
// Validate response time
Assert.assertTrue(responseTime <= MAX_RESPONSE_TIME_MS,
"Response time for getting transactions should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + responseTime + "ms");
} else {
logger.error("Failed to get transactions for wallet with ID: {}. Status code: {}", wallet.getId(), response.getStatusCode());
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
// Calculate statistics
double averageResponseTime = responseTimes.stream().mapToLong(Long::longValue).average().orElse(0);
long maxResponseTime = responseTimes.stream().mapToLong(Long::longValue).max().orElse(0);
long minResponseTime = responseTimes.stream().mapToLong(Long::longValue).min().orElse(0);
// Log performance metrics
logger.info("Performance metrics for getting transactions:");
logger.info("Total transactions retrieved: {}/{}", successCount, testWallets.size());
logger.info("Total time: {} ms", totalTime);
logger.info("Average response time: {} ms", averageResponseTime);
logger.info("Min response time: {} ms", minResponseTime);
logger.info("Max response time: {} ms", maxResponseTime);
// Validate performance
Assert.assertEquals(successCount, testWallets.size(),
"All transactions should be retrieved successfully. Retrieved: " + successCount + ", Expected: " + testWallets.size());
Assert.assertTrue(averageResponseTime <= MAX_RESPONSE_TIME_MS,
"Average response time should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + averageResponseTime + "ms");
logger.info("testGetTransactionsPerformance completed successfully");
}
@Test(description = "Performance test for transferring funds between wallets", dependsOnMethods = "testDepositFundsPerformance", invocationCount = 1, threadPoolSize = 5)
public void testTransferFundsPerformance() {
logger.info("Executing testTransferFundsPerformance");
long startTime = System.currentTimeMillis();
List<Long> responseTimes = new ArrayList<>();
int successCount = 0;
// Perform transfers between wallets
for (int i = 0; i < testWallets.size() - 1; i++) {
Wallet fromWallet = testWallets.get(i);
Wallet toWallet = testWallets.get(i + 1);
long requestStartTime = System.currentTimeMillis();
// Transfer funds via API
double transferAmount = 100.00;
String description = "Performance test transfer";
Response response = walletService.transferFunds(fromWallet.getId(), toWallet.getId(), transferAmount, description);
long requestEndTime = System.currentTimeMillis();
long responseTime = requestEndTime - requestStartTime;
responseTimes.add(responseTime);
// Validate response
if (response.getStatusCode() == 200) {
successCount++;
// Validate response time
Assert.assertTrue(responseTime <= MAX_RESPONSE_TIME_MS,
"Response time for transferring funds should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + responseTime + "ms");
} else {
logger.error("Failed to transfer funds from wallet {} to wallet {}. Status code: {}",
fromWallet.getId(), toWallet.getId(), response.getStatusCode());
}
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
// Calculate statistics
double averageResponseTime = responseTimes.stream().mapToLong(Long::longValue).average().orElse(0);
long maxResponseTime = responseTimes.stream().mapToLong(Long::longValue).max().orElse(0);
long minResponseTime = responseTimes.stream().mapToLong(Long::longValue).min().orElse(0);
// Log performance metrics
logger.info("Performance metrics for transferring funds:");
logger.info("Total transfers completed: {}/{}", successCount, testWallets.size() - 1);
logger.info("Total time: {} ms", totalTime);
logger.info("Average response time: {} ms", averageResponseTime);
logger.info("Min response time: {} ms", minResponseTime);
logger.info("Max response time: {} ms", maxResponseTime);
// Validate performance
Assert.assertEquals(successCount, testWallets.size() - 1,
"All transfers should be completed successfully. Completed: " + successCount + ", Expected: " + (testWallets.size() - 1));
Assert.assertTrue(averageResponseTime <= MAX_RESPONSE_TIME_MS,
"Average response time should be less than " + MAX_RESPONSE_TIME_MS + "ms, but was " + averageResponseTime + "ms");
logger.info("testTransferFundsPerformance completed successfully");
}
}

View File

@@ -0,0 +1,359 @@
package com.financial.api.tests;
import com.financial.api.models.wallet.Transaction;
import com.financial.api.models.wallet.Wallet;
import com.financial.api.services.WalletService;
import com.financial.api.utils.TestDataFactory;
import io.restassured.response.Response;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
/**
* Security test class for Wallet API operations
*/
public class WalletSecurityTest {
private static final Logger logger = LogManager.getLogger(WalletSecurityTest.class);
private WalletService walletService;
private Wallet testWallet;
private Transaction testTransaction;
@BeforeClass
public void setUp() {
logger.info("Setting up WalletSecurityTest");
walletService = new WalletService();
// Create test wallet
testWallet = TestDataFactory.generateRandomWallet();
// Create test transaction
testTransaction = TestDataFactory.generateRandomTransaction(testWallet.getId());
// Create wallet via API for testing
Response response = walletService.createWallet(testWallet);
if (response.getStatusCode() == 201) {
Wallet createdWallet = response.as(Wallet.class);
testWallet.setId(createdWallet.getId());
}
}
@Test(description = "Test SQL injection in wallet creation")
public void testSqlInjectionInWalletCreation() {
logger.info("Executing testSqlInjectionInWalletCreation");
// Create wallet with SQL injection payload
Wallet maliciousWallet = new Wallet(
"test-customer'; DROP TABLE wallets; --",
"test-account'; DROP TABLE wallets; --",
new BigDecimal("1000.00"),
"USD"
);
// Attempt to create wallet via API
Response response = walletService.createWallet(maliciousWallet);
// Validate response
// Should return 400 Bad Request or similar error code, not 201 Created
Assert.assertNotEquals(response.getStatusCode(), 201, "API should not create wallet with SQL injection payload");
logger.info("testSqlInjectionInWalletCreation completed successfully");
}
@Test(description = "Test XSS in wallet creation")
public void testXssInWalletCreation() {
logger.info("Executing testXssInWalletCreation");
// Create wallet with XSS payload
Wallet maliciousWallet = new Wallet(
"<script>alert('XSS')</script>",
"test-account",
new BigDecimal("1000.00"),
"USD"
);
// Attempt to create wallet via API
Response response = walletService.createWallet(maliciousWallet);
// Validate response
// Should return 400 Bad Request or similar error code, not 201 Created
Assert.assertNotEquals(response.getStatusCode(), 201, "API should not create wallet with XSS payload");
logger.info("testXssInWalletCreation completed successfully");
}
@Test(description = "Test authentication bypass in wallet retrieval")
public void testAuthenticationBypassInWalletRetrieval() {
logger.info("Executing testAuthenticationBypassInWalletRetrieval");
// Attempt to get wallet without authentication
Response response = walletService.getWalletWithoutAuth(testWallet.getId());
// Validate response
// Should return 401 Unauthorized or 403 Forbidden
Assert.assertTrue(response.getStatusCode() == 401 || response.getStatusCode() == 403,
"API should require authentication for wallet retrieval");
logger.info("testAuthenticationBypassInWalletRetrieval completed successfully");
}
@Test(description = "Test authorization bypass in wallet retrieval")
public void testAuthorizationBypassInWalletRetrieval() {
logger.info("Executing testAuthorizationBypassInWalletRetrieval");
// Attempt to get wallet with different user's authentication
Response response = walletService.getWalletWithDifferentUserAuth(testWallet.getId());
// Validate response
// Should return 403 Forbidden
Assert.assertEquals(response.getStatusCode(), 403, "API should prevent unauthorized access to wallet");
logger.info("testAuthorizationBypassInWalletRetrieval completed successfully");
}
@Test(description = "Test IDOR in wallet retrieval")
public void testIdorInWalletRetrieval() {
logger.info("Executing testIdorInWalletRetrieval");
// Attempt to get wallet with different wallet ID
String differentWalletId = "different-wallet-id-12345";
Response response = walletService.getWallet(differentWalletId);
// Validate response
// Should return 404 Not Found or 403 Forbidden
Assert.assertTrue(response.getStatusCode() == 404 || response.getStatusCode() == 403,
"API should prevent access to non-existent or unauthorized wallet");
logger.info("testIdorInWalletRetrieval completed successfully");
}
@Test(description = "Test SQL injection in transaction creation")
public void testSqlInjectionInTransactionCreation() {
logger.info("Executing testSqlInjectionInTransactionCreation");
// Create transaction with SQL injection payload
Transaction maliciousTransaction = new Transaction(
testWallet.getId(),
Transaction.TransactionType.DEPOSIT,
new BigDecimal("1000.00"),
"USD",
"test-description'; DROP TABLE transactions; --"
);
// Attempt to create transaction via API
Response response = walletService.createTransaction(testWallet.getId(), maliciousTransaction);
// Validate response
// Should return 400 Bad Request or similar error code, not 201 Created
Assert.assertNotEquals(response.getStatusCode(), 201, "API should not create transaction with SQL injection payload");
logger.info("testSqlInjectionInTransactionCreation completed successfully");
}
@Test(description = "Test XSS in transaction creation")
public void testXssInTransactionCreation() {
logger.info("Executing testXssInTransactionCreation");
// Create transaction with XSS payload
Transaction maliciousTransaction = new Transaction(
testWallet.getId(),
Transaction.TransactionType.DEPOSIT,
new BigDecimal("1000.00"),
"USD",
"<script>alert('XSS')</script>"
);
// Attempt to create transaction via API
Response response = walletService.createTransaction(testWallet.getId(), maliciousTransaction);
// Validate response
// Should return 400 Bad Request or similar error code, not 201 Created
Assert.assertNotEquals(response.getStatusCode(), 201, "API should not create transaction with XSS payload");
logger.info("testXssInTransactionCreation completed successfully");
}
@Test(description = "Test negative amount in transaction creation")
public void testNegativeAmountInTransactionCreation() {
logger.info("Executing testNegativeAmountInTransactionCreation");
// Create transaction with negative amount
Transaction maliciousTransaction = new Transaction(
testWallet.getId(),
Transaction.TransactionType.DEPOSIT,
new BigDecimal("-1000.00"),
"USD",
"Test transaction with negative amount"
);
// Attempt to create transaction via API
Response response = walletService.createTransaction(testWallet.getId(), maliciousTransaction);
// Validate response
// Should return 400 Bad Request or similar error code, not 201 Created
Assert.assertNotEquals(response.getStatusCode(), 201, "API should not create transaction with negative amount");
logger.info("testNegativeAmountInTransactionCreation completed successfully");
}
@Test(description = "Test extremely large amount in transaction creation")
public void testExtremelyLargeAmountInTransactionCreation() {
logger.info("Executing testExtremelyLargeAmountInTransactionCreation");
// Create transaction with extremely large amount
Transaction maliciousTransaction = new Transaction(
testWallet.getId(),
Transaction.TransactionType.DEPOSIT,
new BigDecimal("99999999999999999999.99"),
"USD",
"Test transaction with extremely large amount"
);
// Attempt to create transaction via API
Response response = walletService.createTransaction(testWallet.getId(), maliciousTransaction);
// Validate response
// Should return 400 Bad Request or similar error code, not 201 Created
Assert.assertNotEquals(response.getStatusCode(), 201, "API should not create transaction with extremely large amount");
logger.info("testExtremelyLargeAmountInTransactionCreation completed successfully");
}
@Test(description = "Test authentication bypass in transaction retrieval")
public void testAuthenticationBypassInTransactionRetrieval() {
logger.info("Executing testAuthenticationBypassInTransactionRetrieval");
// Attempt to get transaction without authentication
Response response = walletService.getTransactionWithoutAuth(testWallet.getId(), "test-transaction-id");
// Validate response
// Should return 401 Unauthorized or 403 Forbidden
Assert.assertTrue(response.getStatusCode() == 401 || response.getStatusCode() == 403,
"API should require authentication for transaction retrieval");
logger.info("testAuthenticationBypassInTransactionRetrieval completed successfully");
}
@Test(description = "Test authorization bypass in transaction retrieval")
public void testAuthorizationBypassInTransactionRetrieval() {
logger.info("Executing testAuthorizationBypassInTransactionRetrieval");
// Attempt to get transaction with different user's authentication
Response response = walletService.getTransactionWithDifferentUserAuth(testWallet.getId(), "test-transaction-id");
// Validate response
// Should return 403 Forbidden
Assert.assertEquals(response.getStatusCode(), 403, "API should prevent unauthorized access to transaction");
logger.info("testAuthorizationBypassInTransactionRetrieval completed successfully");
}
@Test(description = "Test CSRF protection in wallet update")
public void testCsrfProtectionInWalletUpdate() {
logger.info("Executing testCsrfProtectionInWalletUpdate");
// Attempt to update wallet without CSRF token
Map<String, Object> updateData = new HashMap<>();
updateData.put("balance", new BigDecimal("2000.00"));
Response response = walletService.updateWalletWithoutCsrfToken(testWallet.getId(), updateData);
// Validate response
// Should return 400 Bad Request or 403 Forbidden if CSRF protection is enabled
Assert.assertTrue(response.getStatusCode() == 400 || response.getStatusCode() == 403,
"API should require CSRF token for wallet update");
logger.info("testCsrfProtectionInWalletUpdate completed successfully");
}
@Test(description = "Test rate limiting in wallet creation")
public void testRateLimitingInWalletCreation() {
logger.info("Executing testRateLimitingInWalletCreation");
int requestCount = 10;
int tooManyRequestsCount = 0;
// Send multiple requests in quick succession
for (int i = 0; i < requestCount; i++) {
Wallet wallet = TestDataFactory.generateRandomWallet();
Response response = walletService.createWallet(wallet);
if (response.getStatusCode() == 429) { // Too Many Requests
tooManyRequestsCount++;
}
}
// Validate response
// Should receive at least one 429 Too Many Requests response if rate limiting is enabled
Assert.assertTrue(tooManyRequestsCount > 0, "API should implement rate limiting for wallet creation");
logger.info("testRateLimitingInWalletCreation completed successfully");
}
@Test(description = "Test input validation in wallet creation")
public void testInputValidationInWalletCreation() {
logger.info("Executing testInputValidationInWalletCreation");
// Test cases for input validation
Map<String, Wallet> testCases = new HashMap<>();
// Empty customer ID
testCases.put("Empty customer ID", new Wallet("", "test-account", new BigDecimal("1000.00"), "USD"));
// Empty account number
testCases.put("Empty account number", new Wallet("test-customer", "", new BigDecimal("1000.00"), "USD"));
// Null balance
testCases.put("Null balance", new Wallet("test-customer", "test-account", null, "USD"));
// Empty currency
testCases.put("Empty currency", new Wallet("test-customer", "test-account", new BigDecimal("1000.00"), ""));
// Invalid currency
testCases.put("Invalid currency", new Wallet("test-customer", "test-account", new BigDecimal("1000.00"), "INVALID"));
// Test each case
for (Map.Entry<String, Wallet> entry : testCases.entrySet()) {
String testCase = entry.getKey();
Wallet wallet = entry.getValue();
Response response = walletService.createWallet(wallet);
// Validate response
// Should return 400 Bad Request or similar error code, not 201 Created
Assert.assertNotEquals(response.getStatusCode(), 201,
"API should validate input for " + testCase + " and not create wallet");
logger.info("Input validation test passed for: {}", testCase);
}
logger.info("testInputValidationInWalletCreation completed successfully");
}
@Test(description = "Test sensitive data exposure in wallet response")
public void testSensitiveDataExposureInWalletResponse() {
logger.info("Executing testSensitiveDataExposureInWalletResponse");
// Get wallet via API
Response response = walletService.getWallet(testWallet.getId());
// Validate response
Assert.assertEquals(response.getStatusCode(), 200, "Expected status code 200 for getting wallet");
// Parse response
Map<String, Object> walletData = response.as(Map.class);
// Check for sensitive data that should not be exposed
Assert.assertFalse(walletData.containsKey("password"), "Wallet response should not contain password");
Assert.assertFalse(walletData.containsKey("pin"), "Wallet response should not contain PIN");
Assert.assertFalse(walletData.containsKey("ssn"), "Wallet response should not contain SSN");
Assert.assertFalse(walletData.containsKey("credit_card_number"), "Wallet response should not contain credit card number");
logger.info("testSensitiveDataExposureInWalletResponse completed successfully");
}
}

View File

@@ -0,0 +1,193 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Financial API Test Suite" verbose="1" parallel="tests" thread-count="5">
<listeners>
<listener class-name="com.financial.api.utils.TestListener"/>
<listener class-name="com.financial.api.utils.AnnotationTransformer"/>
</listeners>
<!-- Parameter definitions -->
<parameter name="baseURI" value="https://api.example.com"/>
<parameter name="apiVersion" value="v1"/>
<parameter name="environment" value="test"/>
<parameter name="browser" value="chrome"/>
<parameter name="headless" value="false"/>
<!-- Test groups configuration -->
<test name="Wallet API Tests">
<parameter name="service" value="wallet"/>
<groups>
<run>
<include name="wallet"/>
<include name="smoke"/>
<include name="regression"/>
<exclude name="performance"/>
<exclude name="security"/>
</run>
</groups>
<classes>
<class name="com.financial.api.tests.WalletApiTest"/>
</classes>
</test>
<test name="Credit API Tests">
<parameter name="service" value="credit"/>
<groups>
<run>
<include name="credit"/>
<include name="smoke"/>
<include name="regression"/>
<exclude name="performance"/>
<exclude name="security"/>
</run>
</groups>
<classes>
<class name="com.financial.api.tests.CreditApiTest"/>
</classes>
</test>
<test name="Wallet Performance Tests">
<parameter name="service" value="wallet"/>
<groups>
<run>
<include name="wallet"/>
<include name="performance"/>
</run>
</groups>
<classes>
<class name="com.financial.api.tests.WalletPerformanceTest"/>
</classes>
</test>
<test name="Credit Performance Tests">
<parameter name="service" value="credit"/>
<groups>
<run>
<include name="credit"/>
<include name="performance"/>
</run>
</groups>
<classes>
<class name="com.financial.api.tests.CreditPerformanceTest"/>
</classes>
</test>
<test name="Wallet Security Tests">
<parameter name="service" value="wallet"/>
<groups>
<run>
<include name="wallet"/>
<include name="security"/>
</run>
</groups>
<classes>
<class name="com.financial.api.tests.WalletSecurityTest"/>
</classes>
</test>
<test name="Credit Security Tests">
<parameter name="service" value="credit"/>
<groups>
<run>
<include name="credit"/>
<include name="security"/>
</run>
</groups>
<classes>
<class name="com.financial.api.tests.CreditSecurityTest"/>
</classes>
</test>
<!-- Smoke test suite -->
<test name="Smoke Tests">
<parameter name="service" value="all"/>
<groups>
<run>
<include name="smoke"/>
</run>
</groups>
<classes>
<class name="com.financial.api.tests.WalletApiTest">
<methods>
<include name="testCreateWallet"/>
<include name="testGetWallet"/>
</methods>
</class>
<class name="com.financial.api.tests.CreditApiTest">
<methods>
<include name="testSubmitApplication"/>
<include name="testGetApplication"/>
</methods>
</class>
</classes>
</test>
<!-- Regression test suite -->
<test name="Regression Tests">
<parameter name="service" value="all"/>
<groups>
<run>
<include name="regression"/>
<exclude name="performance"/>
<exclude name="security"/>
</run>
</groups>
<classes>
<class name="com.financial.api.tests.WalletApiTest"/>
<class name="com.financial.api.tests.CreditApiTest"/>
</classes>
</test>
<!-- Performance test suite -->
<test name="Performance Tests">
<parameter name="service" value="all"/>
<groups>
<run>
<include name="performance"/>
</run>
</groups>
<classes>
<class name="com.financial.api.tests.WalletPerformanceTest"/>
<class name="com.financial.api.tests.CreditPerformanceTest"/>
</classes>
</test>
<!-- Security test suite -->
<test name="Security Tests">
<parameter name="service" value="all"/>
<groups>
<run>
<include name="security"/>
</run>
</groups>
<classes>
<class name="com.financial.api.tests.WalletSecurityTest"/>
<class name="com.financial.api.tests.CreditSecurityTest"/>
</classes>
</test>
<!-- Full test suite -->
<test name="Full Test Suite">
<parameter name="service" value="all"/>
<groups>
<run>
<include name="wallet"/>
<include name="credit"/>
<include name="smoke"/>
<include name="regression"/>
<include name="performance"/>
<include name="security"/>
</run>
</groups>
<classes>
<class name="com.financial.api.tests.WalletApiTest"/>
<class name="com.financial.api.tests.CreditApiTest"/>
<class name="com.financial.api.tests.WalletPerformanceTest"/>
<class name="com.financial.api.tests.CreditPerformanceTest"/>
<class name="com.financial.api.tests.WalletSecurityTest"/>
<class name="com.financial.api.tests.CreditSecurityTest"/>
</classes>
</test>
</suite>