Unfucked gemini's mess
This commit is contained in:
223
tests/test_registry_critical.py
Normal file
223
tests/test_registry_critical.py
Normal file
@@ -0,0 +1,223 @@
|
||||
"""Critical tests for tool registry - Tests that would have caught bugs."""
|
||||
|
||||
import pytest
|
||||
import inspect
|
||||
|
||||
from agent.registry import make_tools, _create_tool_from_function, Tool
|
||||
from agent.prompts import PromptBuilder
|
||||
|
||||
|
||||
class TestToolSpecFormat:
|
||||
"""Critical tests for tool specification format."""
|
||||
|
||||
def test_tool_spec_format_is_openai_compatible(self):
|
||||
"""CRITICAL: Verify tool specs are OpenAI-compatible."""
|
||||
tools = make_tools()
|
||||
builder = PromptBuilder(tools)
|
||||
specs = builder.build_tools_spec()
|
||||
|
||||
# Verify structure
|
||||
assert isinstance(specs, list), "Tool specs must be a list"
|
||||
assert len(specs) > 0, "Tool specs list is empty"
|
||||
|
||||
for spec in specs:
|
||||
# OpenAI format requires these fields
|
||||
assert spec['type'] == 'function', f"Tool type must be 'function', got {spec.get('type')}"
|
||||
assert 'function' in spec, "Tool spec missing 'function' key"
|
||||
|
||||
func = spec['function']
|
||||
assert 'name' in func, "Function missing 'name'"
|
||||
assert 'description' in func, "Function missing 'description'"
|
||||
assert 'parameters' in func, "Function missing 'parameters'"
|
||||
|
||||
params = func['parameters']
|
||||
assert params['type'] == 'object', "Parameters type must be 'object'"
|
||||
assert 'properties' in params, "Parameters missing 'properties'"
|
||||
assert 'required' in params, "Parameters missing 'required'"
|
||||
assert isinstance(params['required'], list), "Required must be a list"
|
||||
|
||||
def test_tool_parameters_match_function_signature(self):
|
||||
"""CRITICAL: Verify generated parameters match function signature."""
|
||||
def test_func(name: str, age: int, active: bool = True):
|
||||
"""Test function with typed parameters."""
|
||||
return {"status": "ok"}
|
||||
|
||||
tool = _create_tool_from_function(test_func)
|
||||
|
||||
# Verify types are correctly mapped
|
||||
assert tool.parameters['properties']['name']['type'] == 'string'
|
||||
assert tool.parameters['properties']['age']['type'] == 'integer'
|
||||
assert tool.parameters['properties']['active']['type'] == 'boolean'
|
||||
|
||||
# Verify required vs optional
|
||||
assert 'name' in tool.parameters['required'], "name should be required"
|
||||
assert 'age' in tool.parameters['required'], "age should be required"
|
||||
assert 'active' not in tool.parameters['required'], "active has default, should not be required"
|
||||
|
||||
def test_all_registered_tools_are_callable(self):
|
||||
"""CRITICAL: Verify all registered tools are actually callable."""
|
||||
tools = make_tools()
|
||||
|
||||
assert len(tools) > 0, "No tools registered"
|
||||
|
||||
for name, tool in tools.items():
|
||||
assert callable(tool.func), f"Tool {name} is not callable"
|
||||
|
||||
# Verify function has valid signature
|
||||
try:
|
||||
sig = inspect.signature(tool.func)
|
||||
# If we get here, signature is valid
|
||||
except Exception as e:
|
||||
pytest.fail(f"Tool {name} has invalid signature: {e}")
|
||||
|
||||
def test_tools_spec_contains_all_registered_tools(self):
|
||||
"""CRITICAL: Verify build_tools_spec() returns all registered tools."""
|
||||
tools = make_tools()
|
||||
builder = PromptBuilder(tools)
|
||||
specs = builder.build_tools_spec()
|
||||
|
||||
spec_names = {spec['function']['name'] for spec in specs}
|
||||
tool_names = set(tools.keys())
|
||||
|
||||
missing = tool_names - spec_names
|
||||
extra = spec_names - tool_names
|
||||
|
||||
assert not missing, f"Tools missing from specs: {missing}"
|
||||
assert not extra, f"Extra tools in specs: {extra}"
|
||||
assert spec_names == tool_names, "Tool specs don't match registered tools"
|
||||
|
||||
def test_tool_description_extracted_from_docstring(self):
|
||||
"""Verify tool description is extracted from function docstring."""
|
||||
def test_func(param: str):
|
||||
"""This is the description.
|
||||
|
||||
More details here.
|
||||
"""
|
||||
return {}
|
||||
|
||||
tool = _create_tool_from_function(test_func)
|
||||
|
||||
assert tool.description == "This is the description."
|
||||
assert "More details" not in tool.description
|
||||
|
||||
def test_tool_without_docstring_uses_function_name(self):
|
||||
"""Verify tool without docstring uses function name as description."""
|
||||
def test_func_no_doc(param: str):
|
||||
return {}
|
||||
|
||||
tool = _create_tool_from_function(test_func_no_doc)
|
||||
|
||||
assert tool.description == "test_func_no_doc"
|
||||
|
||||
def test_tool_parameters_have_descriptions(self):
|
||||
"""Verify all tool parameters have descriptions."""
|
||||
tools = make_tools()
|
||||
builder = PromptBuilder(tools)
|
||||
specs = builder.build_tools_spec()
|
||||
|
||||
for spec in specs:
|
||||
params = spec['function']['parameters']
|
||||
properties = params.get('properties', {})
|
||||
|
||||
for param_name, param_spec in properties.items():
|
||||
assert 'description' in param_spec, \
|
||||
f"Parameter {param_name} in {spec['function']['name']} missing description"
|
||||
|
||||
def test_required_parameters_are_marked_correctly(self):
|
||||
"""Verify required parameters are correctly identified."""
|
||||
def func_with_optional(required: str, optional: int = 5):
|
||||
return {}
|
||||
|
||||
tool = _create_tool_from_function(func_with_optional)
|
||||
|
||||
assert 'required' in tool.parameters['required']
|
||||
assert 'optional' not in tool.parameters['required']
|
||||
assert len(tool.parameters['required']) == 1
|
||||
|
||||
|
||||
class TestToolRegistry:
|
||||
"""Tests for tool registry functionality."""
|
||||
|
||||
def test_make_tools_returns_dict(self):
|
||||
"""Verify make_tools returns a dictionary."""
|
||||
tools = make_tools()
|
||||
|
||||
assert isinstance(tools, dict)
|
||||
assert len(tools) > 0
|
||||
|
||||
def test_all_tools_have_unique_names(self):
|
||||
"""Verify all tool names are unique."""
|
||||
tools = make_tools()
|
||||
|
||||
names = [tool.name for tool in tools.values()]
|
||||
assert len(names) == len(set(names)), "Duplicate tool names found"
|
||||
|
||||
def test_tool_names_match_dict_keys(self):
|
||||
"""Verify tool names match their dictionary keys."""
|
||||
tools = make_tools()
|
||||
|
||||
for key, tool in tools.items():
|
||||
assert key == tool.name, f"Key {key} doesn't match tool name {tool.name}"
|
||||
|
||||
def test_expected_tools_are_registered(self):
|
||||
"""Verify all expected tools are registered."""
|
||||
tools = make_tools()
|
||||
|
||||
expected_tools = [
|
||||
"set_path_for_folder",
|
||||
"list_folder",
|
||||
"find_media_imdb_id",
|
||||
"find_torrent",
|
||||
"add_torrent_by_index",
|
||||
"add_torrent_to_qbittorrent",
|
||||
"get_torrent_by_index",
|
||||
"set_language",
|
||||
]
|
||||
|
||||
for expected in expected_tools:
|
||||
assert expected in tools, f"Expected tool {expected} not registered"
|
||||
|
||||
def test_tool_functions_return_dict(self):
|
||||
"""Verify all tool functions return dictionaries."""
|
||||
tools = make_tools()
|
||||
|
||||
# Test with minimal valid arguments
|
||||
# Note: This is a smoke test, not full integration
|
||||
for name, tool in tools.items():
|
||||
sig = inspect.signature(tool.func)
|
||||
# We can't call all tools without proper setup,
|
||||
# but we can verify they're structured correctly
|
||||
assert callable(tool.func)
|
||||
|
||||
|
||||
class TestToolDataclass:
|
||||
"""Tests for Tool dataclass."""
|
||||
|
||||
def test_tool_creation(self):
|
||||
"""Verify Tool can be created with all fields."""
|
||||
def dummy_func():
|
||||
return {}
|
||||
|
||||
tool = Tool(
|
||||
name="test_tool",
|
||||
description="Test description",
|
||||
func=dummy_func,
|
||||
parameters={"type": "object", "properties": {}, "required": []}
|
||||
)
|
||||
|
||||
assert tool.name == "test_tool"
|
||||
assert tool.description == "Test description"
|
||||
assert tool.func == dummy_func
|
||||
assert isinstance(tool.parameters, dict)
|
||||
|
||||
def test_tool_parameters_structure(self):
|
||||
"""Verify Tool parameters have correct structure."""
|
||||
def dummy_func(arg: str):
|
||||
return {}
|
||||
|
||||
tool = _create_tool_from_function(dummy_func)
|
||||
|
||||
assert 'type' in tool.parameters
|
||||
assert 'properties' in tool.parameters
|
||||
assert 'required' in tool.parameters
|
||||
assert tool.parameters['type'] == 'object'
|
||||
Reference in New Issue
Block a user