68 lines
2.8 KiB
Python
68 lines
2.8 KiB
Python
"""Unit tests for the Notion-style step labels (backend.tools.labels)."""
|
|
|
|
from backend.agent.loop import ToolCallRecord
|
|
from backend.tools.labels import GENERIC_KEY, _STEP_LABELS, tool_step_label
|
|
from backend.tools.registry import list_tools
|
|
from backend.tools.context import ToolScope
|
|
|
|
|
|
class TestToolStepLabel:
|
|
def test_every_in_app_tool_has_a_label(self):
|
|
# A registered tool without a mapping still degrades gracefully to the
|
|
# generic key, but the curated coverage must stay complete for the UI.
|
|
names = {spec.name for spec in list_tools(scope=ToolScope.IN_APP)}
|
|
assert names.issubset(set(_STEP_LABELS)), names - set(_STEP_LABELS)
|
|
|
|
def test_label_argument_names_match_input_models(self):
|
|
# A mapped primary argument that the tool's input model does not
|
|
# actually declare would silently render "{value}" placeholders —
|
|
# verify every (name, arg) pair against the real schemas.
|
|
from backend.tools.registry import get_tool
|
|
|
|
for name, (_key, arg) in _STEP_LABELS.items():
|
|
if arg is None:
|
|
continue
|
|
spec = get_tool(name)
|
|
assert spec is not None, name
|
|
fields = spec.input_model.model_fields
|
|
assert arg in fields, f"{name}: input model has no field '{arg}'"
|
|
|
|
def test_named_tool_returns_key_and_value(self):
|
|
label = tool_step_label("read_file", {"vault": "V", "path": "notes/a.md"})
|
|
assert label["key"] == "file_read"
|
|
assert label["params"]["value"] == "notes/a.md"
|
|
|
|
def test_search_tool_surfaces_the_query(self):
|
|
label = tool_step_label("search_fulltext", {"q": "pizza", "vault": "V"})
|
|
assert label["key"] == "search"
|
|
assert label["params"]["value"] == "pizza"
|
|
|
|
def test_no_argument_tools_have_empty_params(self):
|
|
label = tool_step_label("list_vaults", {})
|
|
assert label["key"] == "vaults"
|
|
assert label["params"] == {}
|
|
|
|
def test_missing_argument_degrades_to_key_only(self):
|
|
label = tool_step_label("read_file", {})
|
|
assert label["key"] == "file_read"
|
|
assert "value" not in label["params"]
|
|
|
|
def test_unknown_tool_falls_back_to_generic(self):
|
|
label = tool_step_label("future_tool_name", {"x": 1})
|
|
assert label["key"] == GENERIC_KEY
|
|
assert label["params"]["tool"] == "future tool name"
|
|
|
|
def test_arguments_none_is_accepted(self):
|
|
assert tool_step_label("list_tags", None)["key"] == "tags"
|
|
|
|
def test_tool_call_record_carries_step(self):
|
|
rec = ToolCallRecord(
|
|
name="read_file",
|
|
arguments={"path": "a.md"},
|
|
ok=True,
|
|
result={},
|
|
step=tool_step_label("read_file", {"path": "a.md"}),
|
|
)
|
|
assert rec.step["key"] == "file_read"
|
|
assert rec.step["params"]["value"] == "a.md"
|