diff --git a/tests/test_planner_hierarchy.py b/tests/test_planner_hierarchy.py new file mode 100644 index 0000000..9e60270 --- /dev/null +++ b/tests/test_planner_hierarchy.py @@ -0,0 +1,63 @@ +import pytest +from unittest.mock import patch +from GramAddict.core.navigation.planner import GoalPlanner +from GramAddict.core.perception.screen_identity import ScreenType + +@pytest.fixture +def planner(): + return GoalPlanner("test_user") + +@patch("GramAddict.core.navigation.brain.ask_brain_for_action") +@patch("GramAddict.core.screen_topology.ScreenTopology.find_route") +def test_brain_is_primary_strategy(mock_find_route, mock_ask_brain, planner): + """ + TDD Proof: Brain must be evaluated BEFORE HD Map. + If Brain returns a valid action, HD Map should never be queried. + """ + # 1. Setup State + goal = "open some screen" + screen = { + "screen_type": ScreenType.HOME_FEED, + "available_actions": ["action A", "action B"], + "context": {} + } + + # 2. Setup Mocks + mock_ask_brain.return_value = "action A" # Brain picks A + mock_find_route.return_value = [("action B", ScreenType.EXPLORE_GRID)] # HD Map would pick B + + # 3. Execute Planner + action = planner.plan_next_step(goal, screen) + + # 4. Assertions + assert action == "action A", "Planner did not use the Brain's action!" + mock_ask_brain.assert_called_once() + mock_find_route.assert_not_called() # Crucial: HD Map must be skipped entirely! + +@patch("GramAddict.core.navigation.brain.ask_brain_for_action") +@patch("GramAddict.core.screen_topology.ScreenTopology.find_route") +@patch("GramAddict.core.screen_topology.ScreenTopology.goal_to_target_screen") +def test_brain_fallback_to_hd_map(mock_goal_target, mock_find_route, mock_ask_brain, planner): + """ + TDD Proof: If Brain fails (returns None), Planner must fallback to HD Map. + """ + # 1. Setup State + goal = "open explore screen" + screen = { + "screen_type": ScreenType.HOME_FEED, + "available_actions": ["action A", "action B"], + "context": {} + } + + # 2. Setup Mocks + mock_ask_brain.return_value = None # Brain fails or is confused + mock_goal_target.return_value = ScreenType.EXPLORE_GRID + mock_find_route.return_value = [("action B", ScreenType.EXPLORE_GRID)] # HD Map picks B + + # 3. Execute Planner + action = planner.plan_next_step(goal, screen) + + # 4. Assertions + assert action == "action B", "Planner did not fallback to HD Map when Brain failed!" + mock_ask_brain.assert_called_once() + mock_find_route.assert_called_once()