280 lines
11 KiB
Python
280 lines
11 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Test script for the ESP32 Modbus REST API /system/logs and /system/log-level endpoints.
|
||
"""
|
||
|
||
import requests
|
||
import json
|
||
import argparse
|
||
import sys
|
||
from colorama import init, Fore, Style
|
||
|
||
# Initialize colorama
|
||
init()
|
||
|
||
# Valid log levels
|
||
VALID_LOG_LEVELS = ["none", "error", "warning", "notice", "trace", "verbose"]
|
||
|
||
def print_success(message):
|
||
print(f"{Fore.GREEN}✓ SUCCESS: {message}{Style.RESET_ALL}")
|
||
|
||
def print_fail(message, error=None):
|
||
print(f"{Fore.RED}✗ FAIL: {message}{Style.RESET_ALL}")
|
||
if error:
|
||
print(f" {Fore.YELLOW}Error: {error}{Style.RESET_ALL}")
|
||
|
||
def print_info(message):
|
||
print(f"{Fore.BLUE}ℹ INFO: {message}{Style.RESET_ALL}")
|
||
|
||
def print_response(response):
|
||
try:
|
||
# Attempt to pretty-print JSON if possible
|
||
data = response.json()
|
||
if isinstance(data, list):
|
||
print(f"{Fore.CYAN}Response (JSON Array, {len(data)} items):{Style.RESET_ALL}")
|
||
# Print first few and last few lines if too long
|
||
limit = 15
|
||
if len(data) > 2 * limit:
|
||
for i in range(limit):
|
||
print(f" {data[i]}")
|
||
print(f" ... ({len(data) - 2 * limit} more lines) ...")
|
||
for i in range(len(data) - limit, len(data)):
|
||
print(f" {data[i]}")
|
||
else:
|
||
for line in data:
|
||
print(f" {line}")
|
||
else:
|
||
formatted_json = json.dumps(data, indent=2)
|
||
print(f"{Fore.CYAN}Response (JSON Object): {formatted_json}{Style.RESET_ALL}")
|
||
|
||
except Exception as e:
|
||
print(f"{Fore.CYAN}Response (raw): {response.text[:500]}...{Style.RESET_ALL}") # Limit raw output
|
||
print(f"{Fore.YELLOW} Could not parse JSON: {e}{Style.RESET_ALL}")
|
||
|
||
def test_get_logs(base_url):
|
||
api_url = f"{base_url}/api/v1/system/logs"
|
||
fail_count = 0
|
||
|
||
# Test GET logs without level parameter
|
||
print_info(f"Testing GET {api_url}")
|
||
try:
|
||
response = requests.get(api_url, timeout=10)
|
||
print_response(response)
|
||
|
||
if response.status_code == 200:
|
||
try:
|
||
data = response.json()
|
||
if isinstance(data, list):
|
||
print_success(f"Logs endpoint returned a list with {len(data)} items.")
|
||
# Optional: Check if items are strings
|
||
if data and not isinstance(data[0], str):
|
||
print_fail("Log items do not appear to be strings.")
|
||
fail_count += 1
|
||
else:
|
||
print_fail("Response is not a JSON list as expected.")
|
||
fail_count += 1
|
||
|
||
except json.JSONDecodeError as e:
|
||
print_fail("Response is not valid JSON.", str(e))
|
||
fail_count += 1
|
||
except Exception as e:
|
||
print_fail("Error processing JSON response.", str(e))
|
||
fail_count += 1
|
||
else:
|
||
print_fail(f"Endpoint returned status code {response.status_code}")
|
||
fail_count += 1
|
||
|
||
except requests.exceptions.RequestException as e:
|
||
print_fail("Failed to connect to the endpoint.", str(e))
|
||
fail_count += 1
|
||
except Exception as e:
|
||
print_fail("An unexpected error occurred.", str(e))
|
||
fail_count += 1
|
||
|
||
# Test GET logs with level parameter
|
||
print_info(f"Testing GET {api_url} with level parameter")
|
||
for level in VALID_LOG_LEVELS:
|
||
print_info(f"Testing GET {api_url}?level={level}")
|
||
try:
|
||
response = requests.get(f"{api_url}?level={level}", timeout=10)
|
||
print_response(response)
|
||
|
||
if response.status_code == 200:
|
||
try:
|
||
data = response.json()
|
||
if isinstance(data, list):
|
||
print_success(f"Logs endpoint with level={level} returned a list with {len(data)} items.")
|
||
# Optional: Check if items are strings
|
||
if data and not isinstance(data[0], str):
|
||
print_fail("Log items do not appear to be strings.")
|
||
fail_count += 1
|
||
else:
|
||
print_fail("Response is not a JSON list as expected.")
|
||
fail_count += 1
|
||
|
||
except json.JSONDecodeError as e:
|
||
print_fail("Response is not valid JSON.", str(e))
|
||
fail_count += 1
|
||
except Exception as e:
|
||
print_fail("Error processing JSON response.", str(e))
|
||
fail_count += 1
|
||
else:
|
||
print_fail(f"Endpoint returned status code {response.status_code}")
|
||
fail_count += 1
|
||
|
||
except requests.exceptions.RequestException as e:
|
||
print_fail("Failed to connect to the endpoint.", str(e))
|
||
fail_count += 1
|
||
except Exception as e:
|
||
print_fail("An unexpected error occurred.", str(e))
|
||
fail_count += 1
|
||
|
||
# Test GET logs with invalid level parameter
|
||
print_info(f"Testing GET {api_url} with invalid level parameter")
|
||
try:
|
||
response = requests.get(f"{api_url}?level=invalid", timeout=10)
|
||
print_response(response)
|
||
|
||
if response.status_code == 400:
|
||
print_success("Server correctly rejected invalid log level")
|
||
else:
|
||
print_fail(f"Server did not reject invalid log level (got {response.status_code}, expected 400)")
|
||
fail_count += 1
|
||
|
||
except requests.exceptions.RequestException as e:
|
||
print_fail("Failed to connect to the endpoint.", str(e))
|
||
fail_count += 1
|
||
except Exception as e:
|
||
print_fail("An unexpected error occurred.", str(e))
|
||
fail_count += 1
|
||
|
||
return fail_count
|
||
|
||
def test_log_level(base_url):
|
||
api_url = f"{base_url}/api/v1/system/log-level"
|
||
fail_count = 0
|
||
|
||
# Test GET log level
|
||
print_info(f"Testing GET {api_url}")
|
||
try:
|
||
response = requests.get(api_url, timeout=10)
|
||
print_response(response)
|
||
|
||
if response.status_code == 200:
|
||
try:
|
||
data = response.json()
|
||
if "level" in data and data["level"] in VALID_LOG_LEVELS:
|
||
print_success(f"Current log level is '{data['level']}'")
|
||
initial_level = data["level"]
|
||
else:
|
||
print_fail("Response missing 'level' field or invalid level value")
|
||
fail_count += 1
|
||
except json.JSONDecodeError as e:
|
||
print_fail("Response is not valid JSON.", str(e))
|
||
fail_count += 1
|
||
else:
|
||
print_fail(f"GET endpoint returned status code {response.status_code}")
|
||
fail_count += 1
|
||
|
||
# Test PUT log level - try each valid level
|
||
print_info(f"Testing GET {api_url} to set different levels")
|
||
for level in VALID_LOG_LEVELS:
|
||
print_info(f"Setting log level to '{level}'")
|
||
try:
|
||
response = requests.get(f"{api_url}?level={level}", timeout=10)
|
||
print_response(response)
|
||
|
||
if response.status_code == 200:
|
||
try:
|
||
data = response.json()
|
||
if "success" in data and data["success"] and "level" in data and data["level"] == level:
|
||
print_success(f"Successfully set log level to '{level}'")
|
||
else:
|
||
print_fail(f"Failed to set log level to '{level}'")
|
||
fail_count += 1
|
||
except json.JSONDecodeError as e:
|
||
print_fail("Response is not valid JSON.", str(e))
|
||
fail_count += 1
|
||
else:
|
||
print_fail(f"GET endpoint returned status code {response.status_code}")
|
||
fail_count += 1
|
||
|
||
except requests.exceptions.RequestException as e:
|
||
print_fail(f"Failed to set log level to '{level}'.", str(e))
|
||
fail_count += 1
|
||
|
||
# Test invalid log level
|
||
print_info("Testing GET with invalid log level")
|
||
try:
|
||
response = requests.get(f"{api_url}?level=invalid", timeout=10)
|
||
print_response(response)
|
||
|
||
if response.status_code == 400:
|
||
print_success("Server correctly rejected invalid log level")
|
||
else:
|
||
print_fail(f"Server did not reject invalid log level (got {response.status_code}, expected 400)")
|
||
fail_count += 1
|
||
except requests.exceptions.RequestException as e:
|
||
print_fail("Failed to test invalid log level.", str(e))
|
||
fail_count += 1
|
||
|
||
# Restore initial log level
|
||
print_info(f"Restoring initial log level '{initial_level}'")
|
||
try:
|
||
response = requests.get(f"{api_url}?level={initial_level}", timeout=10)
|
||
if response.status_code == 200:
|
||
print_success(f"Restored log level to '{initial_level}'")
|
||
else:
|
||
print_fail(f"Failed to restore initial log level")
|
||
fail_count += 1
|
||
except requests.exceptions.RequestException as e:
|
||
print_fail("Failed to restore initial log level.", str(e))
|
||
fail_count += 1
|
||
|
||
except requests.exceptions.RequestException as e:
|
||
print_fail("Failed to connect to the endpoint.", str(e))
|
||
fail_count += 1
|
||
except Exception as e:
|
||
print_fail("An unexpected error occurred.", str(e))
|
||
fail_count += 1
|
||
|
||
return fail_count
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(description='Test the ESP32 Modbus REST API /system/logs and /system/log-level endpoints')
|
||
parser.add_argument('--host', type=str, default='modbus-esp32.local',
|
||
help='Hostname or IP address of the ESP32 device (default: modbus-esp32.local)')
|
||
parser.add_argument('--port', type=int, default=80,
|
||
help='Port number (default: 80)')
|
||
parser.add_argument('--protocol', type=str, default='http',
|
||
choices=['http', 'https'],
|
||
help='Protocol to use (default: http)')
|
||
|
||
args = parser.parse_args()
|
||
|
||
base_url = f"{args.protocol}://{args.host}"
|
||
if args.port != 80:
|
||
base_url += f":{args.port}"
|
||
|
||
fail_count = 0
|
||
|
||
# Test logs endpoint
|
||
print("-" * 80)
|
||
print_info("Testing system logs endpoint")
|
||
fail_count += test_get_logs(base_url)
|
||
|
||
# Test log level endpoint
|
||
print("-" * 80)
|
||
print_info("Testing log level endpoint")
|
||
fail_count += test_log_level(base_url)
|
||
|
||
print("-" * 80)
|
||
if fail_count == 0:
|
||
print(f"{Fore.GREEN}✓ All tests passed!{Style.RESET_ALL}")
|
||
else:
|
||
print(f"{Fore.RED}✗ {fail_count} check(s) failed.{Style.RESET_ALL}")
|
||
|
||
sys.exit(fail_count)
|
||
|
||
if __name__ == "__main__":
|
||
main() |