mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
Introduce comprehensive documentation for the new `lora-manager-e2e` skill, which provides end-to-end testing workflows for LoRa Manager. The skill enables automated validation of standalone mode, including server management, UI interaction via Chrome DevTools MCP, and frontend-to-backend integration testing. Key additions: - Detailed skill description and prerequisites - Quick start workflow for server setup and browser debugging - Common E2E test patterns for page load verification, server restart, and API testing - Example test flows demonstrating step-by-step validation procedures - Scripts and MCP command examples for practical implementation This documentation supports automated testing of LoRa Manager's web interface and backend functionality, ensuring reliable end-to-end validation of features.
170 lines
4.7 KiB
Python
Executable File
170 lines
4.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Start or restart LoRa Manager standalone server for E2E testing.
|
|
"""
|
|
|
|
import argparse
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
import socket
|
|
import signal
|
|
import os
|
|
|
|
|
|
def find_server_process(port: int) -> list[int]:
|
|
"""Find PIDs of processes listening on the given port."""
|
|
try:
|
|
result = subprocess.run(
|
|
["lsof", "-ti", f":{port}"],
|
|
capture_output=True,
|
|
text=True,
|
|
check=False
|
|
)
|
|
if result.returncode == 0 and result.stdout.strip():
|
|
return [int(pid) for pid in result.stdout.strip().split("\n") if pid]
|
|
except FileNotFoundError:
|
|
# lsof not available, try netstat
|
|
try:
|
|
result = subprocess.run(
|
|
["netstat", "-tlnp"],
|
|
capture_output=True,
|
|
text=True,
|
|
check=False
|
|
)
|
|
pids = []
|
|
for line in result.stdout.split("\n"):
|
|
if f":{port}" in line:
|
|
parts = line.split()
|
|
for part in parts:
|
|
if "/" in part:
|
|
try:
|
|
pid = int(part.split("/")[0])
|
|
pids.append(pid)
|
|
except ValueError:
|
|
pass
|
|
return pids
|
|
except FileNotFoundError:
|
|
pass
|
|
return []
|
|
|
|
|
|
def kill_server(port: int) -> None:
|
|
"""Kill processes using the specified port."""
|
|
pids = find_server_process(port)
|
|
for pid in pids:
|
|
try:
|
|
os.kill(pid, signal.SIGTERM)
|
|
print(f"Sent SIGTERM to process {pid}")
|
|
except ProcessLookupError:
|
|
pass
|
|
|
|
# Wait for processes to terminate
|
|
time.sleep(1)
|
|
|
|
# Force kill if still running
|
|
pids = find_server_process(port)
|
|
for pid in pids:
|
|
try:
|
|
os.kill(pid, signal.SIGKILL)
|
|
print(f"Sent SIGKILL to process {pid}")
|
|
except ProcessLookupError:
|
|
pass
|
|
|
|
|
|
def is_server_ready(port: int, timeout: float = 0.5) -> bool:
|
|
"""Check if server is accepting connections."""
|
|
try:
|
|
with socket.create_connection(("127.0.0.1", port), timeout=timeout):
|
|
return True
|
|
except (socket.timeout, ConnectionRefusedError, OSError):
|
|
return False
|
|
|
|
|
|
def wait_for_server(port: int, timeout: int = 30) -> bool:
|
|
"""Wait for server to become ready."""
|
|
start = time.time()
|
|
while time.time() - start < timeout:
|
|
if is_server_ready(port):
|
|
return True
|
|
time.sleep(0.5)
|
|
return False
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(
|
|
description="Start LoRa Manager standalone server for E2E testing"
|
|
)
|
|
parser.add_argument(
|
|
"--port",
|
|
type=int,
|
|
default=8188,
|
|
help="Server port (default: 8188)"
|
|
)
|
|
parser.add_argument(
|
|
"--restart",
|
|
action="store_true",
|
|
help="Kill existing server before starting"
|
|
)
|
|
parser.add_argument(
|
|
"--wait",
|
|
action="store_true",
|
|
help="Wait for server to be ready before exiting"
|
|
)
|
|
parser.add_argument(
|
|
"--timeout",
|
|
type=int,
|
|
default=30,
|
|
help="Timeout for waiting (default: 30)"
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Get project root (parent of .agents directory)
|
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
skill_dir = os.path.dirname(script_dir)
|
|
project_root = os.path.dirname(os.path.dirname(os.path.dirname(skill_dir)))
|
|
|
|
# Restart if requested
|
|
if args.restart:
|
|
print(f"Killing existing server on port {args.port}...")
|
|
kill_server(args.port)
|
|
time.sleep(1)
|
|
|
|
# Check if already running
|
|
if is_server_ready(args.port):
|
|
print(f"Server already running on port {args.port}")
|
|
return 0
|
|
|
|
# Start server
|
|
print(f"Starting LoRa Manager standalone server on port {args.port}...")
|
|
cmd = [sys.executable, "standalone.py", "--port", str(args.port)]
|
|
|
|
# Start in background
|
|
process = subprocess.Popen(
|
|
cmd,
|
|
cwd=project_root,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
start_new_session=True
|
|
)
|
|
|
|
print(f"Server process started with PID {process.pid}")
|
|
|
|
# Wait for ready if requested
|
|
if args.wait:
|
|
print(f"Waiting for server to be ready (timeout: {args.timeout}s)...")
|
|
if wait_for_server(args.port, args.timeout):
|
|
print(f"Server ready at http://127.0.0.1:{args.port}/loras")
|
|
return 0
|
|
else:
|
|
print(f"Timeout waiting for server")
|
|
return 1
|
|
|
|
print(f"Server starting at http://127.0.0.1:{args.port}/loras")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|