Add: test scripts (sync_sessions, test_all_pages, test_e2e_agent_browser)
This commit is contained in:
835
scripts/test_e2e_agent_browser.py
Executable file
835
scripts/test_e2e_agent_browser.py
Executable file
@@ -0,0 +1,835 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
End-to-End Test Script for agent-base Django Project
|
||||
Uses agent-browser CLI for browser automation
|
||||
|
||||
Usage: python3 test_e2e_agent_browser.py
|
||||
|
||||
Environment Variables:
|
||||
AGENT_BROWSER_EXECUTABLE_PATH: Path to Chrome/Chromium binary
|
||||
(Defaults to /Applications/Google Chrome.app/Contents/MacOS/Google Chrome on macOS)
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
import re
|
||||
import uuid
|
||||
import platform
|
||||
from datetime import datetime
|
||||
|
||||
# Detect agent-browser path based on OS
|
||||
def get_agent_browser_path():
|
||||
"""Get agent-browser executable path based on the system."""
|
||||
env_path = os.environ.get("AGENT_BROWSER_PATH")
|
||||
if env_path and os.path.exists(env_path):
|
||||
return env_path
|
||||
|
||||
system = platform.system()
|
||||
if system == "Darwin":
|
||||
path = "/opt/homebrew/bin/agent-browser"
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
elif system == "Linux":
|
||||
for path in [
|
||||
"/home/shenwei/.npm-global/bin/agent-browser",
|
||||
"/usr/local/bin/agent-browser",
|
||||
"/usr/bin/agent-browser",
|
||||
]:
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
|
||||
for cmd in ["agent-browser"]:
|
||||
try:
|
||||
result = subprocess.run(["which", cmd], capture_output=True, text=True, timeout=5)
|
||||
if result.returncode == 0 and result.stdout.strip():
|
||||
return result.stdout.strip()
|
||||
except:
|
||||
pass
|
||||
|
||||
return "agent-browser"
|
||||
|
||||
def get_chrome_path():
|
||||
"""Get Chrome executable path based on the system."""
|
||||
if os.environ.get("AGENT_BROWSER_EXECUTABLE_PATH"):
|
||||
return os.environ["AGENT_BROWSER_EXECUTABLE_PATH"]
|
||||
|
||||
system = platform.system()
|
||||
if system == "Darwin":
|
||||
mac_chrome = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
if os.path.exists(mac_chrome):
|
||||
return mac_chrome
|
||||
elif system == "Linux":
|
||||
for path in [
|
||||
"/usr/bin/google-chrome",
|
||||
"/usr/bin/chromium-browser",
|
||||
"/usr/bin/chromium",
|
||||
"/snap/bin/chromium",
|
||||
os.path.expanduser("~/.local/bin/chromium"),
|
||||
]:
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
|
||||
return ""
|
||||
|
||||
# Initialize paths
|
||||
AGENT_BROWSER = get_agent_browser_path()
|
||||
CHROME_PATH = get_chrome_path()
|
||||
|
||||
if CHROME_PATH:
|
||||
os.environ["AGENT_BROWSER_EXECUTABLE_PATH"] = CHROME_PATH
|
||||
|
||||
print(f"Using agent-browser: {AGENT_BROWSER}", file=sys.stderr)
|
||||
if CHROME_PATH:
|
||||
print(f"Using Chrome: {CHROME_PATH}", file=sys.stderr)
|
||||
else:
|
||||
print("Chrome: auto-detect (ensure agent-browser install completed)", file=sys.stderr)
|
||||
|
||||
# Configuration
|
||||
BASE_URL = "http://192.168.3.45:8765"
|
||||
ADMIN_URL = f"{BASE_URL}/admin/"
|
||||
USERNAME = "admin"
|
||||
PASSWORD = "admin123"
|
||||
SESSION_NAME = "agent-base-e2e-test"
|
||||
STATE_FILE = "/tmp/agent-browser-auth-state.json"
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) if os.path.abspath(__file__).startswith('/tmp') else "/home/shenwei/docker/agent-base/scripts"
|
||||
PROJECT_DIR = "/home/shenwei/docker/agent-base"
|
||||
|
||||
# Test results tracking
|
||||
test_results = {
|
||||
"started_at": datetime.now().isoformat(),
|
||||
"tests": [],
|
||||
"passed": 0,
|
||||
"failed": 0,
|
||||
"errors": []
|
||||
}
|
||||
|
||||
|
||||
def log(msg):
|
||||
"""Print log message with timestamp"""
|
||||
ts = datetime.now().strftime("%H:%M:%S")
|
||||
print(f"[{ts}] {msg}", flush=True)
|
||||
|
||||
|
||||
def log_step(step_num, msg):
|
||||
"""Print step with number"""
|
||||
log(f"[Step {step_num}] {msg}")
|
||||
|
||||
|
||||
def run_agent_browser(args, timeout=30, check=True):
|
||||
"""
|
||||
Run agent-browser command and return JSON result.
|
||||
|
||||
Args:
|
||||
args: List of command arguments (e.g., ['open', 'https://example.com'])
|
||||
timeout: Command timeout in seconds
|
||||
check: Whether to raise exception on non-zero exit
|
||||
|
||||
Returns:
|
||||
tuple: (success: bool, result: dict or error message)
|
||||
"""
|
||||
cmd = [AGENT_BROWSER, "--session", SESSION_NAME, "--json"]
|
||||
cmd.extend(args)
|
||||
|
||||
log(f" Running: agent-browser {' '.join(args)}")
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout
|
||||
)
|
||||
|
||||
if result.stdout:
|
||||
try:
|
||||
return True, json.loads(result.stdout)
|
||||
except json.JSONDecodeError:
|
||||
return True, {"raw": result.stdout}
|
||||
|
||||
if result.returncode != 0 and check:
|
||||
log(f" ERROR: exit code {result.returncode}")
|
||||
log(f" stderr: {result.stderr[:500]}")
|
||||
return False, result.stderr
|
||||
|
||||
return result.returncode == 0, {}
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
log(f" TIMEOUT after {timeout}s")
|
||||
return False, "Command timeout"
|
||||
except Exception as e:
|
||||
log(f" EXCEPTION: {e}")
|
||||
return False, str(e)
|
||||
|
||||
|
||||
def wait_for_page_load(timeout=10):
|
||||
"""Wait for page to finish loading"""
|
||||
time.sleep(2) # Give page time to settle
|
||||
|
||||
|
||||
def get_interactive_elements():
|
||||
"""Get snapshot of interactive elements from current page"""
|
||||
success, result = run_agent_browser(["snapshot", "-i", "--json"], timeout=15, check=False)
|
||||
if success and "data" in result:
|
||||
return result["data"].get("refs", {})
|
||||
return {}
|
||||
|
||||
|
||||
def find_element_by_role(elements, role_pattern, name_pattern=None):
|
||||
"""Find element by role pattern, optionally with name pattern"""
|
||||
for ref, info in elements.items():
|
||||
role = info.get("role", "")
|
||||
name = info.get("name", "")
|
||||
if re.match(role_pattern, role, re.IGNORECASE):
|
||||
if name_pattern is None or re.search(name_pattern, name, re.IGNORECASE):
|
||||
return ref
|
||||
return None
|
||||
|
||||
|
||||
def find_element_by_text(elements, text_pattern, role_pattern=None):
|
||||
"""Find element containing text"""
|
||||
for ref, info in elements.items():
|
||||
role = info.get("role", "")
|
||||
name = info.get("name", "")
|
||||
if re.search(text_pattern, name, re.IGNORECASE):
|
||||
if role_pattern is None or re.match(role_pattern, role, re.IGNORECASE):
|
||||
return ref
|
||||
return None
|
||||
|
||||
|
||||
def record_test(name, passed, error=None, details=None):
|
||||
"""Record test result"""
|
||||
test_entry = {
|
||||
"name": name,
|
||||
"passed": passed,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
if error:
|
||||
test_entry["error"] = error
|
||||
if details:
|
||||
test_entry["details"] = details
|
||||
|
||||
test_results["tests"].append(test_entry)
|
||||
|
||||
if passed:
|
||||
test_results["passed"] += 1
|
||||
log(f" ✓ PASSED: {name}")
|
||||
else:
|
||||
test_results["failed"] += 1
|
||||
test_results["errors"].append(f"{name}: {error}")
|
||||
log(f" ✗ FAILED: {name} - {error}")
|
||||
|
||||
|
||||
def test_admin_login():
|
||||
"""Test 1: Admin Login"""
|
||||
log_step(1, "Testing Admin Login")
|
||||
|
||||
# Navigate to admin login
|
||||
success, _ = run_agent_browser(["open", f"{ADMIN_URL}login/?next=/admin/"])
|
||||
if not success:
|
||||
record_test("Admin Login - Navigate", False, "Failed to open login page")
|
||||
return False
|
||||
|
||||
wait_for_page_load()
|
||||
|
||||
# Get interactive elements
|
||||
elements = get_interactive_elements()
|
||||
if not elements:
|
||||
record_test("Admin Login - Get Elements", False, "No interactive elements found")
|
||||
return False
|
||||
|
||||
# Find username field
|
||||
username_ref = find_element_by_role(elements, "textbox", ".*user.*") or \
|
||||
find_element_by_text(elements, "user", "textbox") or \
|
||||
find_element_by_role(elements, "textbox")
|
||||
|
||||
if not username_ref:
|
||||
# Try to find by id
|
||||
for ref, info in elements.items():
|
||||
if "username" in info.get("name", "").lower() or "id_username" in str(info):
|
||||
username_ref = ref
|
||||
break
|
||||
|
||||
if not username_ref:
|
||||
# List all textboxes for debugging
|
||||
textboxes = {k: v for k, v in elements.items() if "textbox" in v.get("role", "").lower()}
|
||||
log(f" Available textboxes: {textboxes}")
|
||||
record_test("Admin Login - Find Username Field", False, "Could not find username field")
|
||||
return False
|
||||
|
||||
# Fill username
|
||||
success, _ = run_agent_browser(["fill", username_ref, USERNAME])
|
||||
if not success:
|
||||
record_test("Admin Login - Fill Username", False, "Failed to fill username")
|
||||
return False
|
||||
|
||||
# Find password field (role=textbox with 密码 in name on Django admin)
|
||||
password_ref = find_element_by_role(elements, "password")
|
||||
if not password_ref:
|
||||
# Try by name - Django admin uses textbox role with "密码:" label
|
||||
for ref, info in elements.items():
|
||||
if "密码" in info.get("name", "") or "password" in info.get("name", "").lower():
|
||||
password_ref = ref
|
||||
break
|
||||
|
||||
if not password_ref:
|
||||
record_test("Admin Login - Find Password Field", False, "Could not find password field")
|
||||
return False
|
||||
|
||||
# Fill password
|
||||
success, _ = run_agent_browser(["fill", password_ref, PASSWORD])
|
||||
if not success:
|
||||
record_test("Admin Login - Fill Password", False, "Failed to fill password")
|
||||
return False
|
||||
|
||||
# Find and click submit button
|
||||
submit_ref = find_element_by_text(elements, "log|signin|submit|登|登录", "button") or \
|
||||
find_element_by_role(elements, "button", ".*")
|
||||
|
||||
if not submit_ref:
|
||||
record_test("Admin Login - Find Submit Button", False, "Could not find submit button")
|
||||
return False
|
||||
|
||||
success, _ = run_agent_browser(["click", submit_ref])
|
||||
if not success:
|
||||
record_test("Admin Login - Click Submit", False, "Failed to click submit")
|
||||
return False
|
||||
|
||||
wait_for_page_load()
|
||||
|
||||
# Check if login was successful (should be at /admin/ now)
|
||||
success, result = run_agent_browser(["get", "url", "--json"], check=False)
|
||||
current_url = ""
|
||||
if success and result.get("data"):
|
||||
current_url = result["data"].get("url", "")
|
||||
|
||||
log(f" Current URL after login: {current_url}")
|
||||
|
||||
if "/admin/" in current_url and "login" not in current_url.lower():
|
||||
record_test("Admin Login", True, details=f"Logged in successfully, at {current_url}")
|
||||
|
||||
# Save auth state for subsequent tests
|
||||
success, _ = run_agent_browser(["state", "save", STATE_FILE], check=False)
|
||||
if success:
|
||||
log(" Auth state saved for subsequent tests")
|
||||
|
||||
return True
|
||||
else:
|
||||
# Check for error message
|
||||
elements = get_interactive_elements()
|
||||
error_text = find_element_by_text(elements, "error|invalid|错误|无效")
|
||||
|
||||
record_test("Admin Login", False, f"Login failed, URL: {current_url}, error_element: {error_text}")
|
||||
return False
|
||||
|
||||
|
||||
def test_session_management():
|
||||
"""Test 2: Session Management"""
|
||||
log_step(2, "Testing Session Management")
|
||||
|
||||
# Load saved auth state if available
|
||||
if os.path.exists(STATE_FILE):
|
||||
run_agent_browser(["state", "load", STATE_FILE], check=False)
|
||||
|
||||
# Navigate to session admin
|
||||
success, _ = run_agent_browser(["open", f"{ADMIN_URL}openclaw/session/"])
|
||||
if not success:
|
||||
record_test("Session Management - Navigate", False, "Failed to navigate to session admin")
|
||||
return False
|
||||
|
||||
wait_for_page_load()
|
||||
|
||||
# Get page title
|
||||
success, result = run_agent_browser(["get", "title", "--json"], check=False)
|
||||
title = ""
|
||||
if success and result.get("data"):
|
||||
title = result["data"].get("title", "")
|
||||
|
||||
log(f" Page title: {title}")
|
||||
|
||||
# Get elements
|
||||
elements = get_interactive_elements()
|
||||
|
||||
# Check for session list - Django admin uses columnheader/cell roles not <table>
|
||||
has_table = find_element_by_role(elements, "table") is not None
|
||||
has_columnheaders = find_element_by_role(elements, "columnheader") is not None
|
||||
has_cells = find_element_by_role(elements, "cell") is not None
|
||||
has_rowheaders = find_element_by_role(elements, "rowheader") is not None
|
||||
|
||||
if has_table or has_columnheaders or has_cells:
|
||||
record_test("Session Management - List View", True,
|
||||
details=f"Table:{has_table}, Headers:{has_columnheaders}, Cells:{has_cells}, RowHeaders:{has_rowheaders}")
|
||||
else:
|
||||
record_test("Session Management - List View", False, "No table/list found on session list page")
|
||||
return False
|
||||
|
||||
# Test search functionality
|
||||
search_ref = find_element_by_role(elements, "searchbox") or \
|
||||
find_element_by_text(elements, "search", "textbox") or \
|
||||
find_element_by_role(elements, "textbox")
|
||||
|
||||
if search_ref:
|
||||
success, _ = run_agent_browser(["fill", search_ref, "test"], check=False)
|
||||
if success:
|
||||
# Try pressing Enter to submit search
|
||||
run_agent_browser(["press", "Enter"], check=False)
|
||||
wait_for_page_load()
|
||||
record_test("Session Management - Search", True)
|
||||
else:
|
||||
record_test("Session Management - Search", False, "Search field found but fill failed")
|
||||
else:
|
||||
record_test("Session Management - Search", False, "No search field found")
|
||||
|
||||
# Test filter functionality
|
||||
elements = get_interactive_elements()
|
||||
filter_ref = find_element_by_role(elements, "combobox") or \
|
||||
find_element_by_text(elements, "filter|筛选", "button") or \
|
||||
find_element_by_text(elements, "all|全部", "button")
|
||||
|
||||
if filter_ref:
|
||||
record_test("Session Management - Filter", True, details="Filter controls found")
|
||||
else:
|
||||
record_test("Session Management - Filter", False, "No filter controls found")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def test_message_management():
|
||||
"""Test 3: Message Management"""
|
||||
log_step(3, "Testing Message Management")
|
||||
|
||||
# Load saved auth state
|
||||
if os.path.exists(STATE_FILE):
|
||||
run_agent_browser(["state", "load", STATE_FILE], check=False)
|
||||
|
||||
# Navigate to message admin
|
||||
success, _ = run_agent_browser(["open", f"{ADMIN_URL}openclaw/message/"])
|
||||
if not success:
|
||||
record_test("Message Management - Navigate", False, "Failed to navigate to message admin")
|
||||
return False
|
||||
|
||||
wait_for_page_load()
|
||||
|
||||
# Get page title
|
||||
success, result = run_agent_browser(["get", "title", "--json"], check=False)
|
||||
title = ""
|
||||
if success and result.get("data"):
|
||||
title = result["data"].get("title", "")
|
||||
|
||||
log(f" Page title: {title}")
|
||||
|
||||
# Get elements
|
||||
elements = get_interactive_elements()
|
||||
|
||||
# Check for message list - Django admin uses columnheader/cell roles
|
||||
has_table = find_element_by_role(elements, "table") is not None
|
||||
has_columnheaders = find_element_by_role(elements, "columnheader") is not None
|
||||
has_cells = find_element_by_role(elements, "cell") is not None
|
||||
has_rowheaders = find_element_by_role(elements, "rowheader") is not None
|
||||
|
||||
if has_table or has_columnheaders or has_cells:
|
||||
record_test("Message Management - List View", True,
|
||||
details=f"Table:{has_table}, Headers:{has_columnheaders}, Cells:{has_cells}, RowHeaders:{has_rowheaders}")
|
||||
else:
|
||||
record_test("Message Management - List View", False, "No list found on message list page")
|
||||
return False
|
||||
|
||||
# Test search functionality
|
||||
search_ref = find_element_by_role(elements, "searchbox") or \
|
||||
find_element_by_text(elements, "search", "textbox") or \
|
||||
find_element_by_role(elements, "textbox")
|
||||
|
||||
if search_ref:
|
||||
success, _ = run_agent_browser(["fill", search_ref, "hello"], check=False)
|
||||
if success:
|
||||
run_agent_browser(["press", "Enter"], check=False)
|
||||
wait_for_page_load()
|
||||
record_test("Message Management - Search", True)
|
||||
else:
|
||||
record_test("Message Management - Search", False, "Search fill failed")
|
||||
else:
|
||||
record_test("Message Management - Search", False, "No search field found")
|
||||
|
||||
# Test filter functionality
|
||||
elements = get_interactive_elements()
|
||||
filter_ref = find_element_by_role(elements, "combobox") or \
|
||||
find_element_by_text(elements, "filter|筛选", "button")
|
||||
|
||||
if filter_ref:
|
||||
record_test("Message Management - Filter", True)
|
||||
else:
|
||||
record_test("Message Management - Filter", False, "No filter controls found")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def test_toolcall_management():
|
||||
"""Test 4: ToolCall Management"""
|
||||
log_step(4, "Testing ToolCall Management")
|
||||
|
||||
# Load saved auth state
|
||||
if os.path.exists(STATE_FILE):
|
||||
run_agent_browser(["state", "load", STATE_FILE], check=False)
|
||||
|
||||
# Navigate to toolcall admin
|
||||
success, _ = run_agent_browser(["open", f"{ADMIN_URL}openclaw/toolcall/"])
|
||||
if not success:
|
||||
record_test("ToolCall Management - Navigate", False, "Failed to navigate to toolcall admin")
|
||||
return False
|
||||
|
||||
wait_for_page_load()
|
||||
|
||||
# Get page title
|
||||
success, result = run_agent_browser(["get", "title", "--json"], check=False)
|
||||
title = ""
|
||||
if success and result.get("data"):
|
||||
title = result["data"].get("title", "")
|
||||
|
||||
log(f" Page title: {title}")
|
||||
|
||||
# Get elements
|
||||
elements = get_interactive_elements()
|
||||
|
||||
# Check for toolcall list - Django admin uses columnheader/cell roles
|
||||
has_table = find_element_by_role(elements, "table") is not None
|
||||
has_columnheaders = find_element_by_role(elements, "columnheader") is not None
|
||||
has_cells = find_element_by_role(elements, "cell") is not None
|
||||
has_rowheaders = find_element_by_role(elements, "rowheader") is not None
|
||||
|
||||
if has_table or has_columnheaders or has_cells:
|
||||
record_test("ToolCall Management - List View", True,
|
||||
details=f"Table:{has_table}, Headers:{has_columnheaders}, Cells:{has_cells}, RowHeaders:{has_rowheaders}")
|
||||
else:
|
||||
record_test("ToolCall Management - List View", False, "No list found on toolcall list page")
|
||||
return False
|
||||
|
||||
# Test filter functionality
|
||||
filter_ref = find_element_by_role(elements, "combobox") or \
|
||||
find_element_by_text(elements, "filter|筛选", "button")
|
||||
|
||||
if filter_ref:
|
||||
record_test("ToolCall Management - Filter", True)
|
||||
else:
|
||||
record_test("ToolCall Management - Filter", False, "No filter controls found")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def test_daily_reports_list():
|
||||
"""Test 5: Daily Reports List"""
|
||||
log_step(5, "Testing Daily Reports List")
|
||||
|
||||
# Load saved auth state
|
||||
if os.path.exists(STATE_FILE):
|
||||
run_agent_browser(["state", "load", STATE_FILE], check=False)
|
||||
|
||||
# Navigate to daily reports
|
||||
success, _ = run_agent_browser(["open", f"{ADMIN_URL}daily-reports/"])
|
||||
if not success:
|
||||
record_test("Daily Reports - Navigate", False, "Failed to navigate to daily reports")
|
||||
return False
|
||||
|
||||
wait_for_page_load()
|
||||
|
||||
# Get page title
|
||||
success, result = run_agent_browser(["get", "title", "--json"], check=False)
|
||||
title = ""
|
||||
if success and result.get("data"):
|
||||
title = result["data"].get("title", "")
|
||||
|
||||
log(f" Page title: {title}")
|
||||
|
||||
# Get elements
|
||||
elements = get_interactive_elements()
|
||||
|
||||
# Check for reports list
|
||||
has_table = find_element_by_role(elements, "table") is not None
|
||||
has_columnheaders = find_element_by_role(elements, "columnheader") is not None
|
||||
has_cells = find_element_by_role(elements, "cell") is not None
|
||||
has_rowheaders = find_element_by_role(elements, "rowheader") is not None
|
||||
has_links = find_element_by_text(elements, "xingjiang|report|daily", "link") is not None
|
||||
|
||||
if has_table or has_columnheaders or has_cells or has_rowheaders or has_links:
|
||||
record_test("Daily Reports - List View", True,
|
||||
details=f"Table:{has_table}, Headers:{has_columnheaders}, Cells:{has_cells}, Links:{has_links}")
|
||||
else:
|
||||
record_test("Daily Reports - List View", False, "No reports found on page")
|
||||
return False
|
||||
|
||||
# Try to find report links (for xingjiang or date-based links)
|
||||
# The daily reports page may show dates or agent names as links
|
||||
report_link = find_element_by_text(elements, "xingjiang|2026|report", "link")
|
||||
|
||||
if report_link:
|
||||
record_test("Daily Reports - Report Links Present", True, details=f"Found report link: {report_link}")
|
||||
else:
|
||||
# Also check for any date/agent links
|
||||
any_link = find_element_by_role(elements, "link")
|
||||
if any_link:
|
||||
# Found some links, the page is working
|
||||
record_test("Daily Reports - Report Links Present", True, details=f"Page has links (link ref: {any_link})")
|
||||
else:
|
||||
record_test("Daily Reports - Report Links Present", False, "No links found on page")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def test_daily_reports_detail():
|
||||
"""Test 6: Daily Reports Detail"""
|
||||
log_step(6, "Testing Daily Reports Detail")
|
||||
|
||||
# Load saved auth state
|
||||
if os.path.exists(STATE_FILE):
|
||||
run_agent_browser(["state", "load", STATE_FILE], check=False)
|
||||
|
||||
# Navigate directly to a specific daily report
|
||||
detail_url = f"{ADMIN_URL}daily-reports/xingjiang/2026-4-8/"
|
||||
success, _ = run_agent_browser(["open", detail_url])
|
||||
if not success:
|
||||
record_test("Daily Reports Detail - Navigate", False, "Failed to navigate to daily report detail")
|
||||
return False
|
||||
|
||||
wait_for_page_load()
|
||||
|
||||
# Get page title
|
||||
success, result = run_agent_browser(["get", "title", "--json"], check=False)
|
||||
title = ""
|
||||
if success and result.get("data"):
|
||||
title = result["data"].get("title", "")
|
||||
|
||||
log(f" Page title: {title}")
|
||||
|
||||
# Check URL
|
||||
success, result = run_agent_browser(["get", "url", "--json"], check=False)
|
||||
current_url = ""
|
||||
if success and result.get("data"):
|
||||
current_url = result["data"].get("url", "")
|
||||
|
||||
log(f" Current URL: {current_url}")
|
||||
|
||||
if "daily-reports" in current_url and "xingjiang" in current_url:
|
||||
record_test("Daily Reports Detail - URL Valid", True, details=f"URL: {current_url}")
|
||||
else:
|
||||
record_test("Daily Reports Detail - URL Valid", False, f"Unexpected URL: {current_url}")
|
||||
return False
|
||||
|
||||
# Get elements
|
||||
elements = get_interactive_elements()
|
||||
|
||||
# Check for content
|
||||
has_heading = find_element_by_role(elements, "heading") is not None
|
||||
has_content = len(elements) > 5 # More than just navigation
|
||||
|
||||
if has_heading or has_content:
|
||||
record_test("Daily Reports Detail - Content Present", True)
|
||||
else:
|
||||
record_test("Daily Reports Detail - Content Present", False, "No content found on detail page")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def test_bulk_upsert_api():
|
||||
"""Test 7: Bulk Upsert API"""
|
||||
log_step(7, "Testing Bulk Upsert API")
|
||||
|
||||
# Load saved auth state for CSRF token
|
||||
if os.path.exists(STATE_FILE):
|
||||
run_agent_browser(["state", "load", STATE_FILE], check=False)
|
||||
|
||||
# First, get a CSRF token by visiting the API or any admin page
|
||||
success, _ = run_agent_browser(["open", BASE_URL], check=False)
|
||||
wait_for_page_load()
|
||||
|
||||
# Get cookies for CSRF
|
||||
success, result = run_agent_browser(["cookies"], check=False)
|
||||
csrf_token = ""
|
||||
cookies = ""
|
||||
if success and result.get("data"):
|
||||
cookies = result["data"]
|
||||
for cookie in cookies if isinstance(cookies, list) else []:
|
||||
if cookie.get("name") == "csrftoken":
|
||||
csrf_token = cookie.get("value", "")
|
||||
break
|
||||
|
||||
log(f" CSRF token obtained: {'yes' if csrf_token else 'no (will try without)'}")
|
||||
|
||||
# Test the API with curl (more reliable for API testing)
|
||||
# Use unique session_id to avoid unique constraint conflicts
|
||||
unique_session_id = f"test-e2e-{uuid.uuid4().hex[:12]}"
|
||||
test_payload = {
|
||||
"agent_name": "xingjiang",
|
||||
"source_node": "telegram",
|
||||
"sessions": [
|
||||
{
|
||||
"session_id": unique_session_id,
|
||||
"start_time": "2026-04-08T12:00:00Z",
|
||||
"end_time": "2026-04-08T13:00:00Z",
|
||||
"message_count": 10,
|
||||
"tool_call_count": 5,
|
||||
"total_tokens": 1000,
|
||||
"total_cost": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# Build curl command
|
||||
curl_cmd = [
|
||||
"curl", "-s", "-X", "POST",
|
||||
f"{BASE_URL}/api/sessions/bulk_upsert/",
|
||||
"-H", "Content-Type: application/json",
|
||||
"-d", json.dumps(test_payload)
|
||||
]
|
||||
|
||||
if csrf_token:
|
||||
curl_cmd.extend(["-H", f"X-CSRFToken: {csrf_token}"])
|
||||
|
||||
log(f" Testing API: POST /api/sessions/bulk_upsert/")
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
curl_cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=15
|
||||
)
|
||||
|
||||
response = result.stdout
|
||||
log(f" API Response: {response[:500]}")
|
||||
|
||||
# Try to parse response
|
||||
try:
|
||||
resp_data = json.loads(response)
|
||||
if resp_data.get("success") or "created" in str(resp_data).lower() or "upserted" in str(resp_data).lower():
|
||||
record_test("Bulk Upsert API - Create Session", True, details=f"Response: {response[:200]}")
|
||||
elif resp_data.get("error"):
|
||||
# Some errors are expected (like missing fields)
|
||||
if "missing" in resp_data.get("error", "").lower():
|
||||
record_test("Bulk Upsert API - Validation", True, details=f"Expected validation: {resp_data.get('error')}")
|
||||
else:
|
||||
record_test("Bulk Upsert API - Response", True, details=f"Response: {response[:200]}")
|
||||
else:
|
||||
record_test("Bulk Upsert API - Response", True, details=f"Response: {response[:200]}")
|
||||
except json.JSONDecodeError:
|
||||
if result.returncode == 0:
|
||||
record_test("Bulk Upsert API - Endpoint Accessible", True, details=f"Status: {result.returncode}")
|
||||
else:
|
||||
record_test("Bulk Upsert API - Response", False, f"Non-JSON response: {response[:200]}")
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
record_test("Bulk Upsert API - Timeout", False, "API request timed out")
|
||||
except Exception as e:
|
||||
record_test("Bulk Upsert API - Error", False, str(e))
|
||||
|
||||
# Also test the API via browser to ensure it works with session auth
|
||||
success, _ = run_agent_browser(["open", f"{BASE_URL}/api/sessions/bulk_upsert/"], check=False)
|
||||
wait_for_page_load()
|
||||
|
||||
success, result = run_agent_browser(["get", "url", "--json"], check=False)
|
||||
api_url = ""
|
||||
if success and result.get("data"):
|
||||
api_url = result["data"].get("url", "")
|
||||
|
||||
if "bulk_upsert" in api_url:
|
||||
record_test("Bulk Upsert API - Browser Access", True, details=f"URL: {api_url}")
|
||||
else:
|
||||
# It's OK if browser can't access API directly (it might return 403/JSON)
|
||||
elements = get_interactive_elements()
|
||||
# Just check we got some response
|
||||
if elements:
|
||||
record_test("Bulk Upsert API - Browser Access", True, details="API endpoint reachable")
|
||||
else:
|
||||
record_test("Bulk Upsert API - Browser Access", False, "Could not verify API via browser")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def cleanup():
|
||||
"""Cleanup browser sessions"""
|
||||
log("Cleaning up browser sessions...")
|
||||
run_agent_browser(["close"], check=False)
|
||||
# Also close all sessions
|
||||
subprocess.run(
|
||||
[AGENT_BROWSER, "close", "--all"],
|
||||
capture_output=True,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
|
||||
def print_report():
|
||||
"""Print final test report"""
|
||||
print("\n" + "="*70)
|
||||
print("E2E TEST REPORT - agent-base Django Project")
|
||||
print("="*70)
|
||||
print(f"Test Started: {test_results['started_at']}")
|
||||
print(f"Test Finished: {datetime.now().isoformat()}")
|
||||
print("-"*70)
|
||||
|
||||
print(f"\nSUMMARY:")
|
||||
print(f" Total Tests: {len(test_results['tests'])}")
|
||||
print(f" Passed: {test_results['passed']}")
|
||||
print(f" Failed: {test_results['failed']}")
|
||||
print(f" Success Rate: {test_results['passed']/len(test_results['tests'])*100:.1f}%")
|
||||
|
||||
if test_results['errors']:
|
||||
print(f"\nERRORS:")
|
||||
for i, err in enumerate(test_results['errors'], 1):
|
||||
print(f" {i}. {err}")
|
||||
|
||||
print("\nDETAILED RESULTS:")
|
||||
for test in test_results['tests']:
|
||||
status = "✓ PASS" if test['passed'] else "✗ FAIL"
|
||||
print(f" [{status}] {test['name']}")
|
||||
if not test['passed'] and test.get('error'):
|
||||
print(f" Error: {test['error']}")
|
||||
if test.get('details'):
|
||||
print(f" Details: {test['details']}")
|
||||
|
||||
print("\n" + "="*70)
|
||||
|
||||
# Exit with appropriate code
|
||||
if test_results['failed'] > 0:
|
||||
sys.exit(1)
|
||||
else:
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def main():
|
||||
"""Main test runner"""
|
||||
print("\n" + "="*70)
|
||||
print("Starting E2E Tests for agent-base Django Project")
|
||||
print(f"Target: {BASE_URL}")
|
||||
print(f"Session: {SESSION_NAME}")
|
||||
print("="*70 + "\n")
|
||||
|
||||
try:
|
||||
# Clean up any existing sessions first
|
||||
cleanup()
|
||||
time.sleep(1)
|
||||
|
||||
# Run all tests
|
||||
test_admin_login()
|
||||
test_session_management()
|
||||
test_message_management()
|
||||
test_toolcall_management()
|
||||
test_daily_reports_list()
|
||||
test_daily_reports_detail()
|
||||
test_bulk_upsert_api()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
log("Tests interrupted by user")
|
||||
except Exception as e:
|
||||
log(f"Unexpected error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
cleanup()
|
||||
|
||||
# Print final report
|
||||
print_report()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user