firmware-base/scripts/test_logs_api.py

280 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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()