test(e2e): enforce autospec=True on all remaining patch and patch.object calls

This commit is contained in:
2026-04-27 10:49:07 +02:00
parent 4de087ae45
commit 9ad49500f9
7 changed files with 54 additions and 41 deletions

View File

@@ -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)

View File

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

View File

@@ -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 = "<hierarchy></hierarchy>"
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 = '<node class="hierarchy"><node resource-id="com.instagram.android:id/reel_viewer_root" /></node>'
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 = "<hierarchy></hierarchy>"
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 = "<hierarchy></hierarchy>"
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

View File

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

View File

@@ -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)
# ---------------------------------------------------------

View File

@@ -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)

View File

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