add unified test scripts for all services

This commit is contained in:
2025-12-05 11:41:36 +01:00
parent 2fe315b7df
commit 1f949579b3
6 changed files with 1526 additions and 3 deletions

View File

@ -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
@ -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 <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
### Service Not Available

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

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