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:
232
tests/integration/test_vwb_stability_simple.py
Normal file
232
tests/integration/test_vwb_stability_simple.py
Normal file
@@ -0,0 +1,232 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests de stabilité simplifiés - Visual Workflow Builder Frontend V2
|
||||
Auteur : Dom, Alice, Kiro - 09 janvier 2026
|
||||
|
||||
Script de validation sans dépendance pytest.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
# Chemin vers le frontend VWB
|
||||
SCRIPT_DIR = Path(__file__).parent
|
||||
PROJECT_ROOT = SCRIPT_DIR.parent.parent
|
||||
FRONTEND_PATH = PROJECT_ROOT / "visual_workflow_builder" / "frontend"
|
||||
SRC_PATH = FRONTEND_PATH / "src"
|
||||
|
||||
|
||||
def test_api_client_initial_state_is_offline():
|
||||
"""Vérifie que l'état initial du client API est 'offline'."""
|
||||
api_client_path = SRC_PATH / "services" / "apiClient.ts"
|
||||
if not api_client_path.exists():
|
||||
return False, f"Fichier non trouvé: {api_client_path}"
|
||||
|
||||
content = api_client_path.read_text(encoding='utf-8')
|
||||
|
||||
# Vérifier que l'état initial est 'offline' et non 'checking'
|
||||
if "connectionState: ConnectionState = 'offline'" not in content:
|
||||
return False, "L'état initial du client API doit être 'offline'"
|
||||
|
||||
if "connectionState: ConnectionState = 'checking'" in content:
|
||||
return False, "L'état initial ne doit PAS être 'checking'"
|
||||
|
||||
return True, "OK"
|
||||
|
||||
|
||||
def test_api_client_lazy_initialization():
|
||||
"""Vérifie que l'initialisation est paresseuse (lazy)."""
|
||||
api_client_path = SRC_PATH / "services" / "apiClient.ts"
|
||||
content = api_client_path.read_text(encoding='utf-8')
|
||||
|
||||
# Vérifier la présence du commentaire sur l'initialisation paresseuse
|
||||
if "paresseuse" not in content.lower() and "lazy" not in content.lower():
|
||||
return False, "Le code doit mentionner l'initialisation paresseuse (lazy)"
|
||||
|
||||
return True, "OK"
|
||||
|
||||
|
||||
def test_api_client_async_notifications():
|
||||
"""Vérifie que les notifications sont asynchrones (setTimeout)."""
|
||||
api_client_path = SRC_PATH / "services" / "apiClient.ts"
|
||||
content = api_client_path.read_text(encoding='utf-8')
|
||||
|
||||
# Vérifier la présence de setTimeout pour les notifications
|
||||
if 'setTimeout' not in content:
|
||||
return False, "Les notifications doivent être asynchrones (setTimeout)"
|
||||
|
||||
return True, "OK"
|
||||
|
||||
|
||||
def test_connection_status_hook_initial_state():
|
||||
"""Vérifie que useConnectionStatus a un état initial 'offline'."""
|
||||
hook_path = SRC_PATH / "hooks" / "useConnectionStatus.ts"
|
||||
if not hook_path.exists():
|
||||
return False, f"Fichier non trouvé: {hook_path}"
|
||||
|
||||
content = hook_path.read_text(encoding='utf-8')
|
||||
|
||||
# Vérifier que l'état initial est 'offline'
|
||||
if "status: 'offline'" not in content:
|
||||
return False, "L'état initial du hook doit être 'offline'"
|
||||
|
||||
return True, "OK"
|
||||
|
||||
|
||||
def test_connection_status_hook_uses_refs():
|
||||
"""Vérifie que le hook utilise des refs pour les callbacks."""
|
||||
hook_path = SRC_PATH / "hooks" / "useConnectionStatus.ts"
|
||||
content = hook_path.read_text(encoding='utf-8')
|
||||
|
||||
if 'useRef' not in content:
|
||||
return False, "Le hook doit utiliser useRef pour éviter les re-renders"
|
||||
|
||||
return True, "OK"
|
||||
|
||||
|
||||
def test_connection_status_hook_initial_state_constant():
|
||||
"""Vérifie que l'état initial est une constante stable."""
|
||||
hook_path = SRC_PATH / "hooks" / "useConnectionStatus.ts"
|
||||
content = hook_path.read_text(encoding='utf-8')
|
||||
|
||||
if 'INITIAL_STATE' not in content:
|
||||
return False, "L'état initial doit être une constante INITIAL_STATE"
|
||||
|
||||
return True, "OK"
|
||||
|
||||
|
||||
def test_use_api_client_hook_initial_offline():
|
||||
"""Vérifie que useConnectionState a un état initial 'offline'."""
|
||||
hook_path = SRC_PATH / "hooks" / "useApiClient.ts"
|
||||
if not hook_path.exists():
|
||||
return False, f"Fichier non trouvé: {hook_path}"
|
||||
|
||||
content = hook_path.read_text(encoding='utf-8')
|
||||
|
||||
# Vérifier que l'état initial est 'offline'
|
||||
if "'offline'" not in content:
|
||||
return False, "useConnectionState doit avoir un état initial 'offline'"
|
||||
|
||||
return True, "OK"
|
||||
|
||||
|
||||
def test_workflow_manager_uses_connection_state():
|
||||
"""Vérifie que WorkflowManager utilise useConnectionState."""
|
||||
component_path = SRC_PATH / "components" / "WorkflowManager" / "index.tsx"
|
||||
if not component_path.exists():
|
||||
return False, f"Fichier non trouvé: {component_path}"
|
||||
|
||||
content = component_path.read_text(encoding='utf-8')
|
||||
|
||||
if 'useConnectionState' not in content:
|
||||
return False, "WorkflowManager doit utiliser useConnectionState"
|
||||
|
||||
return True, "OK"
|
||||
|
||||
|
||||
def test_executor_uses_connection_status():
|
||||
"""Vérifie que Executor utilise useConnectionStatus."""
|
||||
component_path = SRC_PATH / "components" / "Executor" / "index.tsx"
|
||||
if not component_path.exists():
|
||||
return False, f"Fichier non trouvé: {component_path}"
|
||||
|
||||
content = component_path.read_text(encoding='utf-8')
|
||||
|
||||
if 'useConnectionStatus' not in content:
|
||||
return False, "Executor doit utiliser useConnectionStatus"
|
||||
|
||||
return True, "OK"
|
||||
|
||||
|
||||
def test_french_comments_in_api_client():
|
||||
"""Vérifie que apiClient.ts a des commentaires en français."""
|
||||
api_client_path = SRC_PATH / "services" / "apiClient.ts"
|
||||
content = api_client_path.read_text(encoding='utf-8')
|
||||
|
||||
french_words = ['Auteur', 'janvier', 'gestion', 'connexion', 'hors ligne']
|
||||
found_french = any(word in content for word in french_words)
|
||||
|
||||
if not found_french:
|
||||
return False, "apiClient.ts doit avoir des commentaires en français"
|
||||
|
||||
return True, "OK"
|
||||
|
||||
|
||||
def test_typescript_syntax_balanced():
|
||||
"""Vérifie la syntaxe TypeScript basique."""
|
||||
files_to_check = [
|
||||
SRC_PATH / "services" / "apiClient.ts",
|
||||
SRC_PATH / "hooks" / "useApiClient.ts",
|
||||
SRC_PATH / "hooks" / "useConnectionStatus.ts",
|
||||
]
|
||||
|
||||
for file_path in files_to_check:
|
||||
if not file_path.exists():
|
||||
continue
|
||||
|
||||
content = file_path.read_text(encoding='utf-8')
|
||||
|
||||
if content.count('{') != content.count('}'):
|
||||
return False, f"Accolades non équilibrées dans {file_path.name}"
|
||||
|
||||
if content.count('(') != content.count(')'):
|
||||
return False, f"Parenthèses non équilibrées dans {file_path.name}"
|
||||
|
||||
return True, "OK"
|
||||
|
||||
|
||||
def run_all_tests():
|
||||
"""Exécute tous les tests et affiche les résultats."""
|
||||
tests = [
|
||||
("État initial apiClient = 'offline'", test_api_client_initial_state_is_offline),
|
||||
("Initialisation paresseuse (lazy)", test_api_client_lazy_initialization),
|
||||
("Notifications asynchrones (setTimeout)", test_api_client_async_notifications),
|
||||
("useConnectionStatus état initial 'offline'", test_connection_status_hook_initial_state),
|
||||
("useConnectionStatus utilise useRef", test_connection_status_hook_uses_refs),
|
||||
("useConnectionStatus INITIAL_STATE constant", test_connection_status_hook_initial_state_constant),
|
||||
("useApiClient état initial 'offline'", test_use_api_client_hook_initial_offline),
|
||||
("WorkflowManager utilise useConnectionState", test_workflow_manager_uses_connection_state),
|
||||
("Executor utilise useConnectionStatus", test_executor_uses_connection_status),
|
||||
("Commentaires français dans apiClient", test_french_comments_in_api_client),
|
||||
("Syntaxe TypeScript équilibrée", test_typescript_syntax_balanced),
|
||||
]
|
||||
|
||||
print("=" * 70)
|
||||
print("Tests de Stabilité - Visual Workflow Builder Frontend V2")
|
||||
print("Auteur : Dom, Alice, Kiro - 09 janvier 2026")
|
||||
print("=" * 70)
|
||||
print()
|
||||
|
||||
passed = 0
|
||||
failed = 0
|
||||
|
||||
for name, test_func in tests:
|
||||
try:
|
||||
success, message = test_func()
|
||||
if success:
|
||||
print(f"✅ PASS: {name}")
|
||||
passed += 1
|
||||
else:
|
||||
print(f"❌ FAIL: {name}")
|
||||
print(f" Raison: {message}")
|
||||
failed += 1
|
||||
except Exception as e:
|
||||
print(f"❌ ERROR: {name}")
|
||||
print(f" Exception: {e}")
|
||||
failed += 1
|
||||
|
||||
print()
|
||||
print("=" * 70)
|
||||
print(f"Résultats: {passed} passés, {failed} échoués sur {len(tests)} tests")
|
||||
print("=" * 70)
|
||||
|
||||
return failed == 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = run_all_tests()
|
||||
sys.exit(0 if success else 1)
|
||||
Reference in New Issue
Block a user