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:
332
tests/unit/test_screen_state.py
Normal file
332
tests/unit/test_screen_state.py
Normal file
@@ -0,0 +1,332 @@
|
||||
"""
|
||||
Tests unitaires pour ScreenState
|
||||
|
||||
Property 2: ScreenState Multi-Level Consistency
|
||||
Validates: Requirements 2.1, 2.2, 2.3, 2.4, 2.5
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
|
||||
from core.models.screen_state import (
|
||||
ScreenState,
|
||||
RawLevel,
|
||||
PerceptionLevel,
|
||||
ContextLevel,
|
||||
WindowContext,
|
||||
EmbeddingRef
|
||||
)
|
||||
|
||||
|
||||
class TestEmbeddingRef:
|
||||
"""Tests pour EmbeddingRef"""
|
||||
|
||||
def test_create_embedding_ref(self):
|
||||
"""Test création EmbeddingRef"""
|
||||
emb_ref = EmbeddingRef(
|
||||
provider="openclip_ViT-B-32",
|
||||
vector_id="data/embeddings/test.npy",
|
||||
dimensions=512
|
||||
)
|
||||
|
||||
assert emb_ref.provider == "openclip_ViT-B-32"
|
||||
assert emb_ref.dimensions == 512
|
||||
|
||||
def test_embedding_ref_serialization(self):
|
||||
"""Test sérialisation/désérialisation EmbeddingRef"""
|
||||
emb_ref = EmbeddingRef(
|
||||
provider="openclip_ViT-B-32",
|
||||
vector_id="data/embeddings/test.npy",
|
||||
dimensions=512
|
||||
)
|
||||
|
||||
data = emb_ref.to_dict()
|
||||
emb_ref2 = EmbeddingRef.from_dict(data)
|
||||
|
||||
assert emb_ref2.provider == emb_ref.provider
|
||||
assert emb_ref2.vector_id == emb_ref.vector_id
|
||||
assert emb_ref2.dimensions == emb_ref.dimensions
|
||||
|
||||
|
||||
class TestRawLevel:
|
||||
"""Tests pour RawLevel"""
|
||||
|
||||
def test_create_raw_level(self):
|
||||
"""Test création RawLevel"""
|
||||
raw = RawLevel(
|
||||
screenshot_path="data/screens/test.png",
|
||||
capture_method="mss",
|
||||
file_size_bytes=245678
|
||||
)
|
||||
|
||||
assert raw.screenshot_path == "data/screens/test.png"
|
||||
assert raw.capture_method == "mss"
|
||||
assert raw.file_size_bytes == 245678
|
||||
|
||||
def test_raw_level_serialization(self):
|
||||
"""Test sérialisation/désérialisation RawLevel"""
|
||||
raw = RawLevel(
|
||||
screenshot_path="data/screens/test.png",
|
||||
capture_method="mss",
|
||||
file_size_bytes=245678
|
||||
)
|
||||
|
||||
data = raw.to_dict()
|
||||
raw2 = RawLevel.from_dict(data)
|
||||
|
||||
assert raw2.screenshot_path == raw.screenshot_path
|
||||
assert raw2.capture_method == raw.capture_method
|
||||
assert raw2.file_size_bytes == raw.file_size_bytes
|
||||
|
||||
|
||||
class TestPerceptionLevel:
|
||||
"""Tests pour PerceptionLevel"""
|
||||
|
||||
def test_create_perception_level(self):
|
||||
"""Test création PerceptionLevel"""
|
||||
emb_ref = EmbeddingRef(
|
||||
provider="openclip_ViT-B-32",
|
||||
vector_id="data/embeddings/test.npy",
|
||||
dimensions=512
|
||||
)
|
||||
|
||||
perception = PerceptionLevel(
|
||||
embedding=emb_ref,
|
||||
detected_text=["Button", "Submit", "Cancel"],
|
||||
text_detection_method="qwen_vl",
|
||||
confidence_avg=0.92
|
||||
)
|
||||
|
||||
assert len(perception.detected_text) == 3
|
||||
assert perception.confidence_avg == 0.92
|
||||
|
||||
def test_perception_level_serialization(self):
|
||||
"""Test sérialisation/désérialisation PerceptionLevel"""
|
||||
emb_ref = EmbeddingRef(
|
||||
provider="openclip_ViT-B-32",
|
||||
vector_id="data/embeddings/test.npy",
|
||||
dimensions=512
|
||||
)
|
||||
|
||||
perception = PerceptionLevel(
|
||||
embedding=emb_ref,
|
||||
detected_text=["Button", "Submit"],
|
||||
text_detection_method="qwen_vl",
|
||||
confidence_avg=0.92
|
||||
)
|
||||
|
||||
data = perception.to_dict()
|
||||
perception2 = PerceptionLevel.from_dict(data)
|
||||
|
||||
assert perception2.detected_text == perception.detected_text
|
||||
assert perception2.confidence_avg == perception.confidence_avg
|
||||
assert perception2.embedding.provider == perception.embedding.provider
|
||||
|
||||
|
||||
class TestContextLevel:
|
||||
"""Tests pour ContextLevel"""
|
||||
|
||||
def test_create_context_level(self):
|
||||
"""Test création ContextLevel"""
|
||||
context = ContextLevel(
|
||||
current_workflow_candidate="WF_test",
|
||||
workflow_step=2,
|
||||
user_id="test_user",
|
||||
tags=["test", "demo"],
|
||||
business_variables={"customer": "Test Corp"}
|
||||
)
|
||||
|
||||
assert context.current_workflow_candidate == "WF_test"
|
||||
assert context.workflow_step == 2
|
||||
assert len(context.tags) == 2
|
||||
|
||||
def test_context_level_defaults(self):
|
||||
"""Test valeurs par défaut ContextLevel"""
|
||||
context = ContextLevel()
|
||||
|
||||
assert context.current_workflow_candidate is None
|
||||
assert context.workflow_step is None
|
||||
assert context.user_id == ""
|
||||
assert context.tags == []
|
||||
assert context.business_variables == {}
|
||||
|
||||
def test_context_level_serialization(self):
|
||||
"""Test sérialisation/désérialisation ContextLevel"""
|
||||
context = ContextLevel(
|
||||
current_workflow_candidate="WF_test",
|
||||
user_id="test_user",
|
||||
tags=["test"]
|
||||
)
|
||||
|
||||
data = context.to_dict()
|
||||
context2 = ContextLevel.from_dict(data)
|
||||
|
||||
assert context2.current_workflow_candidate == context.current_workflow_candidate
|
||||
assert context2.user_id == context.user_id
|
||||
assert context2.tags == context.tags
|
||||
|
||||
|
||||
class TestWindowContext:
|
||||
"""Tests pour WindowContext"""
|
||||
|
||||
def test_create_window_context(self):
|
||||
"""Test création WindowContext"""
|
||||
window = WindowContext(
|
||||
app_name="test_app",
|
||||
window_title="Test Window",
|
||||
screen_resolution=[1920, 1080],
|
||||
workspace="main"
|
||||
)
|
||||
|
||||
assert window.app_name == "test_app"
|
||||
assert window.screen_resolution == [1920, 1080]
|
||||
|
||||
def test_window_context_serialization(self):
|
||||
"""Test sérialisation/désérialisation WindowContext"""
|
||||
window = WindowContext(
|
||||
app_name="test_app",
|
||||
window_title="Test Window",
|
||||
screen_resolution=[1920, 1080]
|
||||
)
|
||||
|
||||
data = window.to_dict()
|
||||
window2 = WindowContext.from_dict(data)
|
||||
|
||||
assert window2.app_name == window.app_name
|
||||
assert window2.window_title == window.window_title
|
||||
assert window2.screen_resolution == window.screen_resolution
|
||||
|
||||
|
||||
class TestScreenState:
|
||||
"""Tests pour ScreenState"""
|
||||
|
||||
def create_test_screen_state(self) -> ScreenState:
|
||||
"""Helper pour créer un ScreenState de test"""
|
||||
window = WindowContext(
|
||||
app_name="test_app",
|
||||
window_title="Test Window",
|
||||
screen_resolution=[1920, 1080]
|
||||
)
|
||||
|
||||
raw = RawLevel(
|
||||
screenshot_path="data/screens/test.png",
|
||||
capture_method="mss",
|
||||
file_size_bytes=245678
|
||||
)
|
||||
|
||||
emb_ref = EmbeddingRef(
|
||||
provider="openclip_ViT-B-32",
|
||||
vector_id="data/embeddings/test.npy",
|
||||
dimensions=512
|
||||
)
|
||||
|
||||
perception = PerceptionLevel(
|
||||
embedding=emb_ref,
|
||||
detected_text=["Button", "Submit"],
|
||||
text_detection_method="qwen_vl",
|
||||
confidence_avg=0.92
|
||||
)
|
||||
|
||||
context = ContextLevel(
|
||||
user_id="test_user",
|
||||
tags=["test"]
|
||||
)
|
||||
|
||||
return ScreenState(
|
||||
screen_state_id="screen_test_001",
|
||||
timestamp=datetime(2025, 11, 22, 10, 15, 32),
|
||||
session_id="sess_test_001",
|
||||
window=window,
|
||||
raw=raw,
|
||||
perception=perception,
|
||||
context=context,
|
||||
metadata={"test": "value"}
|
||||
)
|
||||
|
||||
def test_create_screen_state(self):
|
||||
"""Test création ScreenState"""
|
||||
screen_state = self.create_test_screen_state()
|
||||
|
||||
assert screen_state.screen_state_id == "screen_test_001"
|
||||
assert screen_state.session_id == "sess_test_001"
|
||||
assert screen_state.raw is not None
|
||||
assert screen_state.perception is not None
|
||||
assert screen_state.context is not None
|
||||
|
||||
def test_screen_state_to_json(self):
|
||||
"""Test sérialisation JSON"""
|
||||
screen_state = self.create_test_screen_state()
|
||||
|
||||
data = screen_state.to_json()
|
||||
|
||||
assert data["screen_state_id"] == "screen_test_001"
|
||||
assert data["timestamp"] == "2025-11-22T10:15:32"
|
||||
assert "raw" in data
|
||||
assert "perception" in data
|
||||
assert "context" in data
|
||||
assert "window" in data
|
||||
|
||||
def test_screen_state_from_json(self):
|
||||
"""Test désérialisation JSON"""
|
||||
screen_state = self.create_test_screen_state()
|
||||
|
||||
data = screen_state.to_json()
|
||||
screen_state2 = ScreenState.from_json(data)
|
||||
|
||||
assert screen_state2.screen_state_id == screen_state.screen_state_id
|
||||
assert screen_state2.timestamp == screen_state.timestamp
|
||||
assert screen_state2.session_id == screen_state.session_id
|
||||
|
||||
def test_screen_state_round_trip(self):
|
||||
"""Test round trip sérialisation/désérialisation"""
|
||||
screen_state = self.create_test_screen_state()
|
||||
|
||||
data = screen_state.to_json()
|
||||
screen_state2 = ScreenState.from_json(data)
|
||||
|
||||
# Vérifier tous les niveaux
|
||||
assert screen_state2.raw.screenshot_path == screen_state.raw.screenshot_path
|
||||
assert screen_state2.perception.confidence_avg == screen_state.perception.confidence_avg
|
||||
assert screen_state2.context.user_id == screen_state.context.user_id
|
||||
assert screen_state2.window.app_name == screen_state.window.app_name
|
||||
|
||||
def test_screen_state_validate_consistency(self):
|
||||
"""
|
||||
Property 2: ScreenState Multi-Level Consistency
|
||||
|
||||
Pour tout ScreenState, les 4 niveaux doivent référencer
|
||||
le même screenshot et timestamp.
|
||||
|
||||
Validates: Requirements 2.1, 2.2, 2.3, 2.4, 2.5
|
||||
"""
|
||||
screen_state = self.create_test_screen_state()
|
||||
|
||||
# Tous les niveaux doivent être présents
|
||||
assert screen_state.validate_consistency()
|
||||
|
||||
# Vérifier que tous les niveaux existent
|
||||
assert screen_state.raw is not None
|
||||
assert screen_state.perception is not None
|
||||
assert screen_state.context is not None
|
||||
assert screen_state.window is not None
|
||||
|
||||
# Vérifier cohérence du timestamp
|
||||
assert screen_state.timestamp is not None
|
||||
|
||||
def test_save_and_load_file(self):
|
||||
"""Test sauvegarde et chargement depuis fichier"""
|
||||
screen_state = self.create_test_screen_state()
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
filepath = Path(tmpdir) / "test_screen_state.json"
|
||||
|
||||
# Sauvegarder
|
||||
screen_state.save_to_file(filepath)
|
||||
assert filepath.exists()
|
||||
|
||||
# Charger
|
||||
screen_state2 = ScreenState.load_from_file(filepath)
|
||||
assert screen_state2.screen_state_id == screen_state.screen_state_id
|
||||
assert screen_state2.timestamp == screen_state.timestamp
|
||||
Reference in New Issue
Block a user