diff --git a/.gitignore b/.gitignore
index 646b893..fa0cb01 100644
--- a/.gitignore
+++ b/.gitignore
@@ -69,6 +69,12 @@ Desktop.ini
logs/
*.log
+# SQLite Database
+*.db
+*.db-journal
+*.sqlite
+*.sqlite3
+
# Python
__pycache__/
*.py[cod]
diff --git a/apidoc.md b/apidoc.md
new file mode 100644
index 0000000..0278de6
--- /dev/null
+++ b/apidoc.md
@@ -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
+
+
+
+
+
+ John
+
+
+
+```
+
+**SOAP Response Example:**
+```xml
+
+
+
+
+ Hello World, John!
+
+
+
+```
+
+---
+
+## 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
+
+
+
+
+
+
+ John Doe
+ false
+
+
+
+
+```
+
+**SOAP Response Example (Success):**
+```xml
+
+
+
+
+ Registration Successful
+
+
+
+```
+
+**SOAP Response Example (Duplicate):**
+```xml
+
+
+
+
+ Error: Customer already exists
+
+
+
+```
+
+**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
+
+
+
+
+
+
+ John Doe
+ 50000
+ 720
+
+
+
+
+```
+
+**SOAP Response Example (Approved):**
+```xml
+
+
+
+
+
+ true
+ 3.5
+
+ Loan approved with excellent rate
+
+
+
+
+```
+
+**SOAP Response Example (Rejected - Blacklisted):**
+```xml
+
+
+
+
+
+ false
+ 0.0
+ Applicant is blacklisted
+ Loan application rejected
+
+
+
+
+```
+
+**SOAP Response Example (Rejected - Low Credit):**
+```xml
+
+
+
+
+
+ false
+ 0.0
+ Credit score too low
+ Loan application rejected
+
+
+
+
+```
+
+**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
+
+
+
+
+ S:Server
+ Customer name cannot be null or empty
+
+
+
+```
+
+### 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 '
+
+
+
+
+ Test
+
+
+'
+```
+
+#### Register Customer
+```bash
+curl -X POST http://localhost:8080/jaxws-hello-world/loan \
+ -H "Content-Type: text/xml; charset=utf-8" \
+ -H "SOAPAction: " \
+ -d '
+
+
+
+
+
+ John Doe
+ false
+
+
+
+'
+```
+
+#### 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 '
+
+
+
+
+
+ John Doe
+ 50000
+ 720
+
+
+
+'
+```
+
+### 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
diff --git a/docker-compose.yml b/docker-compose.yml
index 62630ed..1e82c4c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -14,6 +14,8 @@ services:
volumes:
# Optional: Mount logs directory for debugging
- ./logs:/usr/local/tomcat/logs
+ # Mount database file to root folder
+ - ./loan_app.db:/usr/local/tomcat/loan_app.db
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/jaxws-hello-world/services/hello?wsdl"]
diff --git a/scripts/README.md b/scripts/README.md
index 46a50b7..275b33e 100644
--- a/scripts/README.md
+++ b/scripts/README.md
@@ -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 ` - Test Hello World service
+- `register ` - Register a regular customer
+- `blacklist ` - Register a blacklisted customer
+- `loan ` - 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
@@ -105,6 +232,114 @@ response = client.call_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 : Register a regular customer
+ - blacklist : 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
### Service Not Available
diff --git a/scripts/test_all_services.py b/scripts/test_all_services.py
new file mode 100644
index 0000000..db1549e
--- /dev/null
+++ b/scripts/test_all_services.py
@@ -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"""
+
+
+
+
+ {name}
+
+
+"""
+
+ 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"""
+
+
+
+
+
+ {customer_name}
+ {blacklisted_value}
+
+
+
+"""
+
+ 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"""
+
+
+
+
+
+ {applicant_name}
+ {requested_amount}
+ {credit_score}
+
+
+
+"""
+
+ 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 - Test Hello World service")
+ print(" register - Register regular customer")
+ print(" blacklist - Register blacklisted customer")
+ print(" loan - 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())
diff --git a/scripts/test_register_customer.py b/scripts/test_register_customer.py
new file mode 100644
index 0000000..1889037
--- /dev/null
+++ b/scripts/test_register_customer.py
@@ -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"""
+
+
+
+
+
+ {customer_name}
+ {blacklisted_value}
+
+
+
+"""
+ 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 : Register a regular customer")
+ print(" - blacklist : 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 or blacklist ")
+ print()
+
+ except KeyboardInterrupt:
+ print("\n Interrupted by user")
+
+ print()
+ print("=" * 70)
+ print("Test completed!")
+ print("=" * 70)
+
+
+if __name__ == "__main__":
+ main()