- 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>
227 lines
6.9 KiB
Python
227 lines
6.9 KiB
Python
"""Unit tests for self-healing workflows."""
|
|
|
|
import pytest
|
|
import tempfile
|
|
import shutil
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
|
|
from core.healing.healing_engine import SelfHealingEngine
|
|
from core.healing.learning_repository import LearningRepository
|
|
from core.healing.confidence_scorer import ConfidenceScorer
|
|
from core.healing.models import RecoveryContext, RecoveryResult, RecoveryPattern
|
|
from core.healing.strategies import (
|
|
SemanticVariantStrategy,
|
|
SpatialFallbackStrategy,
|
|
TimingAdaptationStrategy,
|
|
FormatTransformationStrategy
|
|
)
|
|
|
|
|
|
class TestConfidenceScorer:
|
|
"""Tests for confidence scorer."""
|
|
|
|
def test_confidence_score_range(self):
|
|
"""Confidence scores should be between 0 and 1."""
|
|
scorer = ConfidenceScorer()
|
|
|
|
context = RecoveryContext(
|
|
original_action='click',
|
|
target_element='button',
|
|
failure_reason='element_not_found',
|
|
screenshot_path='/tmp/test.png',
|
|
workflow_id='test_wf',
|
|
node_id='node1',
|
|
attempt_count=1
|
|
)
|
|
|
|
confidence = scorer.calculate_recovery_confidence(
|
|
'semantic_variant',
|
|
context,
|
|
0.5
|
|
)
|
|
|
|
assert 0.0 <= confidence <= 1.0
|
|
|
|
def test_text_similarity(self):
|
|
"""Text similarity should work correctly."""
|
|
scorer = ConfidenceScorer()
|
|
|
|
# Exact match
|
|
assert scorer._text_similarity('submit', 'submit') == 1.0
|
|
|
|
# Similar
|
|
similarity = scorer._text_similarity('submit', 'send')
|
|
assert 0.0 < similarity < 1.0
|
|
|
|
# Different
|
|
similarity = scorer._text_similarity('submit', 'xyz')
|
|
assert similarity < 0.5
|
|
|
|
|
|
class TestLearningRepository:
|
|
"""Tests for learning repository."""
|
|
|
|
def setup_method(self):
|
|
"""Setup test repository."""
|
|
self.temp_dir = tempfile.mkdtemp()
|
|
self.repo = LearningRepository(Path(self.temp_dir))
|
|
|
|
def teardown_method(self):
|
|
"""Cleanup test repository."""
|
|
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
|
|
|
def test_store_and_retrieve_pattern(self):
|
|
"""Should store and retrieve patterns."""
|
|
context = RecoveryContext(
|
|
original_action='click',
|
|
target_element='button',
|
|
failure_reason='element_not_found',
|
|
screenshot_path='/tmp/test.png',
|
|
workflow_id='test_wf',
|
|
node_id='node1',
|
|
attempt_count=1
|
|
)
|
|
|
|
result = RecoveryResult(
|
|
success=True,
|
|
strategy_used='semantic_variant',
|
|
confidence_score=0.85
|
|
)
|
|
|
|
# Store pattern
|
|
self.repo.store_pattern(context, result)
|
|
|
|
# Retrieve patterns
|
|
patterns = self.repo.get_all_patterns()
|
|
assert len(patterns) == 1
|
|
assert patterns[0].recovery_strategy == 'semantic_variant'
|
|
|
|
def test_pattern_matching(self):
|
|
"""Should match patterns correctly."""
|
|
context1 = RecoveryContext(
|
|
original_action='click',
|
|
target_element='button1',
|
|
failure_reason='element_not_found',
|
|
screenshot_path='/tmp/test.png',
|
|
workflow_id='test_wf',
|
|
node_id='node1',
|
|
attempt_count=1,
|
|
metadata={'element_type': 'button'}
|
|
)
|
|
|
|
result = RecoveryResult(
|
|
success=True,
|
|
strategy_used='semantic_variant',
|
|
confidence_score=0.85
|
|
)
|
|
|
|
self.repo.store_pattern(context1, result)
|
|
|
|
# Similar context
|
|
context2 = RecoveryContext(
|
|
original_action='click',
|
|
target_element='button2',
|
|
failure_reason='element_not_found',
|
|
screenshot_path='/tmp/test2.png',
|
|
workflow_id='test_wf',
|
|
node_id='node2',
|
|
attempt_count=1,
|
|
metadata={'element_type': 'button'}
|
|
)
|
|
|
|
matching = self.repo.get_matching_patterns(context2)
|
|
assert len(matching) > 0
|
|
|
|
|
|
class TestSemanticVariantStrategy:
|
|
"""Tests for semantic variant strategy."""
|
|
|
|
def test_can_handle(self):
|
|
"""Should handle element_not_found failures."""
|
|
strategy = SemanticVariantStrategy()
|
|
|
|
context = RecoveryContext(
|
|
original_action='click',
|
|
target_element='button',
|
|
failure_reason='element_not_found',
|
|
screenshot_path='/tmp/test.png',
|
|
workflow_id='test_wf',
|
|
node_id='node1',
|
|
attempt_count=1
|
|
)
|
|
|
|
assert strategy.can_handle(context)
|
|
|
|
def test_get_semantic_variants(self):
|
|
"""Should get semantic variants."""
|
|
strategy = SemanticVariantStrategy()
|
|
|
|
variants = strategy._get_semantic_variants('submit')
|
|
assert 'send' in variants
|
|
assert 'ok' in variants
|
|
assert 'confirm' in variants
|
|
|
|
|
|
class TestSelfHealingEngine:
|
|
"""Tests for self-healing engine."""
|
|
|
|
def setup_method(self):
|
|
"""Setup test engine."""
|
|
self.temp_dir = tempfile.mkdtemp()
|
|
self.engine = SelfHealingEngine(storage_path=Path(self.temp_dir))
|
|
|
|
def teardown_method(self):
|
|
"""Cleanup test engine."""
|
|
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
|
|
|
def test_initialization(self):
|
|
"""Engine should initialize correctly."""
|
|
assert self.engine.learning_repo is not None
|
|
assert self.engine.confidence_scorer is not None
|
|
assert len(self.engine.recovery_strategies) > 0
|
|
|
|
def test_max_attempts_exceeded(self):
|
|
"""Should fail when max attempts exceeded."""
|
|
context = RecoveryContext(
|
|
original_action='click',
|
|
target_element='button',
|
|
failure_reason='element_not_found',
|
|
screenshot_path='/tmp/test.png',
|
|
workflow_id='test_wf',
|
|
node_id='node1',
|
|
attempt_count=5,
|
|
max_attempts=3
|
|
)
|
|
|
|
result = self.engine.attempt_recovery(context)
|
|
assert not result.success
|
|
assert 'Max recovery attempts' in result.error_message
|
|
|
|
def test_learn_from_success(self):
|
|
"""Should learn from successful recovery."""
|
|
context = RecoveryContext(
|
|
original_action='click',
|
|
target_element='button',
|
|
failure_reason='element_not_found',
|
|
screenshot_path='/tmp/test.png',
|
|
workflow_id='test_wf',
|
|
node_id='node1',
|
|
attempt_count=1
|
|
)
|
|
|
|
result = RecoveryResult(
|
|
success=True,
|
|
strategy_used='semantic_variant',
|
|
confidence_score=0.85
|
|
)
|
|
|
|
self.engine.learn_from_success(context, result)
|
|
|
|
patterns = self.engine.learning_repo.get_all_patterns()
|
|
assert len(patterns) > 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
pytest.main([__file__, '-v'])
|