v1.0 - Version stable: multi-PC, détection UI-DETR-1, 3 modes exécution
- Frontend v4 accessible sur réseau local (192.168.1.40) - Ports ouverts: 3002 (frontend), 5001 (backend), 5004 (dashboard) - Ollama GPU fonctionnel - Self-healing interactif - Dashboard confiance Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
366
test_agent_upload.py
Normal file
366
test_agent_upload.py
Normal file
@@ -0,0 +1,366 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Real functionality test for agent upload system.
|
||||
|
||||
Tests the complete upload flow using actual RPA Vision V3 components:
|
||||
- Creates a real RawSession with proper schema
|
||||
- Uses actual agent storage and encryption
|
||||
- Tests real server authentication and processing
|
||||
- Validates complete data flow end-to-end
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import zipfile
|
||||
import json
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from typing import Dict, Any
|
||||
|
||||
import requests
|
||||
|
||||
# Add project root to path for imports
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
# Import real RPA Vision V3 components
|
||||
from core.models.raw_session import RawSession, Event, Screenshot, RawWindowContext
|
||||
from agent_v0.storage_encrypted import create_session_zip_encrypted
|
||||
from agent_v0.uploader import upload_session_zip
|
||||
|
||||
# Load environment configuration
|
||||
def load_env_config() -> Dict[str, str]:
|
||||
"""Load environment variables from .env.local"""
|
||||
env_vars = {}
|
||||
env_file = Path(".env.local")
|
||||
|
||||
if env_file.exists():
|
||||
with open(env_file, 'r') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line and not line.startswith('#') and '=' in line:
|
||||
key, value = line.split('=', 1)
|
||||
env_vars[key] = value
|
||||
os.environ[key] = value
|
||||
|
||||
return env_vars
|
||||
|
||||
# Configuration
|
||||
env_config = load_env_config()
|
||||
ADMIN_TOKEN = env_config.get('RPA_TOKEN_ADMIN') or os.getenv('RPA_TOKEN_ADMIN')
|
||||
ENCRYPTION_PASSWORD = env_config.get('ENCRYPTION_PASSWORD') or os.getenv('ENCRYPTION_PASSWORD')
|
||||
API_URL = "http://localhost:8000/api/traces/upload"
|
||||
|
||||
def create_realistic_raw_session() -> RawSession:
|
||||
"""Create a realistic RawSession with proper schema and data."""
|
||||
session_id = f"test_session_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
||||
|
||||
# Create session with real structure
|
||||
session = RawSession(
|
||||
session_id=session_id,
|
||||
agent_version="0.1.0",
|
||||
environment={
|
||||
"platform": "linux",
|
||||
"hostname": "test-machine",
|
||||
"screen": {
|
||||
"primary_resolution": [1920, 1080],
|
||||
"display_scale": 1.0
|
||||
}
|
||||
},
|
||||
user={
|
||||
"id": "test_user",
|
||||
"label": "Test User"
|
||||
},
|
||||
context={
|
||||
"customer": "Test Organization",
|
||||
"training_label": "Upload Test Workflow",
|
||||
"notes": "Automated test session for upload functionality"
|
||||
},
|
||||
started_at=datetime.now(),
|
||||
ended_at=datetime.now()
|
||||
)
|
||||
|
||||
# Add realistic events
|
||||
window_context = RawWindowContext(
|
||||
title="Test Application",
|
||||
app_name="test_app"
|
||||
)
|
||||
|
||||
# Mouse click event
|
||||
click_event = Event(
|
||||
t=1.0,
|
||||
type="mouse_click",
|
||||
window=window_context,
|
||||
screenshot_id="shot_0001",
|
||||
data={
|
||||
"button": "left",
|
||||
"pos": [450, 320]
|
||||
}
|
||||
)
|
||||
session.add_event(click_event)
|
||||
|
||||
# Keyboard input event
|
||||
key_event = Event(
|
||||
t=2.5,
|
||||
type="key_combo",
|
||||
window=window_context,
|
||||
screenshot_id="shot_0002",
|
||||
data={
|
||||
"keys": ["CTRL", "C"]
|
||||
}
|
||||
)
|
||||
session.add_event(key_event)
|
||||
|
||||
# Add screenshots
|
||||
screenshot1 = Screenshot(
|
||||
screenshot_id="shot_0001",
|
||||
relative_path="shots/shot_0001.png",
|
||||
captured_at=datetime.now().isoformat()
|
||||
)
|
||||
session.add_screenshot(screenshot1)
|
||||
|
||||
screenshot2 = Screenshot(
|
||||
screenshot_id="shot_0002",
|
||||
relative_path="shots/shot_0002.png",
|
||||
captured_at=datetime.now().isoformat()
|
||||
)
|
||||
session.add_screenshot(screenshot2)
|
||||
|
||||
return session
|
||||
|
||||
def create_session_files(session: RawSession, base_dir: Path) -> Path:
|
||||
"""Create actual session files on disk like the real agent does."""
|
||||
session_dir = base_dir / session.session_id
|
||||
session_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Create shots directory
|
||||
shots_dir = session_dir / "shots"
|
||||
shots_dir.mkdir(exist_ok=True)
|
||||
|
||||
# Save session JSON
|
||||
session_json_path = session_dir / f"{session.session_id}.json"
|
||||
session.save_to_file(session_json_path)
|
||||
|
||||
# Create dummy screenshot files
|
||||
for screenshot in session.screenshots:
|
||||
shot_path = session_dir / screenshot.relative_path
|
||||
shot_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Create a minimal PNG file (1x1 pixel)
|
||||
png_data = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x0cIDATx\x9cc```\x00\x00\x00\x04\x00\x01\xdd\x8d\xb4\x1c\x00\x00\x00\x00IEND\xaeB`\x82'
|
||||
with open(shot_path, 'wb') as f:
|
||||
f.write(png_data)
|
||||
|
||||
return session_dir
|
||||
|
||||
def test_real_upload_flow():
|
||||
"""Test the complete upload flow using real components."""
|
||||
print("🧪 Testing Real Agent Upload Flow")
|
||||
print("=" * 50)
|
||||
|
||||
if not ADMIN_TOKEN:
|
||||
print("❌ No admin token found in environment")
|
||||
return False
|
||||
|
||||
if not ENCRYPTION_PASSWORD:
|
||||
print("❌ No encryption password found in environment")
|
||||
return False
|
||||
|
||||
print(f"📡 API URL: {API_URL}")
|
||||
print(f"🔑 Using token: {ADMIN_TOKEN[:8]}...")
|
||||
print(f"🔒 Encryption: {'Enabled' if ENCRYPTION_PASSWORD else 'Disabled'}")
|
||||
print()
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
temp_path = Path(temp_dir)
|
||||
|
||||
try:
|
||||
# 1. Create realistic RawSession
|
||||
print("1️⃣ Creating realistic RawSession...")
|
||||
session = create_realistic_raw_session()
|
||||
print(f" Session ID: {session.session_id}")
|
||||
print(f" Events: {len(session.events)}")
|
||||
print(f" Screenshots: {len(session.screenshots)}")
|
||||
print(f" User: {session.user['label']}")
|
||||
|
||||
# 2. Create session files on disk
|
||||
print("\n2️⃣ Creating session files...")
|
||||
session_dir = create_session_files(session, temp_path)
|
||||
print(f" Session directory: {session_dir}")
|
||||
|
||||
# Verify files exist
|
||||
json_file = session_dir / f"{session.session_id}.json"
|
||||
assert json_file.exists(), "Session JSON not created"
|
||||
|
||||
# Verify JSON is valid RawSession
|
||||
loaded_session = RawSession.load_from_file(json_file)
|
||||
assert loaded_session.session_id == session.session_id
|
||||
print(f" ✅ Session JSON validated")
|
||||
|
||||
# 3. Create encrypted ZIP using real agent code
|
||||
print("\n3️⃣ Creating encrypted ZIP...")
|
||||
encrypted_path = create_session_zip_encrypted(
|
||||
session=session,
|
||||
password=ENCRYPTION_PASSWORD,
|
||||
base_dir=str(temp_path)
|
||||
)
|
||||
print(f" Encrypted file: {encrypted_path}")
|
||||
print(f" File size: {os.path.getsize(encrypted_path)} bytes")
|
||||
|
||||
# 4. Test upload using real uploader
|
||||
print("\n4️⃣ Testing upload with real uploader...")
|
||||
|
||||
# Set environment for uploader
|
||||
os.environ['RPA_TOKEN_ADMIN'] = ADMIN_TOKEN
|
||||
|
||||
# Use real uploader function
|
||||
success = upload_session_zip(
|
||||
zip_path=encrypted_path,
|
||||
session_id=session.session_id,
|
||||
max_retries=1
|
||||
)
|
||||
|
||||
if success:
|
||||
print(" ✅ Upload successful via real uploader!")
|
||||
else:
|
||||
print(" ❌ Upload failed via real uploader")
|
||||
return False
|
||||
|
||||
# 5. Verify server response
|
||||
print("\n5️⃣ Verifying server processed the session...")
|
||||
|
||||
# Check sessions endpoint
|
||||
try:
|
||||
headers = {'Authorization': f'Bearer {ADMIN_TOKEN}'}
|
||||
response = requests.get(f"http://localhost:8000/api/traces/sessions", headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
sessions_data = response.json()
|
||||
uploaded_sessions = [s for s in sessions_data['sessions'] if s['session_id'] == session.session_id]
|
||||
|
||||
if uploaded_sessions:
|
||||
uploaded_session = uploaded_sessions[0]
|
||||
print(f" ✅ Session found on server!")
|
||||
print(f" Events: {uploaded_session['events_count']}")
|
||||
print(f" Screenshots: {uploaded_session['screenshots_count']}")
|
||||
print(f" User: {uploaded_session['user']['label']}")
|
||||
|
||||
# Verify data integrity
|
||||
assert uploaded_session['events_count'] == len(session.events)
|
||||
assert uploaded_session['screenshots_count'] == len(session.screenshots)
|
||||
print(f" ✅ Data integrity verified!")
|
||||
|
||||
else:
|
||||
print(" ❌ Session not found on server")
|
||||
return False
|
||||
else:
|
||||
print(f" ❌ Failed to query sessions: {response.status_code}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Error verifying server: {e}")
|
||||
return False
|
||||
|
||||
print("\n🎉 All tests passed! Real functionality working correctly.")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Test failed with error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def test_authentication_scenarios():
|
||||
"""Test various authentication scenarios."""
|
||||
print("\n🔐 Testing Authentication Scenarios")
|
||||
print("=" * 40)
|
||||
|
||||
# Test with invalid token
|
||||
print("Testing invalid token...")
|
||||
headers = {'Authorization': 'Bearer invalid_token_123'}
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix='.zip') as tmp_file:
|
||||
with zipfile.ZipFile(tmp_file.name, 'w') as zf:
|
||||
zf.writestr('test.json', '{"test": "data"}')
|
||||
|
||||
with open(tmp_file.name, 'rb') as f:
|
||||
files = {'file': ('test.zip', f, 'application/zip')}
|
||||
data = {'session_id': 'test_invalid_auth'}
|
||||
|
||||
response = requests.post(API_URL, files=files, data=data, headers=headers)
|
||||
|
||||
if response.status_code == 401:
|
||||
print(" ✅ Invalid token correctly rejected")
|
||||
else:
|
||||
print(f" ❌ Expected 401, got {response.status_code}")
|
||||
return False
|
||||
|
||||
# Test without token
|
||||
print("Testing missing token...")
|
||||
with tempfile.NamedTemporaryFile(suffix='.zip') as tmp_file:
|
||||
with zipfile.ZipFile(tmp_file.name, 'w') as zf:
|
||||
zf.writestr('test.json', '{"test": "data"}')
|
||||
|
||||
with open(tmp_file.name, 'rb') as f:
|
||||
files = {'file': ('test.zip', f, 'application/zip')}
|
||||
data = {'session_id': 'test_no_auth'}
|
||||
|
||||
response = requests.post(API_URL, files=files, data=data)
|
||||
|
||||
if response.status_code == 401:
|
||||
print(" ✅ Missing token correctly rejected")
|
||||
else:
|
||||
print(f" ❌ Expected 401, got {response.status_code}")
|
||||
return False
|
||||
|
||||
print(" ✅ Authentication tests passed!")
|
||||
return True
|
||||
|
||||
def test_server_availability():
|
||||
"""Test if the server is running and accessible."""
|
||||
print("🌐 Testing Server Availability")
|
||||
print("=" * 30)
|
||||
|
||||
try:
|
||||
response = requests.get("http://localhost:8000/api/traces/status", timeout=5)
|
||||
if response.status_code == 200:
|
||||
status_data = response.json()
|
||||
print(f" ✅ Server online: {status_data['status']}")
|
||||
print(f" Version: {status_data['version']}")
|
||||
print(f" Encryption: {'Enabled' if status_data['encryption_enabled'] else 'Disabled'}")
|
||||
return True
|
||||
else:
|
||||
print(f" ❌ Server returned {response.status_code}")
|
||||
return False
|
||||
except requests.exceptions.ConnectionError:
|
||||
print(" ❌ Server not accessible. Is it running on port 8000?")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f" ❌ Error checking server: {e}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
"""Run all tests."""
|
||||
print("🚀 RPA Vision V3 - Real Agent Upload Test")
|
||||
print("=" * 60)
|
||||
|
||||
# Check server availability first
|
||||
if not test_server_availability():
|
||||
print("\n💡 Start the server with: ./run.sh --server")
|
||||
return False
|
||||
|
||||
# Test authentication
|
||||
if not test_authentication_scenarios():
|
||||
return False
|
||||
|
||||
# Test real upload flow
|
||||
if not test_real_upload_flow():
|
||||
return False
|
||||
|
||||
print("\n🎉 All tests completed successfully!")
|
||||
print("✅ Real functionality verified end-to-end")
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = main()
|
||||
sys.exit(0 if success else 1)
|
||||
Reference in New Issue
Block a user