add unified test scripts for all services
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@ -69,6 +69,12 @@ Desktop.ini
|
|||||||
logs/
|
logs/
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
|
# SQLite Database
|
||||||
|
*.db
|
||||||
|
*.db-journal
|
||||||
|
*.sqlite
|
||||||
|
*.sqlite3
|
||||||
|
|
||||||
# Python
|
# Python
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
|||||||
553
apidoc.md
Normal file
553
apidoc.md
Normal file
@ -0,0 +1,553 @@
|
|||||||
|
# API Documentation
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This project provides JAX-WS SOAP web services for a loan approval system. The system includes customer registration, loan application processing with credit score evaluation, and a simple Hello World demonstration service.
|
||||||
|
|
||||||
|
**Version:** 1.0
|
||||||
|
**Base URL:** `http://localhost:8080/jaxws-hello-world`
|
||||||
|
**Protocol:** SOAP 1.1 / HTTP
|
||||||
|
**Data Format:** XML
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
- [Services Overview](#services-overview)
|
||||||
|
- [Hello World Service](#hello-world-service)
|
||||||
|
- [Loan Approval Service](#loan-approval-service)
|
||||||
|
- [Data Models](#data-models)
|
||||||
|
- [Database Schema](#database-schema)
|
||||||
|
- [Error Handling](#error-handling)
|
||||||
|
- [Testing](#testing)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Services Overview
|
||||||
|
|
||||||
|
| Service Name | Endpoint | WSDL | Description |
|
||||||
|
|-------------|----------|------|-------------|
|
||||||
|
| HelloWorldService | `/hello` | [WSDL](http://localhost:8080/jaxws-hello-world/hello?wsdl) | Simple demonstration service |
|
||||||
|
| LoanApprovalService | `/loan` | [WSDL](http://localhost:8080/jaxws-hello-world/loan?wsdl) | Customer registration and loan processing |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hello World Service
|
||||||
|
|
||||||
|
### Service Information
|
||||||
|
|
||||||
|
- **Endpoint:** `http://localhost:8080/jaxws-hello-world/hello`
|
||||||
|
- **WSDL:** `http://localhost:8080/jaxws-hello-world/hello?wsdl`
|
||||||
|
- **Namespace:** `http://service.example.com/`
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
|
||||||
|
#### getHelloWorld
|
||||||
|
|
||||||
|
Returns a personalized greeting message.
|
||||||
|
|
||||||
|
**Operation:** `getHelloWorld`
|
||||||
|
|
||||||
|
**Input Parameters:**
|
||||||
|
| Parameter | Type | Required | Description |
|
||||||
|
|-----------|------|----------|-------------|
|
||||||
|
| name | string | Yes | Name to include in greeting |
|
||||||
|
|
||||||
|
**Returns:** `string` - Greeting message in format "Hello World, {name}!"
|
||||||
|
|
||||||
|
**SOAP Request Example:**
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
|
xmlns:ser="http://service.example.com/">
|
||||||
|
<soapenv:Header/>
|
||||||
|
<soapenv:Body>
|
||||||
|
<ser:getHelloWorld>
|
||||||
|
<arg0>John</arg0>
|
||||||
|
</ser:getHelloWorld>
|
||||||
|
</soapenv:Body>
|
||||||
|
</soapenv:Envelope>
|
||||||
|
```
|
||||||
|
|
||||||
|
**SOAP Response Example:**
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
|
||||||
|
<S:Body>
|
||||||
|
<ns2:getHelloWorldResponse xmlns:ns2="http://service.example.com/">
|
||||||
|
<return>Hello World, John!</return>
|
||||||
|
</ns2:getHelloWorldResponse>
|
||||||
|
</S:Body>
|
||||||
|
</S:Envelope>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Loan Approval Service
|
||||||
|
|
||||||
|
### Service Information
|
||||||
|
|
||||||
|
- **Endpoint:** `http://localhost:8080/jaxws-hello-world/loan`
|
||||||
|
- **WSDL:** `http://localhost:8080/jaxws-hello-world/loan?wsdl`
|
||||||
|
- **Namespace:** `http://service.example.com/`
|
||||||
|
- **Database:** SQLite (`loan_app.db`)
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
|
||||||
|
#### 1. registerNewCustomer
|
||||||
|
|
||||||
|
Registers a new customer in the system with optional blacklist status.
|
||||||
|
|
||||||
|
**Operation:** `registerNewCustomer`
|
||||||
|
|
||||||
|
**Input Parameters:**
|
||||||
|
| Parameter | Type | Required | Description |
|
||||||
|
|-----------|------|----------|-------------|
|
||||||
|
| request | CustomerRegistrationRequest | Yes | Customer registration details |
|
||||||
|
| └─ customerName | string | Yes | Customer's full name |
|
||||||
|
| └─ blacklisted | boolean | No | Blacklist status (default: false) |
|
||||||
|
|
||||||
|
**Returns:** `string` - Registration status message
|
||||||
|
|
||||||
|
**Possible Return Values:**
|
||||||
|
- `"Registration Successful"` - Customer registered successfully
|
||||||
|
- `"Error: Customer already exists"` - Customer name already in database
|
||||||
|
|
||||||
|
**SOAP Request Example:**
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
|
xmlns:ser="http://service.example.com/">
|
||||||
|
<soapenv:Header/>
|
||||||
|
<soapenv:Body>
|
||||||
|
<ser:registerNewCustomer>
|
||||||
|
<arg0>
|
||||||
|
<customerName>John Doe</customerName>
|
||||||
|
<blacklisted>false</blacklisted>
|
||||||
|
</arg0>
|
||||||
|
</ser:registerNewCustomer>
|
||||||
|
</soapenv:Body>
|
||||||
|
</soapenv:Envelope>
|
||||||
|
```
|
||||||
|
|
||||||
|
**SOAP Response Example (Success):**
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
|
||||||
|
<S:Body>
|
||||||
|
<ns2:registerNewCustomerResponse xmlns:ns2="http://service.example.com/">
|
||||||
|
<return>Registration Successful</return>
|
||||||
|
</ns2:registerNewCustomerResponse>
|
||||||
|
</S:Body>
|
||||||
|
</S:Envelope>
|
||||||
|
```
|
||||||
|
|
||||||
|
**SOAP Response Example (Duplicate):**
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
|
||||||
|
<S:Body>
|
||||||
|
<ns2:registerNewCustomerResponse xmlns:ns2="http://service.example.com/">
|
||||||
|
<return>Error: Customer already exists</return>
|
||||||
|
</ns2:registerNewCustomerResponse>
|
||||||
|
</S:Body>
|
||||||
|
</S:Envelope>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Business Rules:**
|
||||||
|
- Customer names must be unique
|
||||||
|
- Customer name cannot be null or empty
|
||||||
|
- Blacklist status is optional (defaults to false)
|
||||||
|
- Registration time is automatically recorded
|
||||||
|
|
||||||
|
#### 2. processLoanApplication
|
||||||
|
|
||||||
|
Processes a loan application with credit score-based approval logic.
|
||||||
|
|
||||||
|
**Operation:** `processLoanApplication`
|
||||||
|
|
||||||
|
**Input Parameters:**
|
||||||
|
| Parameter | Type | Required | Description |
|
||||||
|
|-----------|------|----------|-------------|
|
||||||
|
| request | LoanRequest | Yes | Loan application details |
|
||||||
|
| └─ applicantName | string | Yes | Applicant's full name |
|
||||||
|
| └─ requestedAmount | int | Yes | Loan amount requested (must be > 0) |
|
||||||
|
| └─ creditScore | int | No | Credit score (system may use default) |
|
||||||
|
|
||||||
|
**Returns:** `LoanResponse` - Loan decision result
|
||||||
|
|
||||||
|
**SOAP Request Example:**
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
|
xmlns:ser="http://service.example.com/">
|
||||||
|
<soapenv:Header/>
|
||||||
|
<soapenv:Body>
|
||||||
|
<ser:processLoanApplication>
|
||||||
|
<arg0>
|
||||||
|
<applicantName>John Doe</applicantName>
|
||||||
|
<requestedAmount>50000</requestedAmount>
|
||||||
|
<creditScore>720</creditScore>
|
||||||
|
</arg0>
|
||||||
|
</ser:processLoanApplication>
|
||||||
|
</soapenv:Body>
|
||||||
|
</soapenv:Envelope>
|
||||||
|
```
|
||||||
|
|
||||||
|
**SOAP Response Example (Approved):**
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
|
||||||
|
<S:Body>
|
||||||
|
<ns2:processLoanApplicationResponse xmlns:ns2="http://service.example.com/">
|
||||||
|
<return>
|
||||||
|
<approved>true</approved>
|
||||||
|
<approvedRate>3.5</approvedRate>
|
||||||
|
<rejectionReason></rejectionReason>
|
||||||
|
<message>Loan approved with excellent rate</message>
|
||||||
|
</return>
|
||||||
|
</ns2:processLoanApplicationResponse>
|
||||||
|
</S:Body>
|
||||||
|
</S:Envelope>
|
||||||
|
```
|
||||||
|
|
||||||
|
**SOAP Response Example (Rejected - Blacklisted):**
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
|
||||||
|
<S:Body>
|
||||||
|
<ns2:processLoanApplicationResponse xmlns:ns2="http://service.example.com/">
|
||||||
|
<return>
|
||||||
|
<approved>false</approved>
|
||||||
|
<approvedRate>0.0</approvedRate>
|
||||||
|
<rejectionReason>Applicant is blacklisted</rejectionReason>
|
||||||
|
<message>Loan application rejected</message>
|
||||||
|
</return>
|
||||||
|
</ns2:processLoanApplicationResponse>
|
||||||
|
</S:Body>
|
||||||
|
</S:Envelope>
|
||||||
|
```
|
||||||
|
|
||||||
|
**SOAP Response Example (Rejected - Low Credit):**
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
|
||||||
|
<S:Body>
|
||||||
|
<ns2:processLoanApplicationResponse xmlns:ns2="http://service.example.com/">
|
||||||
|
<return>
|
||||||
|
<approved>false</approved>
|
||||||
|
<approvedRate>0.0</approvedRate>
|
||||||
|
<rejectionReason>Credit score too low</rejectionReason>
|
||||||
|
<message>Loan application rejected</message>
|
||||||
|
</return>
|
||||||
|
</ns2:processLoanApplicationResponse>
|
||||||
|
</S:Body>
|
||||||
|
</S:Envelope>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Credit Score Approval Logic:**
|
||||||
|
|
||||||
|
| Credit Score Range | Approval Status | Interest Rate | Message |
|
||||||
|
|-------------------|-----------------|---------------|---------|
|
||||||
|
| >= 700 | Approved | 3.5% | Loan approved with excellent rate |
|
||||||
|
| 600 - 699 | Approved | 5.5% | Loan approved with standard rate |
|
||||||
|
| 500 - 599 | Approved | 8.5% | Loan approved with high risk rate |
|
||||||
|
| < 500 | Rejected | N/A | Credit score too low |
|
||||||
|
| Blacklisted | Rejected | N/A | Applicant is blacklisted |
|
||||||
|
|
||||||
|
**Business Rules:**
|
||||||
|
- Blacklisted applicants are automatically rejected
|
||||||
|
- Applicant name cannot be null or empty
|
||||||
|
- Requested amount must be greater than 0
|
||||||
|
- Credit score is retrieved from CreditScoreService (default: 700)
|
||||||
|
- All loan applications are logged to loan_history table
|
||||||
|
- System records: applicant name, amount, approval status, rate, rejection reason, and timestamp
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Models
|
||||||
|
|
||||||
|
### CustomerRegistrationRequest
|
||||||
|
|
||||||
|
Request model for customer registration.
|
||||||
|
|
||||||
|
**Fields:**
|
||||||
|
| Field | Type | Required | Description |
|
||||||
|
|-------|------|----------|-------------|
|
||||||
|
| customerName | string | Yes | Customer's full name (must be unique) |
|
||||||
|
| blacklisted | boolean | No | Whether customer is blacklisted (default: false) |
|
||||||
|
|
||||||
|
**Java Example:**
|
||||||
|
```java
|
||||||
|
CustomerRegistrationRequest request = new CustomerRegistrationRequest();
|
||||||
|
request.setCustomerName("John Doe");
|
||||||
|
request.setBlacklisted(false);
|
||||||
|
```
|
||||||
|
|
||||||
|
### LoanRequest
|
||||||
|
|
||||||
|
Request model for loan application.
|
||||||
|
|
||||||
|
**Fields:**
|
||||||
|
| Field | Type | Required | Description |
|
||||||
|
|-------|------|----------|-------------|
|
||||||
|
| applicantName | string | Yes | Applicant's full name |
|
||||||
|
| requestedAmount | int | Yes | Loan amount in currency units (must be > 0) |
|
||||||
|
| creditScore | int | No | Credit score (0-850 scale) |
|
||||||
|
|
||||||
|
**Java Example:**
|
||||||
|
```java
|
||||||
|
LoanRequest request = new LoanRequest();
|
||||||
|
request.setApplicantName("John Doe");
|
||||||
|
request.setRequestedAmount(50000);
|
||||||
|
request.setCreditScore(720);
|
||||||
|
```
|
||||||
|
|
||||||
|
### LoanResponse
|
||||||
|
|
||||||
|
Response model for loan application result.
|
||||||
|
|
||||||
|
**Fields:**
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| approved | boolean | Whether the loan was approved |
|
||||||
|
| approvedRate | double | Interest rate if approved (0.0 if rejected) |
|
||||||
|
| rejectionReason | string | Reason for rejection (null if approved) |
|
||||||
|
| message | string | Human-readable status message |
|
||||||
|
|
||||||
|
**Java Example:**
|
||||||
|
```java
|
||||||
|
LoanResponse response = loanService.processLoanApplication(request);
|
||||||
|
if (response.isApproved()) {
|
||||||
|
System.out.println("Approved at " + response.getApprovedRate() + "%");
|
||||||
|
} else {
|
||||||
|
System.out.println("Rejected: " + response.getRejectionReason());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Database Schema
|
||||||
|
|
||||||
|
The service uses SQLite database (`loan_app.db`) with the following schema:
|
||||||
|
|
||||||
|
### customers
|
||||||
|
|
||||||
|
Stores registered customer information.
|
||||||
|
|
||||||
|
| Column | Type | Constraints | Description |
|
||||||
|
|--------|------|-------------|-------------|
|
||||||
|
| customer_name | TEXT | PRIMARY KEY | Customer's unique name |
|
||||||
|
| is_blacklisted | INTEGER | DEFAULT 0 | Blacklist status (0=false, 1=true) |
|
||||||
|
| registered_at | TEXT | | Registration timestamp (ISO 8601) |
|
||||||
|
|
||||||
|
**Indexes:**
|
||||||
|
- Primary key on `customer_name`
|
||||||
|
|
||||||
|
### loan_history
|
||||||
|
|
||||||
|
Stores all loan application records.
|
||||||
|
|
||||||
|
| Column | Type | Constraints | Description |
|
||||||
|
|--------|------|-------------|-------------|
|
||||||
|
| id | INTEGER | PRIMARY KEY AUTOINCREMENT | Unique record ID |
|
||||||
|
| applicant_name | TEXT | | Applicant's name |
|
||||||
|
| requested_amount | INTEGER | | Loan amount requested |
|
||||||
|
| approved | INTEGER | | Approval status (0=rejected, 1=approved) |
|
||||||
|
| approved_rate | REAL | | Interest rate if approved |
|
||||||
|
| rejection_reason | TEXT | | Reason for rejection (null if approved) |
|
||||||
|
| processed_at | TEXT | | Processing timestamp (ISO 8601) |
|
||||||
|
|
||||||
|
**Indexes:**
|
||||||
|
- Primary key on `id`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### SOAP Faults
|
||||||
|
|
||||||
|
The service returns SOAP faults for validation errors and invalid requests.
|
||||||
|
|
||||||
|
**Common SOAP Fault Example:**
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
|
||||||
|
<S:Body>
|
||||||
|
<S:Fault>
|
||||||
|
<faultcode>S:Server</faultcode>
|
||||||
|
<faultstring>Customer name cannot be null or empty</faultstring>
|
||||||
|
</S:Fault>
|
||||||
|
</S:Body>
|
||||||
|
</S:Envelope>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Validation Rules
|
||||||
|
|
||||||
|
#### registerNewCustomer
|
||||||
|
- **SOAP Fault:** Customer name is null or empty
|
||||||
|
- **Response:** "Error: Customer already exists" (not a fault, returned as normal response)
|
||||||
|
|
||||||
|
#### processLoanApplication
|
||||||
|
- **SOAP Fault:** Applicant name is null or empty
|
||||||
|
- **SOAP Fault:** Requested amount is 0 or negative
|
||||||
|
- **Normal Response:** Rejection due to blacklist or low credit score
|
||||||
|
|
||||||
|
### HTTP Status Codes
|
||||||
|
|
||||||
|
| Status Code | Description |
|
||||||
|
|-------------|-------------|
|
||||||
|
| 200 OK | Request processed successfully (including business logic rejections) |
|
||||||
|
| 500 Internal Server Error | SOAP fault or server error |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### Using Python Test Scripts
|
||||||
|
|
||||||
|
The project includes Python test clients for easy testing:
|
||||||
|
|
||||||
|
#### Unified Test Script (Recommended)
|
||||||
|
|
||||||
|
Test all services with a single comprehensive script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd scripts
|
||||||
|
python test_all_services.py
|
||||||
|
```
|
||||||
|
|
||||||
|
This script includes:
|
||||||
|
- Automated test suite with 20+ test cases
|
||||||
|
- Tests all three service operations
|
||||||
|
- Interactive mode for manual testing
|
||||||
|
- Clear pass/fail indicators with summary
|
||||||
|
|
||||||
|
#### Individual Service Tests
|
||||||
|
|
||||||
|
Test specific services:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test Hello World Service
|
||||||
|
python test_hello_world.py
|
||||||
|
|
||||||
|
# Test Customer Registration
|
||||||
|
python test_register_customer.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using cURL
|
||||||
|
|
||||||
|
#### Test Hello World Service
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8080/jaxws-hello-world/hello \
|
||||||
|
-H "Content-Type: text/xml; charset=utf-8" \
|
||||||
|
-H "SOAPAction: " \
|
||||||
|
-d '<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
|
xmlns:ser="http://service.example.com/">
|
||||||
|
<soapenv:Header/>
|
||||||
|
<soapenv:Body>
|
||||||
|
<ser:getHelloWorld>
|
||||||
|
<arg0>Test</arg0>
|
||||||
|
</ser:getHelloWorld>
|
||||||
|
</soapenv:Body>
|
||||||
|
</soapenv:Envelope>'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Register Customer
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8080/jaxws-hello-world/loan \
|
||||||
|
-H "Content-Type: text/xml; charset=utf-8" \
|
||||||
|
-H "SOAPAction: " \
|
||||||
|
-d '<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
|
xmlns:ser="http://service.example.com/">
|
||||||
|
<soapenv:Header/>
|
||||||
|
<soapenv:Body>
|
||||||
|
<ser:registerNewCustomer>
|
||||||
|
<arg0>
|
||||||
|
<customerName>John Doe</customerName>
|
||||||
|
<blacklisted>false</blacklisted>
|
||||||
|
</arg0>
|
||||||
|
</ser:registerNewCustomer>
|
||||||
|
</soapenv:Body>
|
||||||
|
</soapenv:Envelope>'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Process Loan Application
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8080/jaxws-hello-world/loan \
|
||||||
|
-H "Content-Type: text/xml; charset=utf-8" \
|
||||||
|
-H "SOAPAction: " \
|
||||||
|
-d '<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
|
xmlns:ser="http://service.example.com/">
|
||||||
|
<soapenv:Header/>
|
||||||
|
<soapenv:Body>
|
||||||
|
<ser:processLoanApplication>
|
||||||
|
<arg0>
|
||||||
|
<applicantName>John Doe</applicantName>
|
||||||
|
<requestedAmount>50000</requestedAmount>
|
||||||
|
<creditScore>720</creditScore>
|
||||||
|
</arg0>
|
||||||
|
</ser:processLoanApplication>
|
||||||
|
</soapenv:Body>
|
||||||
|
</soapenv:Envelope>'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using SoapUI
|
||||||
|
|
||||||
|
1. Create a new SOAP project
|
||||||
|
2. Import WSDL:
|
||||||
|
- HelloWorldService: `http://localhost:8080/jaxws-hello-world/hello?wsdl`
|
||||||
|
- LoanApprovalService: `http://localhost:8080/jaxws-hello-world/loan?wsdl`
|
||||||
|
3. SoapUI will automatically generate request templates
|
||||||
|
4. Fill in the parameters and execute requests
|
||||||
|
|
||||||
|
### Checking the Database
|
||||||
|
|
||||||
|
After testing, you can inspect the SQLite database:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# View all customers
|
||||||
|
sqlite3 loan_app.db "SELECT * FROM customers;"
|
||||||
|
|
||||||
|
# View loan history
|
||||||
|
sqlite3 loan_app.db "SELECT * FROM loan_history;"
|
||||||
|
|
||||||
|
# View blacklisted customers
|
||||||
|
sqlite3 loan_app.db "SELECT * FROM customers WHERE is_blacklisted = 1;"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Production Considerations
|
||||||
|
|
||||||
|
### Security
|
||||||
|
- **Authentication:** No authentication is currently implemented (add WS-Security for production)
|
||||||
|
- **HTTPS:** Use HTTPS in production environments
|
||||||
|
- **Input Validation:** All inputs are validated for null/empty values
|
||||||
|
- **SQL Injection:** Using PreparedStatements to prevent SQL injection
|
||||||
|
|
||||||
|
### Database
|
||||||
|
- **Location:** `loan_app.db` in project root (Docker volume mounted)
|
||||||
|
- **Persistence:** Database persists between container restarts
|
||||||
|
- **Backup:** Regular backups recommended for production
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- **Connection Pooling:** Consider adding connection pooling for production
|
||||||
|
- **Caching:** Credit score service can be cached
|
||||||
|
- **Monitoring:** Add logging and monitoring for production use
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
- **Credit Score Service:** Currently returns fixed value (700), integrate with real credit bureau API for production
|
||||||
|
- **Interest Rates:** Rates are hardcoded, should be configurable
|
||||||
|
- **Database Path:** Configurable via environment variable for different environments
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For issues and feature requests, please refer to the project README.md.
|
||||||
|
|
||||||
|
**Project Repository:** See README.md for deployment instructions
|
||||||
|
**Docker Support:** Fully containerized with Docker Compose
|
||||||
|
**Test Scripts:** Located in `scripts/` directory
|
||||||
@ -14,6 +14,8 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
# Optional: Mount logs directory for debugging
|
# Optional: Mount logs directory for debugging
|
||||||
- ./logs:/usr/local/tomcat/logs
|
- ./logs:/usr/local/tomcat/logs
|
||||||
|
# Mount database file to root folder
|
||||||
|
- ./loan_app.db:/usr/local/tomcat/loan_app.db
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "curl", "-f", "http://localhost:8080/jaxws-hello-world/services/hello?wsdl"]
|
test: ["CMD", "curl", "-f", "http://localhost:8080/jaxws-hello-world/services/hello?wsdl"]
|
||||||
|
|||||||
@ -1,8 +1,135 @@
|
|||||||
# Test Scripts for JAX-WS Hello World Service
|
# Test Scripts for JAX-WS Services
|
||||||
|
|
||||||
This folder contains test scripts to interact with the JAX-WS Hello World Service.
|
This folder contains test scripts to interact with the JAX-WS services.
|
||||||
|
|
||||||
## Python Test Script
|
## Available Test Scripts
|
||||||
|
|
||||||
|
- `test_all_services.py` - **Unified test script for all services** (Recommended)
|
||||||
|
- `test_hello_world.py` - Test the Hello World SOAP service
|
||||||
|
- `test_register_customer.py` - Test the Customer Registration SOAP service
|
||||||
|
- `simple_test.py` - Simple automated test script for CI/CD
|
||||||
|
|
||||||
|
## Unified Test Script (Recommended)
|
||||||
|
|
||||||
|
### test_all_services.py
|
||||||
|
|
||||||
|
This is the **recommended** way to test all services. It provides comprehensive testing for all three service operations in a single script.
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Tests all service operations (Hello World, Customer Registration, Loan Processing)
|
||||||
|
- Automated test suite with 20+ test cases
|
||||||
|
- Interactive mode for manual testing
|
||||||
|
- Clear pass/fail indicators with test summary
|
||||||
|
- Comprehensive error handling and validation testing
|
||||||
|
|
||||||
|
**Running the unified test:**
|
||||||
|
|
||||||
|
**Windows:**
|
||||||
|
```cmd
|
||||||
|
cd scripts
|
||||||
|
python test_all_services.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Linux/Mac:**
|
||||||
|
```bash
|
||||||
|
cd scripts
|
||||||
|
python3 test_all_services.py
|
||||||
|
# OR
|
||||||
|
chmod +x test_all_services.py
|
||||||
|
./test_all_services.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**What it tests:**
|
||||||
|
|
||||||
|
1. **Hello World Service**
|
||||||
|
- Normal greetings
|
||||||
|
- Edge cases (empty names)
|
||||||
|
|
||||||
|
2. **Customer Registration Service**
|
||||||
|
- Regular customer registration
|
||||||
|
- Blacklisted customer registration
|
||||||
|
- Duplicate registration handling
|
||||||
|
- Validation error handling
|
||||||
|
|
||||||
|
3. **Loan Application Processing**
|
||||||
|
- High credit score approvals (3.5% rate)
|
||||||
|
- Good credit score approvals (5.5% rate)
|
||||||
|
- Fair credit score approvals (8.5% rate)
|
||||||
|
- Low credit score rejections
|
||||||
|
- Blacklisted customer rejections
|
||||||
|
- Validation error handling (empty names, invalid amounts)
|
||||||
|
|
||||||
|
**Interactive Mode:**
|
||||||
|
|
||||||
|
After automated tests complete, you can enter interactive mode with these commands:
|
||||||
|
- `hello <name>` - Test Hello World service
|
||||||
|
- `register <name>` - Register a regular customer
|
||||||
|
- `blacklist <name>` - Register a blacklisted customer
|
||||||
|
- `loan <name> <amount> <credit_score>` - Process loan application
|
||||||
|
- `quit` - Exit
|
||||||
|
|
||||||
|
**Example output:**
|
||||||
|
```
|
||||||
|
================================================================================
|
||||||
|
JAX-WS UNIFIED TEST CLIENT
|
||||||
|
================================================================================
|
||||||
|
Testing all services and operations
|
||||||
|
|
||||||
|
Checking services availability...
|
||||||
|
|
||||||
|
Hello World Service: Service is accessible
|
||||||
|
Loan Approval Service: Service is accessible
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
AUTOMATED TEST SUITE
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
1. Testing Hello World Service
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
✓ PASS | getHelloWorld('John') - Should return greeting
|
||||||
|
Hello World, John!
|
||||||
|
|
||||||
|
✓ PASS | getHelloWorld('Alice') - Should return greeting
|
||||||
|
Hello World, Alice!
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
2. Testing Customer Registration Service
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
✓ PASS | registerNewCustomer('Alice Johnson', blacklisted=False) - Register regular customer
|
||||||
|
Registration Successful
|
||||||
|
|
||||||
|
✓ PASS | registerNewCustomer('Charlie Brown', blacklisted=True) - Register blacklisted customer
|
||||||
|
Registration Successful
|
||||||
|
|
||||||
|
✓ PASS | registerNewCustomer('Alice Johnson', blacklisted=False) - Duplicate registration (should fail)
|
||||||
|
Error: Customer already exists
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
3. Testing Loan Application Processing Service
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
✓ PASS | processLoanApplication('Alice Johnson', $50000, score=750) - High credit score
|
||||||
|
approved: True
|
||||||
|
approvedRate: 3.5
|
||||||
|
message: Loan approved with excellent rate
|
||||||
|
|
||||||
|
✓ PASS | processLoanApplication('Charlie Brown', $40000, score=700) - Blacklisted customer
|
||||||
|
approved: False
|
||||||
|
rejectionReason: Applicant is blacklisted
|
||||||
|
message: Loan application rejected
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
TEST SUMMARY
|
||||||
|
================================================================================
|
||||||
|
Total Tests: 20
|
||||||
|
Passed: 20
|
||||||
|
Failed: 0
|
||||||
|
Success Rate: 100.0%
|
||||||
|
|
||||||
|
Would you like to enter interactive mode? (y/n):
|
||||||
|
```
|
||||||
|
|
||||||
|
## Python Test Scripts
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
@ -105,6 +232,114 @@ response = client.call_hello_world("Your Name")
|
|||||||
print(response) # Output: Hello World, Your Name!
|
print(response) # Output: Hello World, Your Name!
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Customer Registration Test Script
|
||||||
|
|
||||||
|
### Running the Customer Registration Test
|
||||||
|
|
||||||
|
Make sure the JAX-WS service is running (using Docker or traditional setup).
|
||||||
|
|
||||||
|
**Windows:**
|
||||||
|
```cmd
|
||||||
|
python test_register_customer.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Linux/Mac:**
|
||||||
|
```bash
|
||||||
|
python3 test_register_customer.py
|
||||||
|
# OR
|
||||||
|
chmod +x test_register_customer.py
|
||||||
|
./test_register_customer.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### What the Script Does
|
||||||
|
|
||||||
|
The `test_register_customer.py` script:
|
||||||
|
|
||||||
|
1. **Checks WSDL availability** - Verifies the LoanApprovalService is running
|
||||||
|
2. **Tests customer registration** - Registers multiple customers with different scenarios
|
||||||
|
3. **Tests blacklist functionality** - Registers both regular and blacklisted customers
|
||||||
|
4. **Tests duplicate registration** - Verifies that duplicate registrations are handled correctly
|
||||||
|
5. **Interactive mode** - Allows you to register customers interactively
|
||||||
|
|
||||||
|
### Example Output
|
||||||
|
|
||||||
|
```
|
||||||
|
======================================================================
|
||||||
|
Customer Registration Service Test Client
|
||||||
|
======================================================================
|
||||||
|
|
||||||
|
1. Checking WSDL availability...
|
||||||
|
WSDL is accessible
|
||||||
|
|
||||||
|
2. Testing Customer Registration...
|
||||||
|
|
||||||
|
Test 1: Regular customer
|
||||||
|
Customer Name: John Doe
|
||||||
|
Blacklisted: False
|
||||||
|
Result: Registration Successful
|
||||||
|
|
||||||
|
Test 2: Regular customer
|
||||||
|
Customer Name: Jane Smith
|
||||||
|
Blacklisted: False
|
||||||
|
Result: Registration Successful
|
||||||
|
|
||||||
|
Test 3: Blacklisted customer
|
||||||
|
Customer Name: Bad Actor
|
||||||
|
Blacklisted: True
|
||||||
|
Result: Registration Successful
|
||||||
|
|
||||||
|
Test 4: Duplicate registration (should fail)
|
||||||
|
Customer Name: John Doe
|
||||||
|
Blacklisted: False
|
||||||
|
Result: Error: Customer already exists
|
||||||
|
|
||||||
|
3. Interactive Mode
|
||||||
|
------------------------------------------------------------------
|
||||||
|
Commands:
|
||||||
|
- register <name>: Register a regular customer
|
||||||
|
- blacklist <name>: Register a blacklisted customer
|
||||||
|
- quit: Exit
|
||||||
|
|
||||||
|
> register Alice Smith
|
||||||
|
Result: Registration Successful
|
||||||
|
|
||||||
|
> blacklist Evil Corp
|
||||||
|
Result: Registration Successful
|
||||||
|
|
||||||
|
> quit
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
Test completed!
|
||||||
|
======================================================================
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using the CustomerRegistrationClient Class
|
||||||
|
|
||||||
|
You can also use the `CustomerRegistrationClient` class in your own Python scripts:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from test_register_customer import CustomerRegistrationClient
|
||||||
|
|
||||||
|
# Create client
|
||||||
|
client = CustomerRegistrationClient()
|
||||||
|
|
||||||
|
# Check if service is available
|
||||||
|
is_available, message = client.check_wsdl()
|
||||||
|
print(message)
|
||||||
|
|
||||||
|
# Register a regular customer
|
||||||
|
response = client.register_customer("John Doe", is_blacklisted=False)
|
||||||
|
print(response) # Output: Registration Successful
|
||||||
|
|
||||||
|
# Register a blacklisted customer
|
||||||
|
response = client.register_customer("Bad Actor", is_blacklisted=True)
|
||||||
|
print(response) # Output: Registration Successful
|
||||||
|
|
||||||
|
# Try to register duplicate
|
||||||
|
response = client.register_customer("John Doe", is_blacklisted=False)
|
||||||
|
print(response) # Output: Error: Customer already exists
|
||||||
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Service Not Available
|
### Service Not Available
|
||||||
|
|||||||
477
scripts/test_all_services.py
Normal file
477
scripts/test_all_services.py
Normal file
@ -0,0 +1,477 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Unified test script for all JAX-WS services
|
||||||
|
Tests HelloWorldService and LoanApprovalService
|
||||||
|
"""
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
from xml.dom import minidom
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
class JAXWSTestClient:
|
||||||
|
"""Unified client for testing all JAX-WS services"""
|
||||||
|
|
||||||
|
def __init__(self, base_url="http://localhost:8080/jaxws-hello-world"):
|
||||||
|
"""
|
||||||
|
Initialize the test client
|
||||||
|
|
||||||
|
Args:
|
||||||
|
base_url: The base URL for all services
|
||||||
|
"""
|
||||||
|
self.base_url = base_url
|
||||||
|
self.hello_service_url = f"{base_url}/hello"
|
||||||
|
self.loan_service_url = f"{base_url}/loan"
|
||||||
|
self.headers = {
|
||||||
|
'Content-Type': 'text/xml; charset=utf-8',
|
||||||
|
'SOAPAction': ''
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==================== Hello World Service ====================
|
||||||
|
|
||||||
|
def test_hello_world(self, name):
|
||||||
|
"""
|
||||||
|
Test Hello World service
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: Name to send to the service
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple of (success: bool, result: str)
|
||||||
|
"""
|
||||||
|
soap_envelope = f"""<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
|
xmlns:ser="http://service.example.com/">
|
||||||
|
<soapenv:Header/>
|
||||||
|
<soapenv:Body>
|
||||||
|
<ser:getHelloWorld>
|
||||||
|
<arg0>{name}</arg0>
|
||||||
|
</ser:getHelloWorld>
|
||||||
|
</soapenv:Body>
|
||||||
|
</soapenv:Envelope>"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(
|
||||||
|
self.hello_service_url,
|
||||||
|
data=soap_envelope,
|
||||||
|
headers=self.headers,
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
root = ET.fromstring(response.text)
|
||||||
|
namespaces = {
|
||||||
|
'S': 'http://schemas.xmlsoap.org/soap/envelope/',
|
||||||
|
'ns2': 'http://service.example.com/'
|
||||||
|
}
|
||||||
|
|
||||||
|
return_elem = root.find('.//ns2:getHelloWorldResponse/return', namespaces)
|
||||||
|
if return_elem is not None:
|
||||||
|
return True, return_elem.text
|
||||||
|
else:
|
||||||
|
return False, "Could not find return value in response"
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
return False, f"Error: {str(e)}"
|
||||||
|
|
||||||
|
# ==================== Customer Registration ====================
|
||||||
|
|
||||||
|
def register_customer(self, customer_name, is_blacklisted=False):
|
||||||
|
"""
|
||||||
|
Register a new customer
|
||||||
|
|
||||||
|
Args:
|
||||||
|
customer_name: Customer name
|
||||||
|
is_blacklisted: Blacklist status
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple of (success: bool, result: str)
|
||||||
|
"""
|
||||||
|
blacklisted_value = "true" if is_blacklisted else "false"
|
||||||
|
|
||||||
|
soap_envelope = f"""<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
|
xmlns:ser="http://service.example.com/">
|
||||||
|
<soapenv:Header/>
|
||||||
|
<soapenv:Body>
|
||||||
|
<ser:registerNewCustomer>
|
||||||
|
<arg0>
|
||||||
|
<customerName>{customer_name}</customerName>
|
||||||
|
<blacklisted>{blacklisted_value}</blacklisted>
|
||||||
|
</arg0>
|
||||||
|
</ser:registerNewCustomer>
|
||||||
|
</soapenv:Body>
|
||||||
|
</soapenv:Envelope>"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(
|
||||||
|
self.loan_service_url,
|
||||||
|
data=soap_envelope,
|
||||||
|
headers=self.headers,
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
result = self._parse_soap_response(response.text, 'registerNewCustomerResponse')
|
||||||
|
return True, result
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
return False, f"Error: {str(e)}"
|
||||||
|
|
||||||
|
# ==================== Loan Application Processing ====================
|
||||||
|
|
||||||
|
def process_loan_application(self, applicant_name, requested_amount, credit_score=700):
|
||||||
|
"""
|
||||||
|
Process a loan application
|
||||||
|
|
||||||
|
Args:
|
||||||
|
applicant_name: Applicant name
|
||||||
|
requested_amount: Loan amount requested
|
||||||
|
credit_score: Credit score (optional)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple of (success: bool, result: dict)
|
||||||
|
"""
|
||||||
|
soap_envelope = f"""<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
|
xmlns:ser="http://service.example.com/">
|
||||||
|
<soapenv:Header/>
|
||||||
|
<soapenv:Body>
|
||||||
|
<ser:processLoanApplication>
|
||||||
|
<arg0>
|
||||||
|
<applicantName>{applicant_name}</applicantName>
|
||||||
|
<requestedAmount>{requested_amount}</requestedAmount>
|
||||||
|
<creditScore>{credit_score}</creditScore>
|
||||||
|
</arg0>
|
||||||
|
</ser:processLoanApplication>
|
||||||
|
</soapenv:Body>
|
||||||
|
</soapenv:Envelope>"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(
|
||||||
|
self.loan_service_url,
|
||||||
|
data=soap_envelope,
|
||||||
|
headers=self.headers,
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
result = self._parse_loan_response(response.text)
|
||||||
|
return True, result
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
return False, {"error": str(e)}
|
||||||
|
|
||||||
|
# ==================== Helper Methods ====================
|
||||||
|
|
||||||
|
def _parse_soap_response(self, response_xml, operation_name):
|
||||||
|
"""Parse simple SOAP response"""
|
||||||
|
try:
|
||||||
|
root = ET.fromstring(response_xml)
|
||||||
|
namespaces = {
|
||||||
|
'S': 'http://schemas.xmlsoap.org/soap/envelope/',
|
||||||
|
'ns2': 'http://service.example.com/'
|
||||||
|
}
|
||||||
|
|
||||||
|
return_elem = root.find(f'.//ns2:{operation_name}/return', namespaces)
|
||||||
|
if return_elem is not None:
|
||||||
|
return return_elem.text
|
||||||
|
|
||||||
|
# Check for SOAP fault
|
||||||
|
fault_elem = root.find('.//S:Fault', namespaces)
|
||||||
|
if fault_elem is not None:
|
||||||
|
fault_string = fault_elem.find('.//faultstring', namespaces)
|
||||||
|
if fault_string is not None:
|
||||||
|
return f"SOAP Fault: {fault_string.text}"
|
||||||
|
|
||||||
|
return "Could not parse response"
|
||||||
|
|
||||||
|
except ET.ParseError as e:
|
||||||
|
return f"Parse error: {str(e)}"
|
||||||
|
|
||||||
|
def _parse_loan_response(self, response_xml):
|
||||||
|
"""Parse loan application response"""
|
||||||
|
try:
|
||||||
|
root = ET.fromstring(response_xml)
|
||||||
|
namespaces = {
|
||||||
|
'S': 'http://schemas.xmlsoap.org/soap/envelope/',
|
||||||
|
'ns2': 'http://service.example.com/'
|
||||||
|
}
|
||||||
|
|
||||||
|
return_elem = root.find('.//ns2:processLoanApplicationResponse/return', namespaces)
|
||||||
|
|
||||||
|
if return_elem is not None:
|
||||||
|
result = {}
|
||||||
|
for child in return_elem:
|
||||||
|
tag = child.tag.split('}')[-1] # Remove namespace
|
||||||
|
result[tag] = child.text
|
||||||
|
|
||||||
|
# Convert types
|
||||||
|
result['approved'] = result.get('approved', 'false').lower() == 'true'
|
||||||
|
result['approvedRate'] = float(result.get('approvedRate', 0.0))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Check for SOAP fault
|
||||||
|
fault_elem = root.find('.//S:Fault', namespaces)
|
||||||
|
if fault_elem is not None:
|
||||||
|
fault_string = fault_elem.find('.//faultstring', namespaces)
|
||||||
|
if fault_string is not None:
|
||||||
|
return {"error": f"SOAP Fault: {fault_string.text}"}
|
||||||
|
|
||||||
|
return {"error": "Could not parse response"}
|
||||||
|
|
||||||
|
except ET.ParseError as e:
|
||||||
|
return {"error": f"Parse error: {str(e)}"}
|
||||||
|
|
||||||
|
def check_service(self, service_url):
|
||||||
|
"""Check if a service is available"""
|
||||||
|
try:
|
||||||
|
response = requests.get(f"{service_url}?wsdl", timeout=5)
|
||||||
|
response.raise_for_status()
|
||||||
|
return True, "Service is accessible"
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
return False, f"Service not available: {str(e)}"
|
||||||
|
|
||||||
|
|
||||||
|
def print_header(text, char="="):
|
||||||
|
"""Print a formatted header"""
|
||||||
|
print()
|
||||||
|
print(char * 80)
|
||||||
|
print(text)
|
||||||
|
print(char * 80)
|
||||||
|
|
||||||
|
|
||||||
|
def print_test_result(test_name, success, result):
|
||||||
|
"""Print test result"""
|
||||||
|
status = "✓ PASS" if success else "✗ FAIL"
|
||||||
|
print(f" {status} | {test_name}")
|
||||||
|
if isinstance(result, dict):
|
||||||
|
for key, value in result.items():
|
||||||
|
print(f" {key}: {value}")
|
||||||
|
else:
|
||||||
|
print(f" {result}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def run_automated_tests(client):
|
||||||
|
"""Run all automated tests"""
|
||||||
|
|
||||||
|
print_header("AUTOMATED TEST SUITE", "=")
|
||||||
|
|
||||||
|
total_tests = 0
|
||||||
|
passed_tests = 0
|
||||||
|
|
||||||
|
# ==================== Test 1: Hello World Service ====================
|
||||||
|
print_header("1. Testing Hello World Service", "-")
|
||||||
|
|
||||||
|
test_cases = [
|
||||||
|
("John", "Should return greeting"),
|
||||||
|
("Alice", "Should return greeting"),
|
||||||
|
("", "Empty name test"),
|
||||||
|
]
|
||||||
|
|
||||||
|
for name, description in test_cases:
|
||||||
|
total_tests += 1
|
||||||
|
success, result = client.test_hello_world(name)
|
||||||
|
print_test_result(f"getHelloWorld('{name}') - {description}", success, result)
|
||||||
|
if success:
|
||||||
|
passed_tests += 1
|
||||||
|
|
||||||
|
# ==================== Test 2: Customer Registration ====================
|
||||||
|
print_header("2. Testing Customer Registration Service", "-")
|
||||||
|
|
||||||
|
registration_tests = [
|
||||||
|
("Alice Johnson", False, "Register regular customer"),
|
||||||
|
("Bob Smith", False, "Register another customer"),
|
||||||
|
("Charlie Brown", True, "Register blacklisted customer"),
|
||||||
|
("David Lee", False, "Register regular customer"),
|
||||||
|
("Alice Johnson", False, "Duplicate registration (should fail)"),
|
||||||
|
("", False, "Empty name (should fail)"),
|
||||||
|
]
|
||||||
|
|
||||||
|
for customer_name, is_blacklisted, description in registration_tests:
|
||||||
|
total_tests += 1
|
||||||
|
success, result = client.register_customer(customer_name, is_blacklisted)
|
||||||
|
|
||||||
|
# For empty name, we expect a SOAP fault or error
|
||||||
|
if customer_name == "" and "Fault" in str(result):
|
||||||
|
success = True # Expected failure
|
||||||
|
|
||||||
|
print_test_result(
|
||||||
|
f"registerNewCustomer('{customer_name}', blacklisted={is_blacklisted}) - {description}",
|
||||||
|
success,
|
||||||
|
result
|
||||||
|
)
|
||||||
|
if success:
|
||||||
|
passed_tests += 1
|
||||||
|
|
||||||
|
# ==================== Test 3: Loan Application Processing ====================
|
||||||
|
print_header("3. Testing Loan Application Processing Service", "-")
|
||||||
|
|
||||||
|
loan_tests = [
|
||||||
|
("Alice Johnson", 50000, 750, "High credit score - should approve with excellent rate"),
|
||||||
|
("Bob Smith", 30000, 650, "Good credit score - should approve with standard rate"),
|
||||||
|
("David Lee", 20000, 550, "Fair credit score - should approve with high rate"),
|
||||||
|
("Charlie Brown", 40000, 700, "Blacklisted customer - should reject"),
|
||||||
|
("Eve Williams", 100000, 450, "Low credit score - should reject"),
|
||||||
|
("", 50000, 700, "Empty name - should return SOAP fault"),
|
||||||
|
("Alice Johnson", 0, 700, "Zero amount - should return SOAP fault"),
|
||||||
|
("Alice Johnson", -1000, 700, "Negative amount - should return SOAP fault"),
|
||||||
|
]
|
||||||
|
|
||||||
|
for applicant_name, amount, credit_score, description in loan_tests:
|
||||||
|
total_tests += 1
|
||||||
|
success, result = client.process_loan_application(applicant_name, amount, credit_score)
|
||||||
|
|
||||||
|
# For validation errors, check if we got expected error
|
||||||
|
if applicant_name == "" or amount <= 0:
|
||||||
|
if isinstance(result, dict) and "error" in result:
|
||||||
|
success = True # Expected error
|
||||||
|
|
||||||
|
print_test_result(
|
||||||
|
f"processLoanApplication('{applicant_name}', ${amount}, score={credit_score}) - {description}",
|
||||||
|
success,
|
||||||
|
result
|
||||||
|
)
|
||||||
|
if success:
|
||||||
|
passed_tests += 1
|
||||||
|
|
||||||
|
# ==================== Test Summary ====================
|
||||||
|
print_header("TEST SUMMARY", "=")
|
||||||
|
print(f" Total Tests: {total_tests}")
|
||||||
|
print(f" Passed: {passed_tests}")
|
||||||
|
print(f" Failed: {total_tests - passed_tests}")
|
||||||
|
print(f" Success Rate: {(passed_tests/total_tests)*100:.1f}%")
|
||||||
|
print()
|
||||||
|
|
||||||
|
return passed_tests == total_tests
|
||||||
|
|
||||||
|
|
||||||
|
def run_interactive_mode(client):
|
||||||
|
"""Run interactive testing mode"""
|
||||||
|
|
||||||
|
print_header("INTERACTIVE MODE", "=")
|
||||||
|
print("Available commands:")
|
||||||
|
print(" hello <name> - Test Hello World service")
|
||||||
|
print(" register <name> - Register regular customer")
|
||||||
|
print(" blacklist <name> - Register blacklisted customer")
|
||||||
|
print(" loan <name> <amount> <credit_score> - Process loan application")
|
||||||
|
print(" quit - Exit interactive mode")
|
||||||
|
print()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
user_input = input(">> ").strip()
|
||||||
|
|
||||||
|
if not user_input:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if user_input.lower() in ['quit', 'exit', 'q']:
|
||||||
|
break
|
||||||
|
|
||||||
|
parts = user_input.split()
|
||||||
|
command = parts[0].lower()
|
||||||
|
|
||||||
|
if command == "hello" and len(parts) >= 2:
|
||||||
|
name = " ".join(parts[1:])
|
||||||
|
success, result = client.test_hello_world(name)
|
||||||
|
print(f" Result: {result}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
elif command == "register" and len(parts) >= 2:
|
||||||
|
name = " ".join(parts[1:])
|
||||||
|
success, result = client.register_customer(name, False)
|
||||||
|
print(f" Result: {result}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
elif command == "blacklist" and len(parts) >= 2:
|
||||||
|
name = " ".join(parts[1:])
|
||||||
|
success, result = client.register_customer(name, True)
|
||||||
|
print(f" Result: {result}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
elif command == "loan" and len(parts) >= 4:
|
||||||
|
name = parts[1]
|
||||||
|
try:
|
||||||
|
amount = int(parts[2])
|
||||||
|
credit_score = int(parts[3])
|
||||||
|
success, result = client.process_loan_application(name, amount, credit_score)
|
||||||
|
|
||||||
|
if isinstance(result, dict):
|
||||||
|
print(" Loan Application Result:")
|
||||||
|
for key, value in result.items():
|
||||||
|
print(f" {key}: {value}")
|
||||||
|
else:
|
||||||
|
print(f" Result: {result}")
|
||||||
|
print()
|
||||||
|
except ValueError:
|
||||||
|
print(" Error: Amount and credit score must be numbers")
|
||||||
|
print()
|
||||||
|
|
||||||
|
else:
|
||||||
|
print(" Invalid command. Type 'quit' to exit or use one of the available commands.")
|
||||||
|
print()
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n Interrupted by user")
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Error: {str(e)}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main function"""
|
||||||
|
|
||||||
|
print_header("JAX-WS UNIFIED TEST CLIENT", "=")
|
||||||
|
print("Testing all services and operations")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Create client
|
||||||
|
client = JAXWSTestClient()
|
||||||
|
|
||||||
|
# Check services availability
|
||||||
|
print("Checking services availability...")
|
||||||
|
print()
|
||||||
|
|
||||||
|
hello_ok, hello_msg = client.check_service(client.hello_service_url)
|
||||||
|
print(f" Hello World Service: {hello_msg}")
|
||||||
|
|
||||||
|
loan_ok, loan_msg = client.check_service(client.loan_service_url)
|
||||||
|
print(f" Loan Approval Service: {loan_msg}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
if not hello_ok or not loan_ok:
|
||||||
|
print(" ERROR: One or more services are not available.")
|
||||||
|
print(" Please ensure the Docker container is running:")
|
||||||
|
print(" docker-compose up -d")
|
||||||
|
print()
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# Run automated tests
|
||||||
|
try:
|
||||||
|
all_passed = run_automated_tests(client)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n Tests interrupted by user")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# Ask user if they want interactive mode
|
||||||
|
print()
|
||||||
|
print("Would you like to enter interactive mode? (y/n): ", end="")
|
||||||
|
try:
|
||||||
|
choice = input().strip().lower()
|
||||||
|
if choice in ['y', 'yes']:
|
||||||
|
run_interactive_mode(client)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n Skipping interactive mode")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print_header("TEST COMPLETED", "=")
|
||||||
|
|
||||||
|
return 0 if all_passed else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
250
scripts/test_register_customer.py
Normal file
250
scripts/test_register_customer.py
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Python script to test registerNewCustomer SOAP Web Service
|
||||||
|
"""
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
from xml.dom import minidom
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerRegistrationClient:
|
||||||
|
"""Client for Customer Registration SOAP Service"""
|
||||||
|
|
||||||
|
def __init__(self, service_url="http://localhost:8080/jaxws-hello-world/loan"):
|
||||||
|
"""
|
||||||
|
Initialize the Customer Registration client
|
||||||
|
|
||||||
|
Args:
|
||||||
|
service_url: The SOAP service endpoint URL
|
||||||
|
"""
|
||||||
|
self.service_url = service_url
|
||||||
|
self.wsdl_url = f"{service_url}?wsdl"
|
||||||
|
self.headers = {
|
||||||
|
'Content-Type': 'text/xml; charset=utf-8',
|
||||||
|
'SOAPAction': ''
|
||||||
|
}
|
||||||
|
|
||||||
|
def create_soap_envelope(self, customer_name, is_blacklisted=False):
|
||||||
|
"""
|
||||||
|
Create SOAP envelope for registerNewCustomer request
|
||||||
|
|
||||||
|
Args:
|
||||||
|
customer_name: The customer name to register
|
||||||
|
is_blacklisted: Whether the customer is blacklisted (default: False)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
XML string of SOAP envelope
|
||||||
|
"""
|
||||||
|
blacklisted_value = "true" if is_blacklisted else "false"
|
||||||
|
|
||||||
|
soap_envelope = f"""<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
|
xmlns:ser="http://service.example.com/">
|
||||||
|
<soapenv:Header/>
|
||||||
|
<soapenv:Body>
|
||||||
|
<ser:registerNewCustomer>
|
||||||
|
<arg0>
|
||||||
|
<customerName>{customer_name}</customerName>
|
||||||
|
<blacklisted>{blacklisted_value}</blacklisted>
|
||||||
|
</arg0>
|
||||||
|
</ser:registerNewCustomer>
|
||||||
|
</soapenv:Body>
|
||||||
|
</soapenv:Envelope>"""
|
||||||
|
return soap_envelope
|
||||||
|
|
||||||
|
def register_customer(self, customer_name, is_blacklisted=False):
|
||||||
|
"""
|
||||||
|
Register a new customer via SOAP service
|
||||||
|
|
||||||
|
Args:
|
||||||
|
customer_name: The customer name to register
|
||||||
|
is_blacklisted: Whether the customer is blacklisted
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response message from the service
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Create SOAP envelope
|
||||||
|
soap_request = self.create_soap_envelope(customer_name, is_blacklisted)
|
||||||
|
|
||||||
|
# Make the SOAP request
|
||||||
|
response = requests.post(
|
||||||
|
self.service_url,
|
||||||
|
data=soap_request,
|
||||||
|
headers=self.headers,
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if request was successful
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
# Parse the response
|
||||||
|
return self.parse_response(response.text)
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
return f"Error calling service: {str(e)}"
|
||||||
|
|
||||||
|
def parse_response(self, response_xml):
|
||||||
|
"""
|
||||||
|
Parse the SOAP response
|
||||||
|
|
||||||
|
Args:
|
||||||
|
response_xml: The XML response from the service
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The return value from the service
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Parse XML
|
||||||
|
root = ET.fromstring(response_xml)
|
||||||
|
|
||||||
|
# Find the return element
|
||||||
|
# Handle namespaces
|
||||||
|
namespaces = {
|
||||||
|
'S': 'http://schemas.xmlsoap.org/soap/envelope/',
|
||||||
|
'ns2': 'http://service.example.com/'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Find the return value
|
||||||
|
return_elem = root.find('.//ns2:registerNewCustomerResponse/return', namespaces)
|
||||||
|
|
||||||
|
if return_elem is not None:
|
||||||
|
return return_elem.text
|
||||||
|
else:
|
||||||
|
# Check for SOAP fault
|
||||||
|
fault_elem = root.find('.//S:Fault', namespaces)
|
||||||
|
if fault_elem is not None:
|
||||||
|
fault_string = fault_elem.find('.//faultstring', namespaces)
|
||||||
|
if fault_string is not None:
|
||||||
|
return f"SOAP Fault: {fault_string.text}"
|
||||||
|
return "Could not find return value in response"
|
||||||
|
|
||||||
|
except ET.ParseError as e:
|
||||||
|
return f"Error parsing response: {str(e)}"
|
||||||
|
|
||||||
|
def check_wsdl(self):
|
||||||
|
"""
|
||||||
|
Check if WSDL is accessible
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple of (success: bool, message: str)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
response = requests.get(self.wsdl_url, timeout=5)
|
||||||
|
response.raise_for_status()
|
||||||
|
return True, "WSDL is accessible"
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
return False, f"Cannot access WSDL: {str(e)}"
|
||||||
|
|
||||||
|
def pretty_print_xml(self, xml_string):
|
||||||
|
"""
|
||||||
|
Pretty print XML string
|
||||||
|
|
||||||
|
Args:
|
||||||
|
xml_string: XML string to format
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Formatted XML string
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
parsed = minidom.parseString(xml_string)
|
||||||
|
return parsed.toprettyxml(indent=" ")
|
||||||
|
except Exception:
|
||||||
|
return xml_string
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main function to test the Customer Registration service"""
|
||||||
|
|
||||||
|
print("=" * 70)
|
||||||
|
print("Customer Registration Service Test Client")
|
||||||
|
print("=" * 70)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Create client
|
||||||
|
client = CustomerRegistrationClient()
|
||||||
|
|
||||||
|
# Check WSDL
|
||||||
|
print("1. Checking WSDL availability...")
|
||||||
|
wsdl_ok, wsdl_msg = client.check_wsdl()
|
||||||
|
print(f" {wsdl_msg}")
|
||||||
|
if not wsdl_ok:
|
||||||
|
print(" Service is not available. Make sure the service is running.")
|
||||||
|
return
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Test customer registrations
|
||||||
|
test_cases = [
|
||||||
|
{"name": "John Doe", "blacklisted": False, "description": "Regular customer"},
|
||||||
|
{"name": "Jane Smith", "blacklisted": False, "description": "Regular customer"},
|
||||||
|
{"name": "Bad Actor", "blacklisted": True, "description": "Blacklisted customer"},
|
||||||
|
{"name": "John Doe", "blacklisted": False, "description": "Duplicate registration (should fail)"},
|
||||||
|
{"name": "Alice Johnson", "blacklisted": False, "description": "Regular customer"},
|
||||||
|
{"name": "Bob Wilson", "blacklisted": True, "description": "Blacklisted customer"},
|
||||||
|
]
|
||||||
|
|
||||||
|
print("2. Testing Customer Registration...")
|
||||||
|
print()
|
||||||
|
|
||||||
|
for i, test_case in enumerate(test_cases, 1):
|
||||||
|
name = test_case["name"]
|
||||||
|
blacklisted = test_case["blacklisted"]
|
||||||
|
description = test_case["description"]
|
||||||
|
|
||||||
|
print(f" Test {i}: {description}")
|
||||||
|
print(f" Customer Name: {name}")
|
||||||
|
print(f" Blacklisted: {blacklisted}")
|
||||||
|
|
||||||
|
result = client.register_customer(name, blacklisted)
|
||||||
|
print(f" Result: {result}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Interactive mode
|
||||||
|
print("3. Interactive Mode")
|
||||||
|
print(" " + "-" * 66)
|
||||||
|
print(" Commands:")
|
||||||
|
print(" - register <name>: Register a regular customer")
|
||||||
|
print(" - blacklist <name>: Register a blacklisted customer")
|
||||||
|
print(" - quit: Exit")
|
||||||
|
print()
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
user_input = input(" > ").strip()
|
||||||
|
|
||||||
|
if user_input.lower() in ['quit', 'exit', 'q']:
|
||||||
|
break
|
||||||
|
|
||||||
|
if not user_input:
|
||||||
|
print(" Please enter a command.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
parts = user_input.split(maxsplit=1)
|
||||||
|
command = parts[0].lower()
|
||||||
|
|
||||||
|
if command == "register" and len(parts) == 2:
|
||||||
|
customer_name = parts[1].strip()
|
||||||
|
result = client.register_customer(customer_name, False)
|
||||||
|
print(f" Result: {result}")
|
||||||
|
print()
|
||||||
|
elif command == "blacklist" and len(parts) == 2:
|
||||||
|
customer_name = parts[1].strip()
|
||||||
|
result = client.register_customer(customer_name, True)
|
||||||
|
print(f" Result: {result}")
|
||||||
|
print()
|
||||||
|
else:
|
||||||
|
print(" Invalid command. Use: register <name> or blacklist <name>")
|
||||||
|
print()
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n Interrupted by user")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 70)
|
||||||
|
print("Test completed!")
|
||||||
|
print("=" * 70)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user