#!/usr/bin/env python3 """ Real Functionality Test Utilities for Documentation System Provides utilities for testing real documentation functionality without mocks: - Real API integration testing - Real data validation - Real service management - Real user workflow simulation """ import json import requests import subprocess import time from pathlib import Path from typing import Dict, List, Optional, Any class RealDocumentationData: """Manages real documentation data for testing""" @staticmethod def get_sample_real_data() -> List[Dict[str, Any]]: """Get sample documentation data that matches real RPA Vision V3 system structure""" return [ { "id": "screen_capture", "name": "Screen Capture", "description": "Captures screenshots for RPA Vision V3 analysis using Layer 0 components", "category": "Layer 0 - Raw Capture", "rpa_layer": 0, "parameters": [ {"name": "region", "type": "bbox", "required": False}, {"name": "format", "type": "string", "default": "png"} ], "examples": [ {"description": "Full screen capture", "code": "capture_screen()"}, {"description": "Region capture", "code": "capture_screen(region=(0,0,800,600))"} ], "related_components": ["core.capture.screen_capturer", "agent_v0.screen_capturer"] }, { "id": "ui_detection", "name": "UI Element Detection", "description": "Detects UI elements using OWL-ViT and VLM models for semantic understanding", "category": "Layer 2 - UI Detection", "rpa_layer": 2, "parameters": [ {"name": "screenshot", "type": "image", "required": True}, {"name": "target_description", "type": "string", "required": True}, {"name": "confidence_threshold", "type": "float", "default": 0.8} ], "examples": [ {"description": "Detect button", "code": "detect_ui(screenshot, 'Submit button')"}, {"description": "Find input field", "code": "detect_ui(screenshot, 'username input', 0.9)"} ], "related_components": ["core.detection.ui_detector", "core.detection.owl_detector"] }, { "id": "embedding_fusion", "name": "Multi-modal Embedding Fusion", "description": "Combines visual, textual, and spatial embeddings for robust matching", "category": "Layer 3 - State Embedding", "rpa_layer": 3, "parameters": [ {"name": "image_embedding", "type": "ndarray", "required": True}, {"name": "text_embedding", "type": "ndarray", "required": True}, {"name": "weights", "type": "dict", "default": {"image": 0.6, "text": 0.4}} ], "examples": [ {"description": "Fuse embeddings", "code": "fusion_engine.fuse({'image': img_emb, 'text': txt_emb})"} ], "related_components": ["core.embedding.fusion_engine", "core.embedding.faiss_manager"] }, { "id": "workflow_execution", "name": "Workflow Execution with Self-Healing", "description": "Executes RPA workflows with automatic adaptation to UI changes", "category": "Layer 4 - Workflow Execution", "rpa_layer": 4, "parameters": [ {"name": "workflow_graph", "type": "WorkflowGraph", "required": True}, {"name": "healing_enabled", "type": "boolean", "default": True}, {"name": "max_retries", "type": "int", "default": 3} ], "examples": [ {"description": "Execute workflow", "code": "execute_workflow(workflow_graph)"}, {"description": "Execute with healing", "code": "execute_workflow(workflow_graph, healing_enabled=True)"} ], "related_components": ["core.execution.action_executor", "core.healing.healing_engine"] } ] @staticmethod def create_real_documentation_file(file_path: Path) -> None: """Create a real documentation file for testing""" data = RealDocumentationData.get_sample_real_data() file_path.parent.mkdir(parents=True, exist_ok=True) with open(file_path, 'w') as f: json.dump(data, f, indent=2) @staticmethod def validate_documentation_structure(data: List[Dict[str, Any]]) -> bool: """Validate that documentation data has correct real RPA Vision V3 structure""" required_fields = ['id', 'name', 'description', 'category'] rpa_specific_fields = ['rpa_layer', 'related_components'] for entry in data: # Check basic required fields if not all(field in entry for field in required_fields): return False # Validate parameters if present if 'parameters' in entry: for param in entry['parameters']: if not all(field in param for field in ['name', 'type']): return False # Check for RPA Vision V3 specific fields (at least some entries should have them) if any(field in entry for field in rpa_specific_fields): # Validate RPA layer is valid (0-4) if 'rpa_layer' in entry: if not isinstance(entry['rpa_layer'], int) or not (0 <= entry['rpa_layer'] <= 4): return False # Validate related components format if 'related_components' in entry: if not isinstance(entry['related_components'], list): return False # Check component naming follows RPA Vision V3 conventions for component in entry['related_components']: if not isinstance(component, str) or not component.startswith('core.'): return False return True class RealServiceManager: """Manages real services for testing""" def __init__(self, base_dir: Path = None): self.base_dir = base_dir or Path.cwd() self.processes = {} def start_backend_service(self, port: int = 5000) -> bool: """Start real backend service""" backend_dir = self.base_dir / "visual_workflow_builder" / "backend" if not backend_dir.exists(): print(f"Backend directory not found: {backend_dir}") return False try: # Check if already running if self._is_port_in_use(port): print(f"Backend already running on port {port}") return True # Start backend process = subprocess.Popen( ["python", "app.py"], cwd=backend_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env={**os.environ, "FLASK_ENV": "testing", "PORT": str(port)} ) self.processes['backend'] = process # Wait for service to be ready return self._wait_for_service(f"http://localhost:{port}", timeout=30) except Exception as e: print(f"Failed to start backend: {e}") return False def start_frontend_service(self, port: int = 3000) -> bool: """Start real frontend service""" frontend_dir = self.base_dir / "visual_workflow_builder" / "frontend" if not frontend_dir.exists(): print(f"Frontend directory not found: {frontend_dir}") return False try: # Check if already running if self._is_port_in_use(port): print(f"Frontend already running on port {port}") return True # Start frontend process = subprocess.Popen( ["npm", "start"], cwd=frontend_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env={**os.environ, "PORT": str(port), "BROWSER": "none"} ) self.processes['frontend'] = process # Wait for service to be ready return self._wait_for_service(f"http://localhost:{port}", timeout=60) except Exception as e: print(f"Failed to start frontend: {e}") return False def _is_port_in_use(self, port: int) -> bool: """Check if port is in use""" try: response = requests.get(f"http://localhost:{port}", timeout=2) return True except: return False def _wait_for_service(self, url: str, timeout: int = 30) -> bool: """Wait for service to be ready""" start_time = time.time() while time.time() - start_time < timeout: try: response = requests.get(url, timeout=2) if response.status_code == 200: return True except: pass time.sleep(1) return False def cleanup(self): """Cleanup all started processes""" for name, process in self.processes.items(): try: process.terminate() process.wait(timeout=5) print(f"Stopped {name} service") except: try: process.kill() except: pass class RealAPITester: """Tests real API functionality""" def __init__(self, base_url: str): self.base_url = base_url.rstrip('/') def test_documentation_endpoints(self) -> Dict[str, Any]: """Test real documentation API endpoints""" results = { 'tools_endpoint': False, 'categories_endpoint': False, 'search_endpoint': False, 'data_valid': False, 'response_time_ok': False } # Test tools endpoint try: start_time = time.time() response = requests.get(f"{self.base_url}/api/documentation/tools", timeout=10) response_time = time.time() - start_time if response.status_code == 200: results['tools_endpoint'] = True results['response_time_ok'] = response_time < 2.0 # Should be fast data = response.json() results['data_valid'] = RealDocumentationData.validate_documentation_structure(data) except Exception as e: print(f"Tools endpoint error: {e}") # Test categories endpoint try: response = requests.get(f"{self.base_url}/api/documentation/categories", timeout=10) results['categories_endpoint'] = response.status_code == 200 except Exception as e: print(f"Categories endpoint error: {e}") # Test search endpoint try: response = requests.get(f"{self.base_url}/api/documentation/search?q=click", timeout=10) results['search_endpoint'] = response.status_code == 200 except Exception as e: print(f"Search endpoint error: {e}") return results def get_real_documentation_data(self) -> Optional[List[Dict[str, Any]]]: """Get real documentation data from API""" try: response = requests.get(f"{self.base_url}/api/documentation/tools", timeout=10) if response.status_code == 200: return response.json() except Exception as e: print(f"Failed to get documentation data: {e}") return None class RealUserWorkflowTester: """Tests real user workflows""" @staticmethod def validate_tab_interaction(driver, tab_element) -> bool: """Validate real tab interaction behavior""" try: # Get initial state initial_selected = tab_element.get_attribute("aria-selected") == "true" # Click tab driver.execute_script("arguments[0].click();", tab_element) time.sleep(1) # Verify state changed final_selected = tab_element.get_attribute("aria-selected") == "true" return final_selected and not initial_selected except Exception as e: print(f"Tab interaction validation failed: {e}") return False @staticmethod def validate_content_loading(driver, expected_data: List[Dict[str, Any]]) -> bool: """Validate that real content loaded correctly""" try: from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC wait = WebDriverWait(driver, 10) # Wait for content panel content_panel = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, "[role='tabpanel'], .documentation-content")) ) if not content_panel.is_displayed(): return False # Check for expected content content_text = content_panel.text.lower() # Verify at least some expected entries are present found_count = 0 for entry in expected_data[:3]: # Check first 3 entries if entry['name'].lower() in content_text: found_count += 1 return found_count > 0 except Exception as e: print(f"Content loading validation failed: {e}") return False # Import os for environment variables import os