Files
agentic-mobile-control/tests/test_tool_specs.py
T

138 lines
4.0 KiB
Python

from __future__ import annotations
from runtime.tool_specs import (
ACTION_TOOL_SPECS,
ALL_TOOL_SPECS,
FINISH_TASK_SPEC,
INPUT_TEXT_SPEC,
LAUNCH_APP_SPEC,
SWIPE_SPEC,
TAP_SPEC,
TERMINATE_APP_SPEC,
ToolSpec,
)
def test_action_tool_specs_has_five_entries_and_all_tool_specs_adds_finish_task() -> (
None
):
assert len(ACTION_TOOL_SPECS) == 7
assert len(ALL_TOOL_SPECS) == 8
assert ALL_TOOL_SPECS == [*ACTION_TOOL_SPECS, FINISH_TASK_SPEC]
assert FINISH_TASK_SPEC not in ACTION_TOOL_SPECS
def test_all_tool_spec_names_are_unique() -> None:
names = [spec.name for spec in ALL_TOOL_SPECS]
assert len(names) == len(set(names))
def test_every_tool_spec_schema_forbids_additional_properties() -> None:
for spec in ALL_TOOL_SPECS:
assert isinstance(spec, ToolSpec)
assert spec.parameters["type"] == "object"
assert spec.parameters["additionalProperties"] is False
def test_tap_spec_requires_x_and_y() -> None:
assert TAP_SPEC.parameters["required"] == ["x", "y", "purpose", "expected_outcome"]
assert set(TAP_SPEC.parameters["properties"]) == {
"x",
"y",
"purpose",
"expected_outcome",
}
def test_swipe_spec_requires_coordinates_and_makes_duration_optional() -> None:
assert SWIPE_SPEC.parameters["required"] == [
"start_x",
"start_y",
"end_x",
"end_y",
"purpose",
"expected_outcome",
]
assert set(SWIPE_SPEC.parameters["properties"]) == {
"start_x",
"start_y",
"end_x",
"end_y",
"duration_ms",
"purpose",
"expected_outcome",
}
assert "duration_ms" not in SWIPE_SPEC.parameters["required"]
assert SWIPE_SPEC.parameters["properties"]["duration_ms"]["default"] == 500
def test_input_text_spec_requires_text() -> None:
assert INPUT_TEXT_SPEC.parameters["required"] == [
"text",
"purpose",
"expected_outcome",
]
assert set(INPUT_TEXT_SPEC.parameters["properties"]) == {
"text",
"purpose",
"expected_outcome",
}
def test_launch_and_terminate_app_specs_require_app_id() -> None:
for spec in (LAUNCH_APP_SPEC, TERMINATE_APP_SPEC):
assert spec.parameters["required"] == [
"app_id",
"purpose",
"expected_outcome",
]
assert set(spec.parameters["properties"]) == {
"app_id",
"purpose",
"expected_outcome",
}
def test_action_metadata_is_required_nonempty_text() -> None:
for spec in ACTION_TOOL_SPECS:
for field_name in ("purpose", "expected_outcome"):
field = spec.parameters["properties"][field_name]
assert field["type"] == "string"
assert field["minLength"] == 1
def test_finish_task_spec_requires_success_and_reason() -> None:
assert FINISH_TASK_SPEC.parameters["required"] == ["success", "reason"]
assert set(FINISH_TASK_SPEC.parameters["properties"]) == {"success", "reason"}
assert FINISH_TASK_SPEC.parameters["properties"]["success"]["type"] == "boolean"
def test_no_tool_spec_declares_device_id() -> None:
for spec in ALL_TOOL_SPECS:
assert "device_id" not in spec.parameters["properties"]
def test_long_press_and_double_tap_are_action_tools():
from runtime.tool_specs import ACTION_TOOL_NAMES
assert "long_press" in ACTION_TOOL_NAMES
assert "double_tap" in ACTION_TOOL_NAMES
def test_long_press_spec_has_duration_with_default():
from runtime.tool_specs import LONG_PRESS_SPEC
props = LONG_PRESS_SPEC.parameters["properties"]
assert props["x"]["type"] == "number"
assert props["y"]["type"] == "number"
assert props["duration_ms"]["default"] == 1200
# purpose/expected_outcome are required metadata
assert "purpose" in LONG_PRESS_SPEC.parameters["required"]
def test_double_tap_spec_has_interval_with_default():
from runtime.tool_specs import DOUBLE_TAP_SPEC
props = DOUBLE_TAP_SPEC.parameters["properties"]
assert props["interval_ms"]["default"] == 80