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()