- 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>
333 lines
11 KiB
Python
333 lines
11 KiB
Python
"""
|
|
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
|