test: add E2E coverage for GOAP trap recovery and restart state reset

This commit is contained in:
2026-05-02 23:11:31 +02:00
parent 3da3849ca1
commit 91effbc843

View File

@@ -0,0 +1,75 @@
"""
E2E: GOAP Trap Recovery
========================
Validates that the GOAP engine can properly escape "completely trapped" states
by forcing an app restart, and that the internal failure states are correctly
purged so it doesn't immediately trap itself again on the next iteration.
"""
from GramAddict.core.goap import GoalExecutor
from GramAddict.core.screen_topology import ScreenTopology
from tests.e2e.conftest import load_fixture_xml
def test_goap_recovers_from_trapped_state_with_restart(e2e_device, monkeypatch):
"""
Simulates a broken UI where an action repeatedly fails.
Verifies that GOAP triggers 'force start instagram', and successfully
clears its tracking arrays (action_failures, etc.) to start fresh,
avoiding an infinite restart loop.
"""
xml = load_fixture_xml("home_feed_real.xml")
# We create a device that ALWAYS returns the same home_feed XML,
# no matter what action is taken. This forces the UI to never change,
# which causes actions to fail repeatedly.
device = e2e_device([xml] * 20)
goap = GoalExecutor.get_instance(device, bot_username="testuser")
goap.action_failures.clear()
# We intercept app_start to track how many times it was forced to restart
restart_count = 0
def mock_app_start(*args, **kwargs):
nonlocal restart_count
restart_count += 1
monkeypatch.setattr(device, "app_start", mock_app_start)
# We want to ask it to reach REELS_FEED.
# Since the UI never changes, 'tap reels tab' will fail.
# It will fail twice, get masked, GOAP gets trapped, triggers restart.
# We set max_steps to a small number so it doesn't loop forever in the test.
# We just want to see that AFTER the restart, it tries 'tap reels tab' again,
# meaning it cleared the state.
# Let's mock _execute_action slightly just to spy on it without changing behavior
original_execute = goap._execute_action
executed_actions = []
def spy_execute(action, goal=None):
executed_actions.append(action)
return original_execute(action, goal)
monkeypatch.setattr(goap, "_execute_action", spy_execute)
goap.achieve("open reels", max_steps=6)
# It should have tried 'tap reels tab' twice, failed both times,
# then triggered 'force start instagram'.
# Because of the fix, after the restart, it should have cleared failures
# and tried 'tap reels tab' AGAIN.
assert restart_count > 0, "GOAP never attempted to force restart Instagram when trapped!"
# Count how many times it tried the action
reels_attempts = executed_actions.count("tap reels tab")
assert reels_attempts > 2, (
f"GOAP got trapped, restarted, but never tried the action again! "
f"It only tried {reels_attempts} times, meaning the memory leak is still there. "
f"Actions executed: {executed_actions}"
)
# If the bug was present, it would only try 'tap reels tab' 2 times, mask it forever,
# and then spam 'force start instagram' for the remaining steps.
# With the fix, it tries 2 times, restarts, tries 2 times, restarts, etc.