""" 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