diff --git a/tests/e2e/test_goap_loop_prevention.py b/tests/e2e/test_goap_loop_prevention.py new file mode 100644 index 0000000..f259532 --- /dev/null +++ b/tests/e2e/test_goap_loop_prevention.py @@ -0,0 +1,44 @@ +import pytest +from unittest.mock import MagicMock, patch + +from GramAddict.core.goap import GoalExecutor +from GramAddict.core.perception.screen_identity import ScreenType +from GramAddict.core.q_nav_graph import QNavGraph +from GramAddict.core.navigation.planner import GoalPlanner +from GramAddict.core.screen_topology import ScreenTopology + +def test_goap_planner_avoids_infinite_loop_on_masked_edge(): + """ + Testet, dass der Planner eine blockierte HD Map Kante + (die wegen Fehlversuchen 'masked' wurde) erfolgreich umgeht + und nicht in einen endlosen Loop gerät. + """ + planner = GoalPlanner("test_user") + + screen = { + "screen_type": ScreenType.HOME_FEED, + "available_actions": ["tap profile tab", "scroll down"], + "context": {} + } + + # NORMAL BEHAVIOR: HD Map routes us to OWN_PROFILE first + action_normal = planner.plan_next_step("open following list", screen) + assert action_normal == "tap profile tab", "HD Map sollte primär über OWN_PROFILE routen" + + # LOOP PREVENTION: + # Simulate that "tap following list" failed repeatedly and is masked. + action_failures = { + "tap following list": 2, + } + + action_avoided = planner.plan_next_step( + "open following list", + screen, + action_failures=action_failures + ) + + # The planner must NO LONGER route to OWN_PROFILE because the exit edge is blocked. + # It should fall back to autonomous discovery (returning the goal itself) + assert action_avoided != "tap profile tab", "Planner ist in die Falle getappt und hat blind geroutet!" + assert action_avoided == "open following list", "Planner sollte auf Autonomous Discovery zurückfallen" +