Add test data schedule configuration methods to ConfigManager
This commit is contained in:
376
README.md
Normal file
376
README.md
Normal 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
162
pom.xml
Normal 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>
|
245
src/main/java/com/financial/api/config/ConfigManager.java
Normal file
245
src/main/java/com/financial/api/config/ConfigManager.java
Normal 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");
|
||||
}
|
||||
}
|
@@ -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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
320
src/main/java/com/financial/api/models/credit/CreditScore.java
Normal file
320
src/main/java/com/financial/api/models/credit/CreditScore.java
Normal 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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
330
src/main/java/com/financial/api/models/wallet/Transaction.java
Normal file
330
src/main/java/com/financial/api/models/wallet/Transaction.java
Normal 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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
237
src/main/java/com/financial/api/models/wallet/Wallet.java
Normal file
237
src/main/java/com/financial/api/models/wallet/Wallet.java
Normal 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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
259
src/main/java/com/financial/api/services/BaseApiClient.java
Normal file
259
src/main/java/com/financial/api/services/BaseApiClient.java
Normal 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();
|
||||
}
|
||||
}
|
312
src/main/java/com/financial/api/services/CreditService.java
Normal file
312
src/main/java/com/financial/api/services/CreditService.java
Normal 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);
|
||||
}
|
||||
}
|
279
src/main/java/com/financial/api/services/WalletService.java
Normal file
279
src/main/java/com/financial/api/services/WalletService.java
Normal 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);
|
||||
}
|
||||
}
|
271
src/main/java/com/financial/api/utils/AnnotationTransformer.java
Normal file
271
src/main/java/com/financial/api/utils/AnnotationTransformer.java
Normal 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;
|
||||
}
|
||||
}
|
484
src/main/java/com/financial/api/utils/CouchbaseUtil.java
Normal file
484
src/main/java/com/financial/api/utils/CouchbaseUtil.java
Normal 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<>();
|
||||
}
|
||||
}
|
||||
}
|
274
src/main/java/com/financial/api/utils/DatabaseUtil.java
Normal file
274
src/main/java/com/financial/api/utils/DatabaseUtil.java
Normal 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;
|
||||
}
|
||||
}
|
395
src/main/java/com/financial/api/utils/ElasticsearchUtil.java
Normal file
395
src/main/java/com/financial/api/utils/ElasticsearchUtil.java
Normal 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);
|
||||
}
|
||||
}
|
445
src/main/java/com/financial/api/utils/TestDataFactory.java
Normal file
445
src/main/java/com/financial/api/utils/TestDataFactory.java
Normal 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;
|
||||
}
|
||||
}
|
207
src/main/java/com/financial/api/utils/TestListener.java
Normal file
207
src/main/java/com/financial/api/utils/TestListener.java
Normal 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
|
||||
}
|
||||
}
|
44
src/main/resources/config.properties
Normal file
44
src/main/resources/config.properties
Normal 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
|
85
src/main/resources/log4j2.xml
Normal file
85
src/main/resources/log4j2.xml
Normal 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>
|
486
src/test/java/com/financial/api/tests/CreditApiTest.java
Normal file
486
src/test/java/com/financial/api/tests/CreditApiTest.java
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
469
src/test/java/com/financial/api/tests/CreditPerformanceTest.java
Normal file
469
src/test/java/com/financial/api/tests/CreditPerformanceTest.java
Normal 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");
|
||||
}
|
||||
}
|
496
src/test/java/com/financial/api/tests/CreditSecurityTest.java
Normal file
496
src/test/java/com/financial/api/tests/CreditSecurityTest.java
Normal 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");
|
||||
}
|
||||
}
|
420
src/test/java/com/financial/api/tests/WalletApiTest.java
Normal file
420
src/test/java/com/financial/api/tests/WalletApiTest.java
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
472
src/test/java/com/financial/api/tests/WalletPerformanceTest.java
Normal file
472
src/test/java/com/financial/api/tests/WalletPerformanceTest.java
Normal 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");
|
||||
}
|
||||
}
|
359
src/test/java/com/financial/api/tests/WalletSecurityTest.java
Normal file
359
src/test/java/com/financial/api/tests/WalletSecurityTest.java
Normal 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");
|
||||
}
|
||||
}
|
193
src/test/resources/testng.xml
Normal file
193
src/test/resources/testng.xml
Normal 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>
|
Reference in New Issue
Block a user