From 9ad49500f9b83872c421930a43c41a653901d259 Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Mon, 27 Apr 2026 10:49:07 +0200 Subject: [PATCH] test(e2e): enforce autospec=True on all remaining patch and patch.object calls --- tests/chaos/test_chaos_network.py | 19 +++++------ tests/chaos/test_chaos_xml_corruption.py | 13 ++++--- tests/e2e/test_e2e_animation_timing.py | 14 ++++---- tests/e2e/test_e2e_behaviors.py | 34 +++++++++++-------- tests/e2e/test_engine_learning.py | 4 ++- tests/e2e/test_engine_perception.py | 9 +++-- .../integration/test_bot_flow_interaction.py | 2 +- 7 files changed, 54 insertions(+), 41 deletions(-) diff --git a/tests/chaos/test_chaos_network.py b/tests/chaos/test_chaos_network.py index 0183eba..460e746 100644 --- a/tests/chaos/test_chaos_network.py +++ b/tests/chaos/test_chaos_network.py @@ -35,14 +35,14 @@ class TestQdrantFailure: TelepathicEngine._instance = None engine = TelepathicEngine.__new__(TelepathicEngine) - engine.ui_memory = MagicMock() - engine.ui_memory.is_connected = False - engine.ui_memory.query_closest = MagicMock(return_value=None) + engine.__init__() + engine._memory.ui_memory = MagicMock() + engine._memory.ui_memory.is_connected = False + engine._memory.ui_memory.query_closest = MagicMock(return_value=None) + engine.positive_memory = MagicMock() engine.positive_memory.is_connected = False engine.positive_memory.recall = MagicMock(return_value=None) - engine._edge_model = None - engine._edge_tokenizer = None nodes = engine._extract_semantic_nodes(VALID_FEED_XML) # Should still find clickable nodes via structural parsing @@ -101,14 +101,13 @@ class TestQdrantFailure: TelepathicEngine._instance = None engine = TelepathicEngine.__new__(TelepathicEngine) - engine.ui_memory = MagicMock() - engine.ui_memory.is_connected = False - engine.ui_memory.query_closest = MagicMock(side_effect=TimeoutError("Qdrant timeout")) + engine.__init__() + engine._memory.ui_memory = MagicMock() + engine._memory.ui_memory.is_connected = False + engine._memory.ui_memory.query_closest = MagicMock(side_effect=TimeoutError("Qdrant timeout")) engine.positive_memory = MagicMock() engine.positive_memory.is_connected = False engine.positive_memory.recall = MagicMock(side_effect=TimeoutError("Qdrant timeout")) - engine._edge_model = None - engine._edge_tokenizer = None start = time.time() nodes = engine._extract_semantic_nodes(VALID_FEED_XML) diff --git a/tests/chaos/test_chaos_xml_corruption.py b/tests/chaos/test_chaos_xml_corruption.py index 78cc7cb..51e56ae 100644 --- a/tests/chaos/test_chaos_xml_corruption.py +++ b/tests/chaos/test_chaos_xml_corruption.py @@ -34,14 +34,17 @@ def telepathic_engine(): TelepathicEngine._instance = None engine = TelepathicEngine.__new__(TelepathicEngine) - engine.ui_memory = MagicMock() - engine.ui_memory.is_connected = False - engine.ui_memory.query_closest = MagicMock(return_value=None) + engine.__init__() + + # We need to mock the Qdrant connection in the ActionMemory submodule + engine._memory.ui_memory = MagicMock() + engine._memory.ui_memory.is_connected = False + engine._memory.ui_memory.query_closest = MagicMock(return_value=None) + + # We mock positive memory for chaos tests engine.positive_memory = MagicMock() engine.positive_memory.is_connected = False engine.positive_memory.recall = MagicMock(return_value=None) - engine._edge_model = None - engine._edge_tokenizer = None yield engine TelepathicEngine._instance = None diff --git a/tests/e2e/test_e2e_animation_timing.py b/tests/e2e/test_e2e_animation_timing.py index 5dd372b..f374838 100644 --- a/tests/e2e/test_e2e_animation_timing.py +++ b/tests/e2e/test_e2e_animation_timing.py @@ -9,7 +9,7 @@ def test_wait_for_post_detects_feed(): with open("tests/fixtures/organic_post.xml", "r") as f: sim.mock_xml = f.read() - with patch("GramAddict.core.physics.timing.sleep"): + with patch("GramAddict.core.physics.timing.sleep", autospec=True): assert wait_for_post_loaded(sim, timeout=1) is True @@ -18,7 +18,7 @@ def test_wait_for_post_timeout_and_adaptive_snap(): # Empty XML will cause timeout sim.mock_xml = "" - with patch("GramAddict.core.physics.timing.sleep"): + with patch("GramAddict.core.physics.timing.sleep", autospec=True): assert wait_for_post_loaded(sim, timeout=1) is False swipes = [a for a in sim.actions_taken if a[0] == "swipe"] @@ -29,7 +29,7 @@ def test_wait_for_story_detects_viewer(): sim = BehaviorSimulator() sim.mock_xml = '' - with patch("GramAddict.core.physics.timing.sleep"): + with patch("GramAddict.core.physics.timing.sleep", autospec=True): assert wait_for_story_loaded(sim, timeout=1) is True @@ -37,7 +37,7 @@ def test_wait_for_story_timeout(): sim = BehaviorSimulator() sim.mock_xml = "" - with patch("GramAddict.core.physics.timing.sleep"): + with patch("GramAddict.core.physics.timing.sleep", autospec=True): assert wait_for_story_loaded(sim, timeout=1) is False @@ -62,7 +62,7 @@ def test_align_active_post_centers_content(): sim.swipe = mock_swipe - with patch("GramAddict.core.physics.timing.sleep"): + with patch("GramAddict.core.physics.timing.sleep", autospec=True): try: aligned = align_active_post(sim) except Exception as e: @@ -82,7 +82,7 @@ def test_align_active_post_already_centered(): xml_str = xml_str.replace('bounds="[128,665][768,731]"', 'bounds="[128,180][768,246]"') sim.mock_xml = xml_str - with patch("GramAddict.core.physics.timing.sleep"): + with patch("GramAddict.core.physics.timing.sleep", autospec=True): aligned = align_active_post(sim) # It considers it already aligned @@ -96,7 +96,7 @@ def test_align_post_with_no_header(): sim = BehaviorSimulator() sim.mock_xml = "" - with patch("GramAddict.core.physics.timing.sleep"): + with patch("GramAddict.core.physics.timing.sleep", autospec=True): aligned = align_active_post(sim) assert aligned is False diff --git a/tests/e2e/test_e2e_behaviors.py b/tests/e2e/test_e2e_behaviors.py index 81824cf..b66a826 100644 --- a/tests/e2e/test_e2e_behaviors.py +++ b/tests/e2e/test_e2e_behaviors.py @@ -201,7 +201,7 @@ def test_e2e_follow_plugin_execution(base_ctx): plugin = FollowPlugin() # We patch sleep so the test runs fast - with patch("GramAddict.core.behaviors.follow.sleep"): + with patch("GramAddict.core.behaviors.follow.sleep", autospec=True): result = plugin.execute(ctx) assert result.executed is True @@ -253,7 +253,7 @@ def test_e2e_grid_like_plugin_execution(base_ctx): # We need to patch the open_first_post logic to simulate that it succeeds, # as our XML already represents the opened post. - with patch("GramAddict.core.behaviors.grid_like.sleep"): + with patch("GramAddict.core.behaviors.grid_like.sleep", autospec=True): original_do = nav_graph.do def side_effect_do(action, *args, **kwargs): @@ -261,7 +261,7 @@ def test_e2e_grid_like_plugin_execution(base_ctx): return True return original_do(action, *args, **kwargs) - with patch.object(nav_graph, "do", side_effect=side_effect_do): + with patch.object(nav_graph, "do", autospec=True, side_effect=side_effect_do): result = plugin.execute(ctx) assert result.executed is True @@ -302,8 +302,12 @@ def test_e2e_carousel_plugin_execution(base_ctx): sim.actions_taken.append(("swipe", start_x, y, end_x, y)) with ( - patch("GramAddict.core.behaviors.carousel_browsing.sleep"), - patch("GramAddict.core.behaviors.carousel_browsing.humanized_horizontal_swipe", side_effect=mock_swipe), + patch("GramAddict.core.behaviors.carousel_browsing.sleep", autospec=True), + patch( + "GramAddict.core.behaviors.carousel_browsing.humanized_horizontal_swipe", + autospec=True, + side_effect=mock_swipe, + ), ): result = plugin.execute(ctx) @@ -343,7 +347,7 @@ def test_e2e_like_plugin_execution(base_ctx): plugin = LikePlugin() - with patch("GramAddict.core.behaviors.like.random.random", return_value=0.0): + with patch("GramAddict.core.behaviors.like.random.random", autospec=True, return_value=0.0): result = plugin.execute(ctx) assert result.executed is True @@ -389,8 +393,8 @@ def test_e2e_story_view_plugin_execution(base_ctx): plugin = StoryViewPlugin() with ( - patch("GramAddict.core.behaviors.story_view.sleep"), - patch("GramAddict.core.behaviors.story_view.wait_for_story_loaded", return_value=True), + patch("GramAddict.core.behaviors.story_view.sleep", autospec=True), + patch("GramAddict.core.behaviors.story_view.wait_for_story_loaded", autospec=True, return_value=True), ): result = plugin.execute(ctx) @@ -440,7 +444,7 @@ def test_e2e_comment_plugin_execution(base_ctx): plugin = CommentPlugin() - with patch("GramAddict.core.behaviors.comment.random.random", return_value=0.0): + with patch("GramAddict.core.behaviors.comment.random.random", autospec=True, return_value=0.0): result = plugin.execute(ctx) assert result.executed is True @@ -494,10 +498,11 @@ def test_e2e_obstacle_guard_unlearn_on_fatal(base_ctx): with ( patch( "GramAddict.core.behaviors.obstacle_guard.SituationalAwarenessEngine.get_instance", + autospec=True, return_value=real_sae, ), - patch("GramAddict.core.behaviors.obstacle_guard.dump_ui_state"), - patch("GramAddict.core.behaviors.obstacle_guard.sleep"), + patch("GramAddict.core.behaviors.obstacle_guard.dump_ui_state", autospec=True), + patch("GramAddict.core.behaviors.obstacle_guard.sleep", autospec=True), ): result = plugin.execute(ctx) @@ -556,8 +561,9 @@ def test_e2e_obstacle_guard_dismiss_modal(base_ctx): with ( patch( "GramAddict.core.behaviors.obstacle_guard.SituationalAwarenessEngine.get_instance", + autospec=True, ) as mock_sae, - patch("GramAddict.core.behaviors.obstacle_guard.sleep"), + patch("GramAddict.core.behaviors.obstacle_guard.sleep", autospec=True), ): mock_instance = MagicMock() mock_instance.perceive.return_value = SituationType.OBSTACLE_MODAL @@ -621,7 +627,7 @@ def test_e2e_resonance_evaluator_visual_vibe_check(base_ctx): plugin = ResonanceEvaluatorPlugin() - with patch("GramAddict.core.behaviors.resonance_evaluator.random.random", return_value=0.0): + with patch("GramAddict.core.behaviors.resonance_evaluator.random.random", autospec=True, return_value=0.0): result = plugin.execute(ctx) assert result.executed is True @@ -679,7 +685,7 @@ def test_e2e_resonance_evaluator_no_persona_interests(base_ctx): plugin = ResonanceEvaluatorPlugin() - with patch("GramAddict.core.behaviors.resonance_evaluator.random.random", return_value=0.0): + with patch("GramAddict.core.behaviors.resonance_evaluator.random.random", autospec=True, return_value=0.0): result = plugin.execute(ctx) assert result.executed is True diff --git a/tests/e2e/test_engine_learning.py b/tests/e2e/test_engine_learning.py index 6ce27c4..80636d1 100644 --- a/tests/e2e/test_engine_learning.py +++ b/tests/e2e/test_engine_learning.py @@ -71,7 +71,9 @@ def test_real_llm_learning_and_unlearning(isolated_screen_memory): # We patch the underlying LLM call just to spy on it (wraps the original function) from GramAddict.core.llm_provider import query_telepathic_llm - with patch("GramAddict.core.llm_provider.query_telepathic_llm", wraps=query_telepathic_llm) as spy_llm: + with patch( + "GramAddict.core.llm_provider.query_telepathic_llm", autospec=True, wraps=query_telepathic_llm + ) as spy_llm: # --------------------------------------------------------- # PASS 1: The Initial Encounter (Learn) # --------------------------------------------------------- diff --git a/tests/e2e/test_engine_perception.py b/tests/e2e/test_engine_perception.py index e939866..42f8ec9 100644 --- a/tests/e2e/test_engine_perception.py +++ b/tests/e2e/test_engine_perception.py @@ -484,7 +484,7 @@ class TestSAELearning: assert sae._compute_situation_hash(c1) == sae._compute_situation_hash(c2) assert sae._compute_situation_hash(c1) != sae._compute_situation_hash(c3) - @patch("GramAddict.core.qdrant_memory.ScreenMemoryDB.store_screen") + @patch("GramAddict.core.qdrant_memory.ScreenMemoryDB.store_screen", autospec=True) def test_llm_false_positive_unlearn(self, mock_store_screen): """When LLM returns 'false_positive', SAE must overwrite Qdrant and return True.""" device = make_mock_device() @@ -493,10 +493,13 @@ class TestSAELearning: device.dump_hierarchy.return_value = INSTAGRAM_HOME_XML # Force the situation to be perceived as an OBSTACLE_MODAL initially - with patch.object(sae, "perceive", return_value=SituationType.OBSTACLE_MODAL): + with patch.object(sae, "perceive", autospec=True, return_value=SituationType.OBSTACLE_MODAL): # Mock LLM to return 'false_positive' with patch.object( - sae, "_plan_escape_via_llm", return_value=EscapeAction("false_positive", reason="No modal found") + sae, + "_plan_escape_via_llm", + autospec=True, + return_value=EscapeAction("false_positive", reason="No modal found"), ): result = sae.ensure_clear_screen(max_attempts=1, initial_xml=INSTAGRAM_HOME_XML) diff --git a/tests/integration/test_bot_flow_interaction.py b/tests/integration/test_bot_flow_interaction.py index 971b2a4..c6fdcd7 100644 --- a/tests/integration/test_bot_flow_interaction.py +++ b/tests/integration/test_bot_flow_interaction.py @@ -316,7 +316,7 @@ def test_feed_loop_deep_engagement(mock_device, mock_cognitive_stack): patch("GramAddict.core.bot_flow._align_active_post", return_value=False), patch("GramAddict.core.bot_flow._humanized_scroll"), patch("GramAddict.core.bot_flow._humanized_click") as mock_click, - patch("GramAddict.core.stealth_typing.ghost_type") as mock_type, + patch("GramAddict.core.stealth_typing.ghost_type", autospec=True) as mock_type, ): mock_extract.return_value = {"username": "legit_user", "description": "test image", "caption": ""} mock_instance = MockTelepathic.get_instance.return_value