import pytest from tiny_computer_agent import ACTION_KINDS, ACTION_MENU, Action, ActionError, parse_action def test_click_by_label(): action = Action("click", label="Save") assert action.kind == "click" assert "label='Save'" in action.describe() def test_click_by_pixels(): assert Action("click", x=10, y=20).x == 10 @pytest.mark.parametrize( "kwargs, message", [ ({"kind": "fly"}, "unknown action"), ({"kind": "click"}, "click needs a label"), ({"kind": "click", "x": -1, "y": 5}, "non-negative"), ({"kind": "type", "text": ""}, "type needs non-empty text"), ({"kind": "key"}, "key needs a chord"), ({"kind": "scroll", "amount": 0}, "non-zero amount"), ({"kind": "wait", "seconds": 0}, "positive number of seconds"), ], ) def test_validation_rejects_bad_actions(kwargs, message): with pytest.raises(ActionError, match=message): Action(**kwargs) # the same rule must hold for anything a model sends back with pytest.raises(ActionError): parse_action(kwargs) def test_parse_action_ignores_chatty_extra_keys(): action = parse_action({"kind": "type", "text": "hello", "confidence": 0.9, "thought": "..."}) assert action.text == "hello" def test_parse_action_rejects_a_non_mapping(): with pytest.raises(ActionError, match="expected a mapping"): parse_action(["click", "Save"]) def test_menu_documents_every_verb(): for kind in ACTION_KINDS: assert f'"kind": "{kind}"' in ACTION_MENU def test_round_trip_to_dict(): action = Action("scroll", amount=-3, reason="more content below") assert parse_action(action.to_dict()).amount == -3