Luxx/tests/test_tools.py

314 lines
9.3 KiB
Python

"""Tests for tools module"""
import pytest
from luxx.tools.core import (
ToolContext,
ToolDefinition,
ToolResult,
ToolRegistry,
CommandPermission
)
class TestToolContext:
"""Tests for ToolContext dataclass"""
def test_tool_context_creation(self):
"""Should create context with default values"""
ctx = ToolContext()
assert ctx.workspace is None
assert ctx.user_id is None
assert ctx.username is None
assert ctx.extra == {}
def test_tool_context_with_values(self):
"""Should create context with provided values"""
ctx = ToolContext(
workspace="/workspace/test",
user_id=1,
username="testuser",
extra={"key": "value"}
)
assert ctx.workspace == "/workspace/test"
assert ctx.user_id == 1
assert ctx.username == "testuser"
assert ctx.extra["key"] == "value"
class TestToolDefinition:
"""Tests for ToolDefinition dataclass"""
def test_tool_definition_creation(self):
"""Should create tool definition"""
def handler(args):
return {"result": "ok"}
tool = ToolDefinition(
name="test_tool",
description="A test tool",
parameters={"type": "object"},
handler=handler
)
assert tool.name == "test_tool"
assert tool.description == "A test tool"
assert tool.category == "general"
assert tool.required_permission == CommandPermission.READ_ONLY
def test_tool_definition_to_openai_format(self):
"""Should convert to OpenAI format"""
def handler(args):
return {"result": "ok"}
tool = ToolDefinition(
name="test_tool",
description="A test tool",
parameters={"type": "object", "properties": {}},
handler=handler
)
result = tool.to_openai_format()
assert result["type"] == "function"
assert result["function"]["name"] == "test_tool"
class TestToolResult:
"""Tests for ToolResult dataclass"""
def test_tool_result_success(self):
"""Should create success result"""
result = ToolResult(success=True, data={"key": "value"})
assert result.success is True
assert result.data["key"] == "value"
assert result.error is None
def test_tool_result_failure(self):
"""Should create failure result"""
result = ToolResult(success=False, error="Something went wrong")
assert result.success is False
assert result.error == "Something went wrong"
def test_tool_result_to_dict(self):
"""Should convert to dictionary"""
result = ToolResult(success=True, data={"key": "value"})
d = result.to_dict()
assert isinstance(d, dict)
assert d["success"] is True
assert d["data"]["key"] == "value"
def test_tool_result_ok_factory(self):
"""Should use ok() factory method"""
result = ToolResult.ok({"result": "success"})
assert result.success is True
assert result.data == {"result": "success"}
def test_tool_result_fail_factory(self):
"""Should use fail() factory method"""
result = ToolResult.fail("Error occurred")
assert result.success is False
assert result.error == "Error occurred"
class TestToolRegistry:
"""Tests for ToolRegistry class"""
def test_registry_singleton(self):
"""Should return same instance"""
reg1 = ToolRegistry()
reg2 = ToolRegistry()
assert reg1 is reg2
def test_register_tool(self):
"""Should register a tool"""
registry = ToolRegistry()
registry.clear() # Start fresh
def handler(args):
return {"result": "ok"}
tool = ToolDefinition(
name="my_tool",
description="My test tool",
parameters={},
handler=handler
)
registry.register(tool)
assert registry.get("my_tool") is not None
assert registry.tool_count() == 1
def test_get_nonexistent_tool(self):
"""Should return None for nonexistent tool"""
registry = ToolRegistry()
registry.clear()
assert registry.get("nonexistent") is None
def test_list_all_tools(self):
"""Should list all registered tools"""
registry = ToolRegistry()
registry.clear()
def handler(args):
return {}
tool1 = ToolDefinition(name="tool1", description="Tool 1", parameters={}, handler=handler)
tool2 = ToolDefinition(name="tool2", description="Tool 2", parameters={}, handler=handler)
registry.register(tool1)
registry.register(tool2)
tools = registry.list_all()
assert len(tools) == 2
def test_list_by_category(self):
"""Should filter tools by category"""
registry = ToolRegistry()
registry.clear()
def handler(args):
return {}
tool1 = ToolDefinition(
name="tool1", description="Tool 1", parameters={},
handler=handler, category="code"
)
tool2 = ToolDefinition(
name="tool2", description="Tool 2", parameters={},
handler=handler, category="file"
)
registry.register(tool1)
registry.register(tool2)
code_tools = registry.list_by_category("code")
assert len(code_tools) == 1
def test_execute_tool(self):
"""Should execute a tool"""
registry = ToolRegistry()
registry.clear()
def handler(args):
return {"executed": True, "args": args}
tool = ToolDefinition(
name="test_tool",
description="Test tool",
parameters={},
handler=handler
)
registry.register(tool)
result = registry.execute("test_tool", {"input": "value"})
# Direct handler returns are passed through as-is
assert result["executed"] is True
assert result["args"]["input"] == "value"
def test_execute_tool_with_tool_result(self):
"""Should return ToolResult when handler returns ToolResult"""
registry = ToolRegistry()
registry.clear()
def handler(args):
return ToolResult.ok({"executed": True})
tool = ToolDefinition(
name="test_tool",
description="Test tool",
parameters={},
handler=handler
)
registry.register(tool)
result = registry.execute("test_tool", {})
assert result["success"] is True
assert result["data"]["executed"] is True
def test_execute_nonexistent_tool(self):
"""Should return error for nonexistent tool"""
registry = ToolRegistry()
registry.clear()
result = registry.execute("nonexistent", {})
assert result["success"] is False
assert "not found" in result["error"]
def test_execute_with_context(self):
"""Should pass context to handler"""
registry = ToolRegistry()
registry.clear()
received_context = None
def handler(args, context=None):
nonlocal received_context
received_context = context
return ToolResult.ok({"received": True})
tool = ToolDefinition(
name="test_tool",
description="Test tool",
parameters={},
handler=handler
)
registry.register(tool)
ctx = ToolContext(user_id=1, username="test")
registry.execute("test_tool", {}, context=ctx)
assert received_context is not None
assert received_context.user_id == 1
def test_permission_check(self):
"""Should check user permission"""
registry = ToolRegistry()
registry.clear()
def handler(args):
return ToolResult.ok({"ok": True})
tool = ToolDefinition(
name="admin_tool",
description="Admin tool",
parameters={},
handler=handler,
required_permission=CommandPermission.ADMIN
)
registry.register(tool)
# User with low permission
ctx = ToolContext(
user_id=1,
extra={"user_permission_level": CommandPermission.READ_ONLY}
)
result = registry.execute("admin_tool", {}, context=ctx)
assert result["success"] is False
assert "Permission denied" in result["error"]
def test_remove_tool(self):
"""Should remove a tool"""
registry = ToolRegistry()
registry.clear()
def handler(args):
return {}
tool = ToolDefinition(
name="removable",
description="To be removed",
parameters={},
handler=handler
)
registry.register(tool)
assert registry.get("removable") is not None
registry.remove("removable")
assert registry.get("removable") is None
def test_clear_tools(self):
"""Should clear all tools"""
registry = ToolRegistry()
registry.clear()
def handler(args):
return {}
tool = ToolDefinition(name="tool1", description="", parameters={}, handler=handler)
registry.register(tool)
assert registry.tool_count() > 0
registry.clear()
assert registry.tool_count() == 0