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:
298
test_server_with_env.py
Normal file
298
test_server_with_env.py
Normal file
@@ -0,0 +1,298 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test de fonctionnalité réelle pour l'authentification serveur avec variables d'environnement.
|
||||
|
||||
Ce test utilise les vraies implémentations:
|
||||
- Chargement réel des variables d'environnement
|
||||
- Serveur Flask réel avec authentification
|
||||
- Vraies requêtes HTTP avec tokens
|
||||
- Validation complète du pipeline d'authentification
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import subprocess
|
||||
import requests
|
||||
import socket
|
||||
from pathlib import Path
|
||||
from contextlib import contextmanager
|
||||
|
||||
# Ajouter le répertoire racine au path pour les imports
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
def find_free_port():
|
||||
"""Trouve un port libre pour le serveur de test."""
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.bind(('', 0))
|
||||
s.listen(1)
|
||||
port = s.getsockname()[1]
|
||||
return port
|
||||
|
||||
def load_env_local():
|
||||
"""Charge les variables d'environnement depuis .env.local"""
|
||||
env_path = Path(".env.local")
|
||||
if env_path.exists():
|
||||
print(f"📁 Chargement de {env_path}")
|
||||
with open(env_path, 'r') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line and not line.startswith('#') and '=' in line:
|
||||
key, value = line.split('=', 1)
|
||||
os.environ[key.strip()] = value.strip()
|
||||
if key.strip() in ['RPA_TOKEN_ADMIN', 'RPA_TOKEN_READONLY']:
|
||||
print(f" 🔑 {key.strip()}: {value.strip()[:16]}...")
|
||||
return True
|
||||
else:
|
||||
print(f"⚠️ Fichier {env_path} non trouvé")
|
||||
return False
|
||||
|
||||
def validate_environment():
|
||||
"""Valide que l'environnement est correctement configuré."""
|
||||
required_vars = ['RPA_TOKEN_ADMIN', 'RPA_TOKEN_READONLY']
|
||||
missing_vars = []
|
||||
|
||||
for var in required_vars:
|
||||
if not os.environ.get(var):
|
||||
missing_vars.append(var)
|
||||
|
||||
if missing_vars:
|
||||
print(f"❌ Variables manquantes: {', '.join(missing_vars)}")
|
||||
return False
|
||||
|
||||
print("✅ Variables d'environnement validées")
|
||||
return True
|
||||
|
||||
@contextmanager
|
||||
def real_server_instance(port):
|
||||
"""Context manager pour démarrer/arrêter un serveur réel."""
|
||||
server_process = None
|
||||
try:
|
||||
# Configurer l'environnement pour le serveur
|
||||
server_env = os.environ.copy()
|
||||
server_env['FLASK_PORT'] = str(port)
|
||||
server_env['FLASK_HOST'] = '127.0.0.1'
|
||||
|
||||
print(f"🚀 Démarrage serveur réel sur port {port}...")
|
||||
|
||||
# Démarrer le serveur avec les vraies implémentations
|
||||
server_process = subprocess.Popen([
|
||||
sys.executable, "server/api_upload.py"
|
||||
],
|
||||
env=server_env,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
cwd=Path(__file__).parent
|
||||
)
|
||||
|
||||
# Attendre que le serveur soit prêt avec vérification active
|
||||
max_attempts = 20
|
||||
for attempt in range(max_attempts):
|
||||
try:
|
||||
response = requests.get(f"http://127.0.0.1:{port}/health", timeout=1)
|
||||
if response.status_code == 200:
|
||||
print(f"✅ Serveur prêt après {attempt + 1} tentatives")
|
||||
break
|
||||
except requests.exceptions.RequestException:
|
||||
pass
|
||||
|
||||
if attempt < max_attempts - 1:
|
||||
time.sleep(0.5)
|
||||
else:
|
||||
# Vérifier les logs du serveur si échec
|
||||
stdout, stderr = server_process.communicate(timeout=1)
|
||||
print(f"❌ Serveur non prêt après {max_attempts} tentatives")
|
||||
if stderr:
|
||||
print(f"Erreurs serveur: {stderr.decode()}")
|
||||
raise RuntimeError("Serveur non démarré")
|
||||
|
||||
yield port
|
||||
|
||||
finally:
|
||||
if server_process:
|
||||
print("🛑 Arrêt du serveur...")
|
||||
server_process.terminate()
|
||||
try:
|
||||
server_process.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
server_process.kill()
|
||||
server_process.wait()
|
||||
|
||||
def test_authentication_endpoints(port, token_admin, token_readonly):
|
||||
"""Test les endpoints d'authentification avec de vrais tokens."""
|
||||
base_url = f"http://127.0.0.1:{port}"
|
||||
|
||||
test_cases = [
|
||||
{
|
||||
"name": "Status avec token admin",
|
||||
"url": f"{base_url}/api/traces/status",
|
||||
"headers": {"Authorization": f"Bearer {token_admin}"},
|
||||
"expected_status": 200,
|
||||
"should_have_json": True
|
||||
},
|
||||
{
|
||||
"name": "Status avec token readonly",
|
||||
"url": f"{base_url}/api/traces/status",
|
||||
"headers": {"Authorization": f"Bearer {token_readonly}"},
|
||||
"expected_status": 200,
|
||||
"should_have_json": True
|
||||
},
|
||||
{
|
||||
"name": "Status sans token",
|
||||
"url": f"{base_url}/api/traces/status",
|
||||
"headers": {},
|
||||
"expected_status": 401,
|
||||
"should_have_json": False
|
||||
},
|
||||
{
|
||||
"name": "Status avec token invalide",
|
||||
"url": f"{base_url}/api/traces/status",
|
||||
"headers": {"Authorization": "Bearer invalid_token_123"},
|
||||
"expected_status": 401,
|
||||
"should_have_json": False
|
||||
}
|
||||
]
|
||||
|
||||
results = []
|
||||
|
||||
for test_case in test_cases:
|
||||
print(f"🧪 Test: {test_case['name']}")
|
||||
|
||||
try:
|
||||
response = requests.get(
|
||||
test_case['url'],
|
||||
headers=test_case['headers'],
|
||||
timeout=5
|
||||
)
|
||||
|
||||
# Vérifier le status code
|
||||
if response.status_code == test_case['expected_status']:
|
||||
print(f" ✅ Status code correct: {response.status_code}")
|
||||
|
||||
# Vérifier le contenu JSON si attendu
|
||||
if test_case['should_have_json']:
|
||||
try:
|
||||
json_data = response.json()
|
||||
print(f" ✅ Réponse JSON valide: {json_data}")
|
||||
except ValueError:
|
||||
print(f" ❌ Réponse JSON invalide")
|
||||
results.append(False)
|
||||
continue
|
||||
|
||||
results.append(True)
|
||||
else:
|
||||
print(f" ❌ Status code incorrect: {response.status_code} (attendu: {test_case['expected_status']})")
|
||||
print(f" 📄 Réponse: {response.text}")
|
||||
results.append(False)
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Erreur requête: {e}")
|
||||
results.append(False)
|
||||
|
||||
return all(results)
|
||||
|
||||
def test_real_upload_functionality(port, token_admin):
|
||||
"""Test la fonctionnalité d'upload réelle avec de vraies données."""
|
||||
print("🧪 Test upload de session réelle...")
|
||||
|
||||
# Créer des données de test réalistes
|
||||
test_session_data = {
|
||||
"session_id": "test_session_real_functionality",
|
||||
"user_id": "test_user",
|
||||
"timestamp": "2025-01-05T10:00:00Z",
|
||||
"events": [
|
||||
{
|
||||
"type": "mouse_click",
|
||||
"timestamp": "2025-01-05T10:00:01Z",
|
||||
"x": 100,
|
||||
"y": 200,
|
||||
"button": "left"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
f"http://127.0.0.1:{port}/api/traces/upload",
|
||||
headers={
|
||||
"Authorization": f"Bearer {token_admin}",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
json=test_session_data,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code in [200, 201]:
|
||||
print(" ✅ Upload réussi")
|
||||
try:
|
||||
result = response.json()
|
||||
print(f" 📄 Réponse: {result}")
|
||||
return True
|
||||
except ValueError:
|
||||
print(" ⚠️ Upload réussi mais réponse non-JSON")
|
||||
return True
|
||||
else:
|
||||
print(f" ❌ Upload échoué: {response.status_code}")
|
||||
print(f" 📄 Erreur: {response.text}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Erreur upload: {e}")
|
||||
return False
|
||||
|
||||
def test_server_with_real_env():
|
||||
"""Test principal avec fonctionnalités réelles."""
|
||||
print("=" * 60)
|
||||
print("🎯 Test de fonctionnalité réelle - Authentification serveur")
|
||||
print("=" * 60)
|
||||
|
||||
# 1. Charger l'environnement réel
|
||||
if not load_env_local():
|
||||
print("❌ Impossible de charger .env.local")
|
||||
return False
|
||||
|
||||
# 2. Valider l'environnement
|
||||
if not validate_environment():
|
||||
return False
|
||||
|
||||
# 3. Récupérer les tokens réels
|
||||
token_admin = os.environ.get('RPA_TOKEN_ADMIN')
|
||||
token_readonly = os.environ.get('RPA_TOKEN_READONLY')
|
||||
|
||||
print(f"🔑 Token admin: {token_admin[:16]}...")
|
||||
print(f"🔑 Token readonly: {token_readonly[:16]}...")
|
||||
|
||||
# 4. Trouver un port libre
|
||||
port = find_free_port()
|
||||
print(f"🌐 Port sélectionné: {port}")
|
||||
|
||||
# 5. Démarrer serveur réel et tester
|
||||
try:
|
||||
with real_server_instance(port):
|
||||
# Test authentification
|
||||
auth_success = test_authentication_endpoints(port, token_admin, token_readonly)
|
||||
|
||||
# Test upload réel
|
||||
upload_success = test_real_upload_functionality(port, token_admin)
|
||||
|
||||
return auth_success and upload_success
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Erreur serveur: {e}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = test_server_with_real_env()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
if success:
|
||||
print("🎉 SUCCÈS - Tous les tests de fonctionnalité réelle passent!")
|
||||
print("✅ Authentification serveur validée")
|
||||
print("✅ Variables d'environnement correctes")
|
||||
print("✅ Pipeline complet fonctionnel")
|
||||
else:
|
||||
print("💥 ÉCHEC - Certains tests ont échoué")
|
||||
print("❌ Vérifiez la configuration et les logs ci-dessus")
|
||||
|
||||
print("=" * 60)
|
||||
exit(0 if success else 1)
|
||||
Reference in New Issue
Block a user