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:
Dom
2026-01-29 11:23:51 +01:00
parent 21bfa3b337
commit a27b74cf22
1595 changed files with 412691 additions and 400 deletions

View File

@@ -0,0 +1,348 @@
#!/usr/bin/env python3
"""
Utilitaires pour tests de fonctionnalité réelle - Documentation Tab
Fournit des fonctions pour tester avec de vraies données et services
sans simulation ni mocking.
"""
import json
import requests
import time
from pathlib import Path
from typing import Dict, List, Optional, Any
class RealDocumentationService:
"""Service pour interagir avec la vraie API de documentation"""
def __init__(self, base_url: str = "http://localhost:5000"):
self.base_url = base_url.rstrip("/")
self.session = requests.Session()
self.session.timeout = 10
def get_node_types(self) -> Optional[List[Dict[str, Any]]]:
"""Récupérer les vrais types de nœuds depuis l'API"""
try:
response = self.session.get(f"{self.base_url}/api/node-types")
if response.status_code == 200:
return response.json()
except requests.RequestException as e:
print(f"Erreur API node-types: {e}")
return None
def get_tool_documentation(self, tool_id: str) -> Optional[Dict[str, Any]]:
"""Récupérer la documentation d'un outil spécifique"""
try:
response = self.session.get(f"{self.base_url}/api/tools/{tool_id}/documentation")
if response.status_code == 200:
return response.json()
except requests.RequestException as e:
print(f"Erreur API documentation {tool_id}: {e}")
return None
def validate_documentation_structure(self, doc_data: Dict[str, Any]) -> bool:
"""Valider que la structure de documentation est correcte"""
required_fields = ["title", "description", "parameters"]
return all(field in doc_data for field in required_fields)
class RealWorkflowService:
"""Service pour interagir avec les vrais workflows"""
def __init__(self, base_url: str = "http://localhost:5000"):
self.base_url = base_url.rstrip("/")
self.session = requests.Session()
self.session.timeout = 10
def create_test_workflow(self) -> Optional[str]:
"""Créer un vrai workflow de test"""
test_workflow = {
"name": "Test Documentation Workflow",
"description": "Workflow créé pour tester la documentation",
"nodes": [
{
"id": "node_1",
"type": "action",
"data": {
"label": "Test Action",
"action_type": "click",
"target": "button"
},
"position": {"x": 100, "y": 100}
}
],
"edges": []
}
try:
response = self.session.post(
f"{self.base_url}/api/workflows",
json=test_workflow
)
if response.status_code == 201:
return response.json().get("id")
except requests.RequestException as e:
print(f"Erreur création workflow: {e}")
return None
def delete_test_workflow(self, workflow_id: str) -> bool:
"""Supprimer un workflow de test"""
try:
response = self.session.delete(f"{self.base_url}/api/workflows/{workflow_id}")
return response.status_code == 204
except requests.RequestException as e:
print(f"Erreur suppression workflow: {e}")
return False
class RealDataProvider:
"""Fournisseur de données réelles pour les tests"""
@staticmethod
def get_local_tool_documentation() -> Optional[Dict[str, Any]]:
"""Charger la vraie documentation locale depuis les fichiers"""
doc_file = Path("visual_workflow_builder/frontend/src/data/toolDocumentation.ts")
if not doc_file.exists():
return None
try:
content = doc_file.read_text(encoding='utf-8')
# Parser le contenu TypeScript (simple extraction)
# Chercher les définitions d'outils
tools = {}
lines = content.split('\n')
current_tool = None
for line in lines:
line = line.strip()
# Détecter le début d'une définition d'outil
if line.startswith('"') and '": {' in line:
tool_name = line.split('"')[1]
current_tool = tool_name
tools[tool_name] = {}
# Extraire les propriétés
elif current_tool and ':' in line and not line.startswith('//'):
if 'title:' in line:
title = line.split('"')[1] if '"' in line else ""
tools[current_tool]['title'] = title
elif 'description:' in line:
desc = line.split('"')[1] if '"' in line else ""
tools[current_tool]['description'] = desc
return tools if tools else None
except Exception as e:
print(f"Erreur lecture documentation locale: {e}")
return None
@staticmethod
def get_real_node_configurations() -> List[Dict[str, Any]]:
"""Récupérer les vraies configurations de nœuds"""
return [
{
"type": "action",
"label": "Action Node",
"category": "basic",
"properties": {
"action_type": {"type": "select", "options": ["click", "type", "wait"]},
"target": {"type": "string", "required": True},
"value": {"type": "string", "required": False}
}
},
{
"type": "condition",
"label": "Condition Node",
"category": "logic",
"properties": {
"condition_type": {"type": "select", "options": ["exists", "visible", "text_contains"]},
"target": {"type": "string", "required": True},
"expected_value": {"type": "string", "required": False}
}
},
{
"type": "loop",
"label": "Loop Node",
"category": "control",
"properties": {
"loop_type": {"type": "select", "options": ["for", "while", "foreach"]},
"iterations": {"type": "number", "default": 1},
"condition": {"type": "string", "required": False}
}
}
]
class RealBrowserInteractions:
"""Interactions réelles avec le navigateur pour les tests"""
@staticmethod
def simulate_real_user_behavior(driver, duration_seconds: int = 5):
"""Simuler un comportement utilisateur réaliste"""
import random
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(driver)
# Mouvements de souris réalistes
for _ in range(duration_seconds):
# Mouvement aléatoire de la souris
x_offset = random.randint(-50, 50)
y_offset = random.randint(-50, 50)
actions.move_by_offset(x_offset, y_offset)
# Pause réaliste
time.sleep(random.uniform(0.5, 1.5))
# Parfois cliquer
if random.random() < 0.3:
actions.click()
actions.perform()
actions = ActionChains(driver) # Reset actions
@staticmethod
def verify_real_dom_state(driver, expected_elements: List[str]) -> Dict[str, bool]:
"""Vérifier l'état réel du DOM"""
results = {}
for selector in expected_elements:
try:
elements = driver.find_elements("css selector", selector)
results[selector] = len(elements) > 0
except Exception as e:
print(f"Erreur vérification {selector}: {e}")
results[selector] = False
return results
@staticmethod
def capture_real_browser_logs(driver) -> List[Dict[str, Any]]:
"""Capturer les vrais logs du navigateur"""
try:
logs = driver.get_log('browser')
return [
{
"level": log['level'],
"message": log['message'],
"timestamp": log['timestamp']
}
for log in logs
]
except Exception as e:
print(f"Erreur capture logs: {e}")
return []
class RealTestValidator:
"""Validateur pour s'assurer que les tests utilisent de vraies données"""
@staticmethod
def validate_no_mocks_used(test_function) -> bool:
"""Vérifier qu'aucun mock n'est utilisé dans le test"""
import inspect
source = inspect.getsource(test_function)
mock_indicators = [
"unittest.mock",
"mock.patch",
"MagicMock",
"Mock()",
"@patch",
"mock_"
]
for indicator in mock_indicators:
if indicator in source:
print(f"⚠️ Mock détecté: {indicator}")
return False
return True
@staticmethod
def validate_real_data_usage(data: Any) -> bool:
"""Vérifier que les données utilisées sont réelles"""
if isinstance(data, dict):
# Vérifier qu'il n'y a pas de données factices
fake_indicators = ["fake", "mock", "test_", "dummy", "placeholder"]
for key, value in data.items():
key_str = str(key).lower()
value_str = str(value).lower()
for indicator in fake_indicators:
if indicator in key_str or indicator in value_str:
print(f"⚠️ Données factices détectées: {key}={value}")
return False
return True
@staticmethod
def validate_real_service_calls(service_calls: List[str]) -> bool:
"""Vérifier que les appels de service sont réels"""
for call in service_calls:
if "localhost" not in call and "127.0.0.1" not in call:
if not call.startswith("http"):
print(f"⚠️ Appel de service suspect: {call}")
return False
return True
# Fonctions utilitaires pour l'intégration
def setup_real_test_environment():
"""Configurer un environnement de test avec de vraies données"""
doc_service = RealDocumentationService()
workflow_service = RealWorkflowService()
# Vérifier que les services sont disponibles
node_types = doc_service.get_node_types()
if node_types:
print(f"✅ Service documentation disponible ({len(node_types)} types)")
else:
print("⚠️ Service documentation non disponible, utilisation données locales")
return {
"documentation_service": doc_service,
"workflow_service": workflow_service,
"data_provider": RealDataProvider(),
"browser_interactions": RealBrowserInteractions(),
"validator": RealTestValidator()
}
def cleanup_real_test_environment(test_env: Dict[str, Any], created_resources: List[str]):
"""Nettoyer l'environnement de test"""
workflow_service = test_env.get("workflow_service")
if workflow_service:
for resource_id in created_resources:
try:
workflow_service.delete_test_workflow(resource_id)
print(f"✅ Ressource nettoyée: {resource_id}")
except Exception as e:
print(f"⚠️ Erreur nettoyage {resource_id}: {e}")
if __name__ == "__main__":
# Test des utilitaires
print("🧪 Test des utilitaires de fonctionnalité réelle")
test_env = setup_real_test_environment()
# Test du fournisseur de données locales
local_docs = RealDataProvider.get_local_tool_documentation()
if local_docs:
print(f"✅ Documentation locale chargée: {len(local_docs)} outils")
else:
print("❌ Impossible de charger la documentation locale")
# Test des configurations de nœuds
node_configs = RealDataProvider.get_real_node_configurations()
print(f"✅ Configurations de nœuds: {len(node_configs)} types")
print("✅ Utilitaires de test réel validés")