Files
ai-rpg/tests/unit/test_tool_registry.py

115 lines
3.1 KiB
Python
Raw Permalink Normal View History

2026-06-20 19:13:05 +03:00
"""Tests for `app.engine.tools.base.ToolRegistry` and a few game tools."""
from __future__ import annotations
import pytest
from app.engine.tools.base import Tool, ToolContext, ToolRegistry, ToolResult
class _StubTool(Tool):
name = "stub"
category = "game"
stages = {"test_stage"}
description = "Stub tool for tests"
parameters_schema = {"type": "object", "properties": {}}
def __init__(self, return_ok: bool = True):
self.return_ok = return_ok
self.calls = []
async def execute(self, arguments, ctx):
self.calls.append(arguments)
if self.return_ok:
return ToolResult(ok=True, data=arguments, message="ok")
return ToolResult(ok=False, error_code="stub_error", error_message="intentional")
def test_registry_register_and_get():
reg = ToolRegistry()
t = _StubTool()
reg.register(t)
assert reg.get("stub") is t
def test_registry_duplicate_register_raises():
reg = ToolRegistry()
reg.register(_StubTool())
with pytest.raises(ValueError):
reg.register(_StubTool())
def test_registry_list_for_stage_filters():
reg = ToolRegistry()
t1 = _StubTool()
t1.stages = {"a", "b"}
t2 = _StubTool()
t2.name = "stub2"
t2.stages = {"b", "c"}
reg.register(t1)
reg.register(t2)
a_tools = reg.list_for_stage("a")
assert a_tools == [t1]
b_tools = reg.list_for_stage("b")
assert set(b_tools) == {t1, t2}
c_tools = reg.list_for_stage("c")
assert c_tools == [t2]
def test_registry_to_openai_format():
reg = ToolRegistry()
reg.register(_StubTool())
fmt = reg.to_openai_format("test_stage")
assert len(fmt) == 1
assert fmt[0]["type"] == "function"
assert fmt[0]["function"]["name"] == "stub"
@pytest.mark.asyncio
async def test_registry_execute_unknown_tool():
reg = ToolRegistry()
ctx = ToolContext(db=None, world=None)
result = await reg.execute("nonexistent", {}, ctx)
assert not result.ok
assert result.error_code == "unknown_tool"
@pytest.mark.asyncio
async def test_registry_execute_dispatches():
reg = ToolRegistry()
t = _StubTool()
reg.register(t)
ctx = ToolContext(db=None, world=None)
result = await reg.execute("stub", {"x": 1}, ctx)
assert result.ok
assert t.calls == [{"x": 1}]
@pytest.mark.asyncio
async def test_registry_execute_catches_exceptions():
class _BoomTool(Tool):
name = "boom"
stages = {"*"}
async def execute(self, arguments, ctx):
raise RuntimeError("boom")
reg = ToolRegistry()
reg.register(_BoomTool())
ctx = ToolContext(db=None, world=None)
result = await reg.execute("boom", {}, ctx)
assert not result.ok
assert result.error_code == "tool_exception"
def test_tool_result_to_dict_ok():
r = ToolResult(ok=True, data={"a": 1}, message="hello")
assert r.to_dict() == {"ok": True, "data": {"a": 1}, "message": "hello"}
def test_tool_result_to_dict_error():
r = ToolResult(ok=False, error_code="x", error_message="boom")
d = r.to_dict()
assert d["ok"] is False
assert d["error"]["code"] == "x"
assert d["error"]["message"] == "boom"