"""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