- 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>
658 lines
25 KiB
Python
658 lines
25 KiB
Python
"""
|
|
Tests Unitaires - Contrats de Données VWB
|
|
|
|
Auteur : Dom, Alice, Kiro - 09 janvier 2026
|
|
|
|
Tests complets pour les contrats de données du Visual Workflow Builder :
|
|
- VWBActionError
|
|
- VWBEvidence
|
|
- VWBVisualAnchor
|
|
|
|
Ces tests valident la sérialisation JSON, la validation des données,
|
|
et le comportement des méthodes utilitaires.
|
|
"""
|
|
|
|
import unittest
|
|
import json
|
|
import base64
|
|
from datetime import datetime
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
# Import des contrats VWB
|
|
import sys
|
|
from pathlib import Path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
|
|
|
from visual_workflow_builder.backend.contracts.error import (
|
|
VWBActionError, VWBErrorType, VWBErrorSeverity, create_vwb_error
|
|
)
|
|
from visual_workflow_builder.backend.contracts.evidence import (
|
|
VWBEvidence, VWBEvidenceType, create_screenshot_evidence, create_interaction_evidence
|
|
)
|
|
from visual_workflow_builder.backend.contracts.visual_anchor import (
|
|
VWBVisualAnchor, VWBVisualAnchorType, create_image_anchor, create_text_anchor
|
|
)
|
|
|
|
|
|
class TestVWBActionError(unittest.TestCase):
|
|
"""Tests pour VWBActionError."""
|
|
|
|
def setUp(self):
|
|
"""Configuration des tests."""
|
|
self.timestamp = datetime.now()
|
|
self.error_data = {
|
|
'error_id': 'test_error_001',
|
|
'error_type': VWBErrorType.ELEMENT_NOT_FOUND,
|
|
'severity': VWBErrorSeverity.ERROR,
|
|
'message': 'Élément non trouvé',
|
|
'description': 'L\'élément bouton "Valider" n\'a pas été trouvé sur l\'écran',
|
|
'action_id': 'action_click_001',
|
|
'step_id': 'step_001',
|
|
'workflow_id': 'workflow_test',
|
|
'timestamp': self.timestamp,
|
|
'execution_time_ms': 1500.0,
|
|
'technical_details': {'screen_resolution': '1920x1080'},
|
|
'stack_trace': 'Traceback...',
|
|
'suggestions': ['Vérifier que l\'élément est visible'],
|
|
'retry_possible': True,
|
|
'user_id': 'user_test',
|
|
'session_id': 'session_001',
|
|
'environment': 'test'
|
|
}
|
|
|
|
def test_error_creation(self):
|
|
"""Test de création d'une erreur VWB."""
|
|
error = VWBActionError(**self.error_data)
|
|
|
|
self.assertEqual(error.error_id, 'test_error_001')
|
|
self.assertEqual(error.error_type, VWBErrorType.ELEMENT_NOT_FOUND)
|
|
self.assertEqual(error.severity, VWBErrorSeverity.ERROR)
|
|
self.assertEqual(error.message, 'Élément non trouvé')
|
|
self.assertEqual(error.action_id, 'action_click_001')
|
|
self.assertTrue(error.retry_possible)
|
|
|
|
def test_error_auto_id_generation(self):
|
|
"""Test de génération automatique de l'ID d'erreur."""
|
|
data = self.error_data.copy()
|
|
data['error_id'] = ''
|
|
|
|
error = VWBActionError(**data)
|
|
|
|
self.assertTrue(error.error_id.startswith('err_action_click_001_'))
|
|
self.assertGreater(len(error.error_id), 20)
|
|
|
|
def test_default_suggestions_generation(self):
|
|
"""Test de génération des suggestions par défaut."""
|
|
data = self.error_data.copy()
|
|
data['suggestions'] = []
|
|
|
|
error = VWBActionError(**data)
|
|
|
|
self.assertGreater(len(error.suggestions), 0)
|
|
self.assertIn('Vérifiez que l\'élément est visible à l\'écran', error.suggestions)
|
|
|
|
def test_error_serialization(self):
|
|
"""Test de sérialisation JSON."""
|
|
error = VWBActionError(**self.error_data)
|
|
|
|
# Test to_dict
|
|
error_dict = error.to_dict()
|
|
self.assertEqual(error_dict['error_type'], 'element_not_found')
|
|
self.assertEqual(error_dict['severity'], 'error')
|
|
self.assertIsInstance(error_dict['timestamp'], str)
|
|
|
|
# Test to_json
|
|
error_json = error.to_json()
|
|
self.assertIsInstance(error_json, str)
|
|
parsed = json.loads(error_json)
|
|
self.assertEqual(parsed['error_type'], 'element_not_found')
|
|
|
|
def test_error_deserialization(self):
|
|
"""Test de désérialisation JSON."""
|
|
error = VWBActionError(**self.error_data)
|
|
error_dict = error.to_dict()
|
|
|
|
# Test from_dict
|
|
restored_error = VWBActionError.from_dict(error_dict)
|
|
self.assertEqual(restored_error.error_id, error.error_id)
|
|
self.assertEqual(restored_error.error_type, error.error_type)
|
|
self.assertEqual(restored_error.severity, error.severity)
|
|
|
|
# Test from_json
|
|
error_json = error.to_json()
|
|
restored_from_json = VWBActionError.from_json(error_json)
|
|
self.assertEqual(restored_from_json.message, error.message)
|
|
|
|
def test_is_retryable(self):
|
|
"""Test de la logique de retry."""
|
|
# Erreur retryable
|
|
error = VWBActionError(**self.error_data)
|
|
self.assertTrue(error.is_retryable())
|
|
|
|
# Erreur non retryable (paramètre invalide)
|
|
data = self.error_data.copy()
|
|
data['error_type'] = VWBErrorType.PARAMETER_INVALID
|
|
error_non_retryable = VWBActionError(**data)
|
|
self.assertFalse(error_non_retryable.is_retryable())
|
|
|
|
# Erreur fatale
|
|
data = self.error_data.copy()
|
|
data['severity'] = VWBErrorSeverity.FATAL
|
|
error_fatal = VWBActionError(**data)
|
|
self.assertFalse(error_fatal.is_retryable())
|
|
|
|
def test_user_friendly_message(self):
|
|
"""Test des messages conviviaux."""
|
|
error = VWBActionError(**self.error_data)
|
|
friendly_msg = error.get_user_friendly_message()
|
|
|
|
self.assertIn('❌', friendly_msg)
|
|
self.assertIn('Élément non trouvé sur l\'écran', friendly_msg)
|
|
|
|
def test_create_vwb_error_utility(self):
|
|
"""Test de la fonction utilitaire create_vwb_error."""
|
|
error = create_vwb_error(
|
|
error_type=VWBErrorType.CLICK_FAILED,
|
|
message='Clic échoué',
|
|
action_id='action_001',
|
|
step_id='step_001',
|
|
severity=VWBErrorSeverity.WARNING
|
|
)
|
|
|
|
self.assertEqual(error.error_type, VWBErrorType.CLICK_FAILED)
|
|
self.assertEqual(error.severity, VWBErrorSeverity.WARNING)
|
|
self.assertEqual(error.message, 'Clic échoué')
|
|
|
|
|
|
class TestVWBEvidence(unittest.TestCase):
|
|
"""Tests pour VWBEvidence."""
|
|
|
|
def setUp(self):
|
|
"""Configuration des tests."""
|
|
self.timestamp = datetime.now()
|
|
|
|
# Créer un screenshot base64 factice
|
|
self.fake_screenshot = base64.b64encode(b'fake_image_data').decode('utf-8')
|
|
|
|
self.evidence_data = {
|
|
'evidence_id': 'ev_test_001',
|
|
'evidence_type': VWBEvidenceType.SCREENSHOT_BEFORE,
|
|
'action_id': 'action_001',
|
|
'step_id': 'step_001',
|
|
'workflow_id': 'workflow_test',
|
|
'timestamp': self.timestamp,
|
|
'execution_time_ms': 500.0,
|
|
'title': 'Capture avant clic',
|
|
'description': 'Screenshot avant l\'action de clic',
|
|
'screenshot_base64': self.fake_screenshot,
|
|
'screenshot_width': 1920,
|
|
'screenshot_height': 1080,
|
|
'highlight_box': {'x': 100, 'y': 200, 'width': 150, 'height': 50},
|
|
'data': {'confidence': 0.95},
|
|
'success': True,
|
|
'confidence_score': 0.95,
|
|
'user_id': 'user_test',
|
|
'session_id': 'session_001',
|
|
'related_evidence_ids': [],
|
|
'parent_evidence_id': None
|
|
}
|
|
|
|
def test_evidence_creation(self):
|
|
"""Test de création d'une evidence VWB."""
|
|
evidence = VWBEvidence(**self.evidence_data)
|
|
|
|
self.assertEqual(evidence.evidence_id, 'ev_test_001')
|
|
self.assertEqual(evidence.evidence_type, VWBEvidenceType.SCREENSHOT_BEFORE)
|
|
self.assertEqual(evidence.title, 'Capture avant clic')
|
|
self.assertTrue(evidence.success)
|
|
self.assertEqual(evidence.screenshot_width, 1920)
|
|
|
|
def test_evidence_auto_id_generation(self):
|
|
"""Test de génération automatique de l'ID d'evidence."""
|
|
data = self.evidence_data.copy()
|
|
data['evidence_id'] = ''
|
|
|
|
evidence = VWBEvidence(**data)
|
|
|
|
self.assertTrue(evidence.evidence_id.startswith('ev_action_001_'))
|
|
|
|
def test_has_screenshot(self):
|
|
"""Test de détection de screenshot."""
|
|
evidence = VWBEvidence(**self.evidence_data)
|
|
self.assertTrue(evidence.has_screenshot())
|
|
|
|
# Sans screenshot
|
|
data = self.evidence_data.copy()
|
|
data['screenshot_base64'] = None
|
|
evidence_no_screenshot = VWBEvidence(**data)
|
|
self.assertFalse(evidence_no_screenshot.has_screenshot())
|
|
|
|
def test_has_highlight(self):
|
|
"""Test de détection de zone surlignée."""
|
|
evidence = VWBEvidence(**self.evidence_data)
|
|
self.assertTrue(evidence.has_highlight())
|
|
|
|
# Sans highlight
|
|
data = self.evidence_data.copy()
|
|
data['highlight_box'] = None
|
|
evidence_no_highlight = VWBEvidence(**data)
|
|
self.assertFalse(evidence_no_highlight.has_highlight())
|
|
|
|
def test_get_screenshot_data_url(self):
|
|
"""Test de génération d'URL data pour screenshot."""
|
|
evidence = VWBEvidence(**self.evidence_data)
|
|
data_url = evidence.get_screenshot_data_url()
|
|
|
|
self.assertIsNotNone(data_url)
|
|
self.assertTrue(data_url.startswith('data:image/png;base64,'))
|
|
self.assertIn(self.fake_screenshot, data_url)
|
|
|
|
def test_evidence_serialization(self):
|
|
"""Test de sérialisation JSON."""
|
|
evidence = VWBEvidence(**self.evidence_data)
|
|
|
|
# Test to_dict
|
|
evidence_dict = evidence.to_dict()
|
|
self.assertEqual(evidence_dict['evidence_type'], 'screenshot_before')
|
|
self.assertIsInstance(evidence_dict['timestamp'], str)
|
|
|
|
# Test to_json
|
|
evidence_json = evidence.to_json()
|
|
self.assertIsInstance(evidence_json, str)
|
|
parsed = json.loads(evidence_json)
|
|
self.assertEqual(parsed['evidence_type'], 'screenshot_before')
|
|
|
|
def test_evidence_deserialization(self):
|
|
"""Test de désérialisation JSON."""
|
|
evidence = VWBEvidence(**self.evidence_data)
|
|
evidence_dict = evidence.to_dict()
|
|
|
|
# Test from_dict
|
|
restored_evidence = VWBEvidence.from_dict(evidence_dict)
|
|
self.assertEqual(restored_evidence.evidence_id, evidence.evidence_id)
|
|
self.assertEqual(restored_evidence.evidence_type, evidence.evidence_type)
|
|
|
|
# Test from_json
|
|
evidence_json = evidence.to_json()
|
|
restored_from_json = VWBEvidence.from_json(evidence_json)
|
|
self.assertEqual(restored_from_json.title, evidence.title)
|
|
|
|
def test_evidence_type_checks(self):
|
|
"""Test des vérifications de type d'evidence."""
|
|
evidence = VWBEvidence(**self.evidence_data)
|
|
|
|
self.assertTrue(evidence.is_visual_evidence())
|
|
self.assertFalse(evidence.is_interaction_evidence())
|
|
|
|
# Test avec evidence d'interaction
|
|
data = self.evidence_data.copy()
|
|
data['evidence_type'] = VWBEvidenceType.CLICK_EVIDENCE
|
|
interaction_evidence = VWBEvidence(**data)
|
|
|
|
self.assertFalse(interaction_evidence.is_visual_evidence())
|
|
self.assertTrue(interaction_evidence.is_interaction_evidence())
|
|
|
|
def test_file_size_calculation(self):
|
|
"""Test du calcul de taille de fichier."""
|
|
evidence = VWBEvidence(**self.evidence_data)
|
|
file_size = evidence.get_file_size_mb()
|
|
|
|
self.assertIsInstance(file_size, float)
|
|
self.assertGreater(file_size, 0)
|
|
|
|
def test_create_screenshot_evidence_utility(self):
|
|
"""Test de la fonction utilitaire create_screenshot_evidence."""
|
|
evidence = create_screenshot_evidence(
|
|
action_id='action_001',
|
|
step_id='step_001',
|
|
screenshot_base64=self.fake_screenshot,
|
|
title='Test screenshot'
|
|
)
|
|
|
|
self.assertEqual(evidence.evidence_type, VWBEvidenceType.SCREENSHOT_BEFORE)
|
|
self.assertEqual(evidence.action_id, 'action_001')
|
|
self.assertEqual(evidence.title, 'Test screenshot')
|
|
self.assertTrue(evidence.has_screenshot())
|
|
|
|
def test_create_interaction_evidence_utility(self):
|
|
"""Test de la fonction utilitaire create_interaction_evidence."""
|
|
interaction_data = {'click_x': 100, 'click_y': 200}
|
|
|
|
evidence = create_interaction_evidence(
|
|
action_id='action_001',
|
|
step_id='step_001',
|
|
evidence_type=VWBEvidenceType.CLICK_EVIDENCE,
|
|
title='Clic effectué',
|
|
interaction_data=interaction_data
|
|
)
|
|
|
|
self.assertEqual(evidence.evidence_type, VWBEvidenceType.CLICK_EVIDENCE)
|
|
self.assertEqual(evidence.data, interaction_data)
|
|
self.assertTrue(evidence.is_interaction_evidence())
|
|
|
|
|
|
class TestVWBVisualAnchor(unittest.TestCase):
|
|
"""Tests pour VWBVisualAnchor."""
|
|
|
|
def setUp(self):
|
|
"""Configuration des tests."""
|
|
self.timestamp = datetime.now()
|
|
self.fake_image = base64.b64encode(b'fake_anchor_image').decode('utf-8')
|
|
|
|
self.anchor_data = {
|
|
'anchor_id': 'anchor_test_001',
|
|
'anchor_type': VWBVisualAnchorType.IMAGE_TEMPLATE,
|
|
'name': 'Bouton Valider',
|
|
'description': 'Bouton de validation du formulaire',
|
|
'reference_image_base64': self.fake_image,
|
|
'reference_width': 120,
|
|
'reference_height': 40,
|
|
'bounding_box': {'x': 500, 'y': 300, 'width': 120, 'height': 40},
|
|
'search_criteria': {'color_tolerance': 10},
|
|
'confidence_threshold': 0.8,
|
|
'max_search_time_ms': 5000,
|
|
'retry_count': 3,
|
|
'visual_embedding': [0.1, 0.2, 0.3],
|
|
'embedding_model': 'clip-vit-base',
|
|
'created_by': 'user_test',
|
|
'created_at': self.timestamp,
|
|
'last_used_at': None,
|
|
'usage_count': 0,
|
|
'success_rate': 0.0,
|
|
'average_match_time_ms': 0.0,
|
|
'screen_resolution': (1920, 1080),
|
|
'application_context': 'web_browser',
|
|
'is_active': True,
|
|
'validation_hash': None
|
|
}
|
|
|
|
def test_anchor_creation(self):
|
|
"""Test de création d'une ancre VWB."""
|
|
anchor = VWBVisualAnchor(**self.anchor_data)
|
|
|
|
self.assertEqual(anchor.anchor_id, 'anchor_test_001')
|
|
self.assertEqual(anchor.anchor_type, VWBVisualAnchorType.IMAGE_TEMPLATE)
|
|
self.assertEqual(anchor.name, 'Bouton Valider')
|
|
self.assertEqual(anchor.confidence_threshold, 0.8)
|
|
self.assertTrue(anchor.is_active)
|
|
self.assertIsNotNone(anchor.validation_hash)
|
|
|
|
def test_anchor_auto_id_generation(self):
|
|
"""Test de génération automatique de l'ID d'ancre."""
|
|
data = self.anchor_data.copy()
|
|
data['anchor_id'] = ''
|
|
|
|
anchor = VWBVisualAnchor(**data)
|
|
|
|
self.assertTrue(anchor.anchor_id.startswith('anchor_bouton_valider_'))
|
|
|
|
def test_has_reference_image(self):
|
|
"""Test de détection d'image de référence."""
|
|
anchor = VWBVisualAnchor(**self.anchor_data)
|
|
self.assertTrue(anchor.has_reference_image())
|
|
|
|
# Sans image
|
|
data = self.anchor_data.copy()
|
|
data['reference_image_base64'] = None
|
|
anchor_no_image = VWBVisualAnchor(**data)
|
|
self.assertFalse(anchor_no_image.has_reference_image())
|
|
|
|
def test_has_bounding_box(self):
|
|
"""Test de détection de bounding box."""
|
|
anchor = VWBVisualAnchor(**self.anchor_data)
|
|
self.assertTrue(anchor.has_bounding_box())
|
|
|
|
# Sans bounding box
|
|
data = self.anchor_data.copy()
|
|
data['bounding_box'] = None
|
|
anchor_no_bbox = VWBVisualAnchor(**data)
|
|
self.assertFalse(anchor_no_bbox.has_bounding_box())
|
|
|
|
def test_has_visual_embedding(self):
|
|
"""Test de détection d'embedding visuel."""
|
|
anchor = VWBVisualAnchor(**self.anchor_data)
|
|
self.assertTrue(anchor.has_visual_embedding())
|
|
|
|
# Sans embedding
|
|
data = self.anchor_data.copy()
|
|
data['visual_embedding'] = None
|
|
anchor_no_embedding = VWBVisualAnchor(**data)
|
|
self.assertFalse(anchor_no_embedding.has_visual_embedding())
|
|
|
|
def test_update_usage_stats(self):
|
|
"""Test de mise à jour des statistiques d'utilisation."""
|
|
anchor = VWBVisualAnchor(**self.anchor_data)
|
|
|
|
# Premier usage réussi
|
|
anchor.update_usage_stats(1000.0, True)
|
|
self.assertEqual(anchor.usage_count, 1)
|
|
self.assertEqual(anchor.success_rate, 1.0)
|
|
self.assertEqual(anchor.average_match_time_ms, 1000.0)
|
|
self.assertIsNotNone(anchor.last_used_at)
|
|
|
|
# Deuxième usage échoué
|
|
anchor.update_usage_stats(2000.0, False)
|
|
self.assertEqual(anchor.usage_count, 2)
|
|
self.assertEqual(anchor.success_rate, 0.5)
|
|
self.assertEqual(anchor.average_match_time_ms, 1500.0)
|
|
|
|
def test_reliability_checks(self):
|
|
"""Test des vérifications de fiabilité."""
|
|
anchor = VWBVisualAnchor(**self.anchor_data)
|
|
|
|
# Ancre neuve - pas encore fiable
|
|
self.assertFalse(anchor.is_reliable())
|
|
self.assertFalse(anchor.needs_optimization())
|
|
|
|
# Simuler des usages réussis
|
|
for _ in range(5):
|
|
anchor.update_usage_stats(1000.0, True)
|
|
|
|
self.assertTrue(anchor.is_reliable())
|
|
self.assertFalse(anchor.needs_optimization())
|
|
|
|
# Simuler des échecs
|
|
for _ in range(10):
|
|
anchor.update_usage_stats(8000.0, False)
|
|
|
|
self.assertFalse(anchor.is_reliable())
|
|
self.assertTrue(anchor.needs_optimization())
|
|
|
|
def test_resolution_compatibility(self):
|
|
"""Test de compatibilité de résolution."""
|
|
anchor = VWBVisualAnchor(**self.anchor_data)
|
|
|
|
# Résolution identique
|
|
self.assertTrue(anchor.is_compatible_with_resolution(1920, 1080))
|
|
|
|
# Résolution proche (dans la tolérance)
|
|
self.assertTrue(anchor.is_compatible_with_resolution(1900, 1070))
|
|
|
|
# Résolution trop différente
|
|
self.assertFalse(anchor.is_compatible_with_resolution(1280, 720))
|
|
|
|
def test_search_area_calculation(self):
|
|
"""Test du calcul de zone de recherche."""
|
|
anchor = VWBVisualAnchor(**self.anchor_data)
|
|
|
|
# Même résolution
|
|
search_area = anchor.get_search_area(1920, 1080)
|
|
self.assertEqual(search_area, anchor.bounding_box)
|
|
|
|
# Résolution différente (scaling)
|
|
search_area_scaled = anchor.get_search_area(960, 540) # Moitié
|
|
expected = {
|
|
'x': 250, # 500 / 2
|
|
'y': 150, # 300 / 2
|
|
'width': 60, # 120 / 2
|
|
'height': 20 # 40 / 2
|
|
}
|
|
self.assertEqual(search_area_scaled, expected)
|
|
|
|
def test_anchor_serialization(self):
|
|
"""Test de sérialisation JSON."""
|
|
anchor = VWBVisualAnchor(**self.anchor_data)
|
|
|
|
# Test to_dict
|
|
anchor_dict = anchor.to_dict()
|
|
self.assertEqual(anchor_dict['anchor_type'], 'image_template')
|
|
self.assertIsInstance(anchor_dict['created_at'], str)
|
|
|
|
# Test to_json
|
|
anchor_json = anchor.to_json()
|
|
self.assertIsInstance(anchor_json, str)
|
|
parsed = json.loads(anchor_json)
|
|
self.assertEqual(parsed['anchor_type'], 'image_template')
|
|
|
|
def test_anchor_deserialization(self):
|
|
"""Test de désérialisation JSON."""
|
|
anchor = VWBVisualAnchor(**self.anchor_data)
|
|
anchor_dict = anchor.to_dict()
|
|
|
|
# Test from_dict
|
|
restored_anchor = VWBVisualAnchor.from_dict(anchor_dict)
|
|
self.assertEqual(restored_anchor.anchor_id, anchor.anchor_id)
|
|
self.assertEqual(restored_anchor.anchor_type, anchor.anchor_type)
|
|
|
|
# Test from_json
|
|
anchor_json = anchor.to_json()
|
|
restored_from_json = VWBVisualAnchor.from_json(anchor_json)
|
|
self.assertEqual(restored_from_json.name, anchor.name)
|
|
|
|
def test_create_image_anchor_utility(self):
|
|
"""Test de la fonction utilitaire create_image_anchor."""
|
|
anchor = create_image_anchor(
|
|
name='Test Button',
|
|
reference_image_base64=self.fake_image,
|
|
created_by='user_test',
|
|
confidence_threshold=0.9
|
|
)
|
|
|
|
self.assertEqual(anchor.anchor_type, VWBVisualAnchorType.IMAGE_TEMPLATE)
|
|
self.assertEqual(anchor.name, 'Test Button')
|
|
self.assertEqual(anchor.confidence_threshold, 0.9)
|
|
self.assertTrue(anchor.has_reference_image())
|
|
|
|
def test_create_text_anchor_utility(self):
|
|
"""Test de la fonction utilitaire create_text_anchor."""
|
|
anchor = create_text_anchor(
|
|
name='Submit Text',
|
|
text_pattern='Valider',
|
|
created_by='user_test'
|
|
)
|
|
|
|
self.assertEqual(anchor.anchor_type, VWBVisualAnchorType.TEXT_EXACT)
|
|
self.assertEqual(anchor.name, 'Submit Text')
|
|
self.assertEqual(anchor.search_criteria['text_pattern'], 'Valider')
|
|
|
|
|
|
class TestContractsIntegration(unittest.TestCase):
|
|
"""Tests d'intégration entre les contrats VWB."""
|
|
|
|
def test_error_evidence_relationship(self):
|
|
"""Test de relation entre erreur et evidence."""
|
|
# Créer une erreur
|
|
error = create_vwb_error(
|
|
error_type=VWBErrorType.ELEMENT_NOT_FOUND,
|
|
message='Élément non trouvé',
|
|
action_id='action_001',
|
|
step_id='step_001'
|
|
)
|
|
|
|
# Créer une evidence d'erreur liée
|
|
evidence = create_screenshot_evidence(
|
|
action_id=error.action_id,
|
|
step_id=error.step_id,
|
|
screenshot_base64=base64.b64encode(b'error_screenshot').decode('utf-8'),
|
|
evidence_type=VWBEvidenceType.SCREENSHOT_ERROR,
|
|
title='Screenshot d\'erreur'
|
|
)
|
|
|
|
# Vérifier la cohérence
|
|
self.assertEqual(error.action_id, evidence.action_id)
|
|
self.assertEqual(error.step_id, evidence.step_id)
|
|
self.assertEqual(evidence.evidence_type, VWBEvidenceType.SCREENSHOT_ERROR)
|
|
|
|
def test_anchor_evidence_workflow(self):
|
|
"""Test de workflow ancre → evidence."""
|
|
# Créer une ancre
|
|
anchor = create_image_anchor(
|
|
name='Login Button',
|
|
reference_image_base64=base64.b64encode(b'button_image').decode('utf-8'),
|
|
created_by='user_test'
|
|
)
|
|
|
|
# Simuler une utilisation réussie avec evidence
|
|
evidence = create_interaction_evidence(
|
|
action_id='click_login',
|
|
step_id='step_001',
|
|
evidence_type=VWBEvidenceType.CLICK_EVIDENCE,
|
|
title='Clic sur bouton login',
|
|
interaction_data={
|
|
'anchor_id': anchor.anchor_id,
|
|
'click_coordinates': {'x': 100, 'y': 200},
|
|
'match_confidence': 0.95
|
|
}
|
|
)
|
|
|
|
# Mettre à jour les stats de l'ancre
|
|
anchor.update_usage_stats(1200.0, True)
|
|
|
|
# Vérifications
|
|
self.assertEqual(evidence.data['anchor_id'], anchor.anchor_id)
|
|
self.assertEqual(anchor.usage_count, 1)
|
|
self.assertEqual(anchor.success_rate, 1.0)
|
|
self.assertTrue(evidence.success)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
# Configuration des tests
|
|
unittest.TestCase.maxDiff = None
|
|
|
|
# Exécution des tests
|
|
print("=" * 70)
|
|
print(" TESTS UNITAIRES - CONTRATS DE DONNÉES VWB")
|
|
print("=" * 70)
|
|
print("Auteur : Dom, Alice, Kiro - 09 janvier 2026")
|
|
print("")
|
|
|
|
# Créer la suite de tests
|
|
loader = unittest.TestLoader()
|
|
suite = unittest.TestSuite()
|
|
|
|
# Ajouter les classes de tests
|
|
suite.addTests(loader.loadTestsFromTestCase(TestVWBActionError))
|
|
suite.addTests(loader.loadTestsFromTestCase(TestVWBEvidence))
|
|
suite.addTests(loader.loadTestsFromTestCase(TestVWBVisualAnchor))
|
|
suite.addTests(loader.loadTestsFromTestCase(TestContractsIntegration))
|
|
|
|
# Exécuter les tests
|
|
runner = unittest.TextTestRunner(verbosity=2)
|
|
result = runner.run(suite)
|
|
|
|
# Résumé
|
|
print("\n" + "=" * 70)
|
|
print(f"RÉSUMÉ DES TESTS CONTRATS VWB")
|
|
print("=" * 70)
|
|
print(f"Tests exécutés : {result.testsRun}")
|
|
print(f"Succès : {result.testsRun - len(result.failures) - len(result.errors)}")
|
|
print(f"Échecs : {len(result.failures)}")
|
|
print(f"Erreurs : {len(result.errors)}")
|
|
|
|
if result.failures:
|
|
print("\nÉCHECS :")
|
|
for test, traceback in result.failures:
|
|
print(f"- {test}: {traceback}")
|
|
|
|
if result.errors:
|
|
print("\nERREURS :")
|
|
for test, traceback in result.errors:
|
|
print(f"- {test}: {traceback}")
|
|
|
|
success_rate = ((result.testsRun - len(result.failures) - len(result.errors)) / result.testsRun) * 100
|
|
print(f"\nTaux de succès : {success_rate:.1f}%")
|
|
|
|
if success_rate == 100.0:
|
|
print("🎉 TOUS LES TESTS CONTRATS VWB SONT RÉUSSIS !")
|
|
else:
|
|
print("⚠️ Certains tests ont échoué - vérification nécessaire") |