Spaces:
Sleeping
Sleeping
| from unittest.mock import MagicMock | |
| import pytest | |
| from pydantic import BaseModel | |
| from swarms.tools.tool import ( | |
| BaseTool, | |
| Runnable, | |
| StructuredTool, | |
| Tool, | |
| tool, | |
| ) | |
| # Define test data | |
| test_input = {"key1": "value1", "key2": "value2"} | |
| expected_output = "expected_output_value" | |
| # Test with global variables | |
| global_var = "global" | |
| # Basic tests for BaseTool | |
| def test_base_tool_init(): | |
| # Test BaseTool initialization | |
| tool = BaseTool() | |
| assert isinstance(tool, BaseTool) | |
| def test_base_tool_invoke(): | |
| # Test BaseTool invoke method | |
| tool = BaseTool() | |
| result = tool.invoke(test_input) | |
| assert result == expected_output | |
| # Basic tests for Tool | |
| def test_tool_init(): | |
| # Test Tool initialization | |
| tool = Tool() | |
| assert isinstance(tool, Tool) | |
| def test_tool_invoke(): | |
| # Test Tool invoke method | |
| tool = Tool() | |
| result = tool.invoke(test_input) | |
| assert result == expected_output | |
| # Basic tests for StructuredTool | |
| def test_structured_tool_init(): | |
| # Test StructuredTool initialization | |
| tool = StructuredTool() | |
| assert isinstance(tool, StructuredTool) | |
| def test_structured_tool_invoke(): | |
| # Test StructuredTool invoke method | |
| tool = StructuredTool() | |
| result = tool.invoke(test_input) | |
| assert result == expected_output | |
| # Test additional functionality and edge cases as needed | |
| def test_tool_creation(): | |
| tool = Tool( | |
| name="test_tool", func=lambda x: x, description="Test tool" | |
| ) | |
| assert tool.name == "test_tool" | |
| assert tool.func is not None | |
| assert tool.description == "Test tool" | |
| def test_tool_ainvoke(): | |
| tool = Tool( | |
| name="test_tool", func=lambda x: x, description="Test tool" | |
| ) | |
| result = tool.ainvoke("input_data") | |
| assert result == "input_data" | |
| def test_tool_ainvoke_with_coroutine(): | |
| async def async_function(input_data): | |
| return input_data | |
| tool = Tool( | |
| name="test_tool", | |
| coroutine=async_function, | |
| description="Test tool", | |
| ) | |
| result = tool.ainvoke("input_data") | |
| assert result == "input_data" | |
| def test_tool_args(): | |
| def sample_function(input_data): | |
| return input_data | |
| tool = Tool( | |
| name="test_tool", | |
| func=sample_function, | |
| description="Test tool", | |
| ) | |
| assert tool.args == {"tool_input": {"type": "string"}} | |
| # Basic tests for StructuredTool class | |
| def test_structured_tool_creation(): | |
| class SampleArgsSchema: | |
| pass | |
| tool = StructuredTool( | |
| name="test_tool", | |
| func=lambda x: x, | |
| description="Test tool", | |
| args_schema=SampleArgsSchema, | |
| ) | |
| assert tool.name == "test_tool" | |
| assert tool.func is not None | |
| assert tool.description == "Test tool" | |
| assert tool.args_schema == SampleArgsSchema | |
| def test_structured_tool_ainvoke(): | |
| class SampleArgsSchema: | |
| pass | |
| tool = StructuredTool( | |
| name="test_tool", | |
| func=lambda x: x, | |
| description="Test tool", | |
| args_schema=SampleArgsSchema, | |
| ) | |
| result = tool.ainvoke({"tool_input": "input_data"}) | |
| assert result == "input_data" | |
| def test_structured_tool_ainvoke_with_coroutine(): | |
| class SampleArgsSchema: | |
| pass | |
| async def async_function(input_data): | |
| return input_data | |
| tool = StructuredTool( | |
| name="test_tool", | |
| coroutine=async_function, | |
| description="Test tool", | |
| args_schema=SampleArgsSchema, | |
| ) | |
| result = tool.ainvoke({"tool_input": "input_data"}) | |
| assert result == "input_data" | |
| def test_structured_tool_args(): | |
| class SampleArgsSchema: | |
| pass | |
| def sample_function(input_data): | |
| return input_data | |
| tool = StructuredTool( | |
| name="test_tool", | |
| func=sample_function, | |
| description="Test tool", | |
| args_schema=SampleArgsSchema, | |
| ) | |
| assert tool.args == {"tool_input": {"type": "string"}} | |
| # Additional tests for exception handling | |
| def test_tool_ainvoke_exception(): | |
| tool = Tool(name="test_tool", func=None, description="Test tool") | |
| with pytest.raises(NotImplementedError): | |
| tool.ainvoke("input_data") | |
| def test_tool_ainvoke_with_coroutine_exception(): | |
| tool = Tool( | |
| name="test_tool", coroutine=None, description="Test tool" | |
| ) | |
| with pytest.raises(NotImplementedError): | |
| tool.ainvoke("input_data") | |
| def test_structured_tool_ainvoke_exception(): | |
| class SampleArgsSchema: | |
| pass | |
| tool = StructuredTool( | |
| name="test_tool", | |
| func=None, | |
| description="Test tool", | |
| args_schema=SampleArgsSchema, | |
| ) | |
| with pytest.raises(NotImplementedError): | |
| tool.ainvoke({"tool_input": "input_data"}) | |
| def test_structured_tool_ainvoke_with_coroutine_exception(): | |
| class SampleArgsSchema: | |
| pass | |
| tool = StructuredTool( | |
| name="test_tool", | |
| coroutine=None, | |
| description="Test tool", | |
| args_schema=SampleArgsSchema, | |
| ) | |
| with pytest.raises(NotImplementedError): | |
| tool.ainvoke({"tool_input": "input_data"}) | |
| def test_tool_description_not_provided(): | |
| tool = Tool(name="test_tool", func=lambda x: x) | |
| assert tool.name == "test_tool" | |
| assert tool.func is not None | |
| assert tool.description == "" | |
| def test_tool_invoke_with_callbacks(): | |
| def sample_function(input_data, callbacks=None): | |
| if callbacks: | |
| callbacks.on_start() | |
| callbacks.on_finish() | |
| return input_data | |
| tool = Tool(name="test_tool", func=sample_function) | |
| callbacks = MagicMock() | |
| result = tool.invoke("input_data", callbacks=callbacks) | |
| assert result == "input_data" | |
| callbacks.on_start.assert_called_once() | |
| callbacks.on_finish.assert_called_once() | |
| def test_tool_invoke_with_new_argument(): | |
| def sample_function(input_data, callbacks=None): | |
| return input_data | |
| tool = Tool(name="test_tool", func=sample_function) | |
| result = tool.invoke("input_data", callbacks=None) | |
| assert result == "input_data" | |
| def test_tool_ainvoke_with_new_argument(): | |
| async def async_function(input_data, callbacks=None): | |
| return input_data | |
| tool = Tool(name="test_tool", coroutine=async_function) | |
| result = tool.ainvoke("input_data", callbacks=None) | |
| assert result == "input_data" | |
| def test_tool_description_from_docstring(): | |
| def sample_function(input_data): | |
| """Sample function docstring""" | |
| return input_data | |
| tool = Tool(name="test_tool", func=sample_function) | |
| assert tool.description == "Sample function docstring" | |
| def test_tool_ainvoke_with_exceptions(): | |
| async def async_function(input_data): | |
| raise ValueError("Test exception") | |
| tool = Tool(name="test_tool", coroutine=async_function) | |
| with pytest.raises(ValueError): | |
| tool.ainvoke("input_data") | |
| # Additional tests for StructuredTool class | |
| def test_structured_tool_infer_schema_false(): | |
| def sample_function(input_data): | |
| return input_data | |
| tool = StructuredTool( | |
| name="test_tool", | |
| func=sample_function, | |
| args_schema=None, | |
| infer_schema=False, | |
| ) | |
| assert tool.args_schema is None | |
| def test_structured_tool_ainvoke_with_callbacks(): | |
| class SampleArgsSchema: | |
| pass | |
| def sample_function(input_data, callbacks=None): | |
| if callbacks: | |
| callbacks.on_start() | |
| callbacks.on_finish() | |
| return input_data | |
| tool = StructuredTool( | |
| name="test_tool", | |
| func=sample_function, | |
| args_schema=SampleArgsSchema, | |
| ) | |
| callbacks = MagicMock() | |
| result = tool.ainvoke( | |
| {"tool_input": "input_data"}, callbacks=callbacks | |
| ) | |
| assert result == "input_data" | |
| callbacks.on_start.assert_called_once() | |
| callbacks.on_finish.assert_called_once() | |
| def test_structured_tool_description_not_provided(): | |
| class SampleArgsSchema: | |
| pass | |
| tool = StructuredTool( | |
| name="test_tool", | |
| func=lambda x: x, | |
| args_schema=SampleArgsSchema, | |
| ) | |
| assert tool.name == "test_tool" | |
| assert tool.func is not None | |
| assert tool.description == "" | |
| def test_structured_tool_args_schema(): | |
| class SampleArgsSchema: | |
| pass | |
| def sample_function(input_data): | |
| return input_data | |
| tool = StructuredTool( | |
| name="test_tool", | |
| func=sample_function, | |
| args_schema=SampleArgsSchema, | |
| ) | |
| assert tool.args_schema == SampleArgsSchema | |
| def test_structured_tool_args_schema_inference(): | |
| def sample_function(input_data): | |
| return input_data | |
| tool = StructuredTool( | |
| name="test_tool", | |
| func=sample_function, | |
| args_schema=None, | |
| infer_schema=True, | |
| ) | |
| assert tool.args_schema is not None | |
| def test_structured_tool_ainvoke_with_new_argument(): | |
| class SampleArgsSchema: | |
| pass | |
| def sample_function(input_data, callbacks=None): | |
| return input_data | |
| tool = StructuredTool( | |
| name="test_tool", | |
| func=sample_function, | |
| args_schema=SampleArgsSchema, | |
| ) | |
| result = tool.ainvoke( | |
| {"tool_input": "input_data"}, callbacks=None | |
| ) | |
| assert result == "input_data" | |
| def test_structured_tool_ainvoke_with_exceptions(): | |
| class SampleArgsSchema: | |
| pass | |
| async def async_function(input_data): | |
| raise ValueError("Test exception") | |
| tool = StructuredTool( | |
| name="test_tool", | |
| coroutine=async_function, | |
| args_schema=SampleArgsSchema, | |
| ) | |
| with pytest.raises(ValueError): | |
| tool.ainvoke({"tool_input": "input_data"}) | |
| def test_base_tool_verbose_logging(caplog): | |
| # Test verbose logging in BaseTool | |
| tool = BaseTool(verbose=True) | |
| result = tool.invoke(test_input) | |
| assert result == expected_output | |
| assert "Verbose logging" in caplog.text | |
| def test_tool_exception_handling(): | |
| # Test exception handling in Tool | |
| tool = Tool() | |
| with pytest.raises(Exception): | |
| tool.invoke(test_input, raise_exception=True) | |
| def test_structured_tool_async_invoke(): | |
| # Test asynchronous invoke in StructuredTool | |
| tool = StructuredTool() | |
| result = tool.ainvoke(test_input) | |
| assert result == expected_output | |
| # Add more tests for specific functionalities and edge cases as needed | |
| # Import necessary libraries and modules | |
| # Example of a mock function to be used in testing | |
| def mock_function(arg: str) -> str: | |
| """A simple mock function for testing.""" | |
| return f"Processed {arg}" | |
| # Example of a Runnable class for testing | |
| class MockRunnable(Runnable): | |
| # Define necessary methods and properties | |
| pass | |
| # Fixture for creating a mock function | |
| def mock_func(): | |
| return mock_function | |
| # Fixture for creating a Runnable instance | |
| def mock_runnable(): | |
| return MockRunnable() | |
| # Basic functionality tests | |
| def test_tool_with_callable(mock_func): | |
| # Test creating a tool with a simple callable | |
| tool_instance = tool(mock_func) | |
| assert isinstance(tool_instance, BaseTool) | |
| def test_tool_with_runnable(mock_runnable): | |
| # Test creating a tool with a Runnable instance | |
| tool_instance = tool(mock_runnable) | |
| assert isinstance(tool_instance, BaseTool) | |
| # ... more basic functionality tests ... | |
| # Argument handling tests | |
| def test_tool_with_invalid_argument(): | |
| # Test passing an invalid argument type | |
| with pytest.raises(ValueError): | |
| tool( | |
| 123 | |
| ) # Using an integer instead of a string/callable/Runnable | |
| def test_tool_with_multiple_arguments(mock_func): | |
| # Test passing multiple valid arguments | |
| tool_instance = tool("mock", mock_func) | |
| assert isinstance(tool_instance, BaseTool) | |
| # ... more argument handling tests ... | |
| # Schema inference and application tests | |
| class TestSchema(BaseModel): | |
| arg: str | |
| def test_tool_with_args_schema(mock_func): | |
| # Test passing a custom args_schema | |
| tool_instance = tool(mock_func, args_schema=TestSchema) | |
| assert tool_instance.args_schema == TestSchema | |
| # ... more schema tests ... | |
| # Exception handling tests | |
| def test_tool_function_without_docstring(): | |
| # Test that a ValueError is raised if the function lacks a docstring | |
| def no_doc_func(arg: str) -> str: | |
| return arg | |
| with pytest.raises(ValueError): | |
| tool(no_doc_func) | |
| # Test suite starts here | |
| class TestTool: | |
| # Basic Functionality Tests | |
| def test_tool_with_valid_callable_creates_base_tool( | |
| self, mock_func | |
| ): | |
| result = tool(mock_func) | |
| assert isinstance(result, BaseTool) | |
| def test_tool_returns_correct_function_name(self, mock_func): | |
| result = tool(mock_func) | |
| assert result.func.__name__ == "mock_function" | |
| # Argument Handling Tests | |
| def test_tool_with_string_and_runnable(self, mock_runnable): | |
| result = tool("mock_runnable", mock_runnable) | |
| assert isinstance(result, BaseTool) | |
| def test_tool_raises_error_with_invalid_arguments(self): | |
| with pytest.raises(ValueError): | |
| tool(123) | |
| def test_tool_with_infer_schema_true(self, mock_func): | |
| tool(mock_func, infer_schema=True) | |
| # Assertions related to schema inference | |
| # Return Direct Feature Tests | |
| def test_tool_with_return_direct_true(self, mock_func): | |
| tool(mock_func, return_direct=True) | |
| # Assertions for return_direct behavior | |
| # Error Handling Tests | |
| def test_tool_raises_error_without_docstring(self): | |
| def no_doc_func(arg: str) -> str: | |
| return arg | |
| with pytest.raises(ValueError): | |
| tool(no_doc_func) | |
| def test_tool_raises_error_runnable_without_object_schema( | |
| self, mock_runnable | |
| ): | |
| with pytest.raises(ValueError): | |
| tool(mock_runnable) | |
| # Decorator Behavior Tests | |
| async def test_async_tool_function(self): | |
| async def async_func(arg: str) -> str: | |
| return arg | |
| # Assertions for async behavior | |
| # Integration with StructuredTool and Tool Classes | |
| def test_integration_with_structured_tool(self, mock_func): | |
| result = tool(mock_func) | |
| assert isinstance(result, StructuredTool) | |
| # Concurrency and Async Handling Tests | |
| def test_concurrency_in_tool(self, mock_func): | |
| # Test related to concurrency | |
| pass | |
| # Mocking and Isolation Tests | |
| def test_mocking_external_dependencies(self, mocker): | |
| # Use mocker to mock external dependencies | |
| pass | |
| def test_tool_with_different_return_types(self): | |
| def return_int(arg: str) -> int: | |
| return int(arg) | |
| result = return_int("123") | |
| assert isinstance(result, int) | |
| assert result == 123 | |
| def return_bool(arg: str) -> bool: | |
| return arg.lower() in ["true", "yes"] | |
| result = return_bool("true") | |
| assert isinstance(result, bool) | |
| assert result is True | |
| # Test with multiple arguments | |
| def test_tool_with_multiple_args(self): | |
| def concat_strings(a: str, b: str) -> str: | |
| return a + b | |
| result = concat_strings("Hello", "World") | |
| assert result == "HelloWorld" | |
| # Test handling of optional arguments | |
| def test_tool_with_optional_args(self): | |
| def greet(name: str, greeting: str = "Hello") -> str: | |
| return f"{greeting} {name}" | |
| assert greet("Alice") == "Hello Alice" | |
| assert greet("Alice", greeting="Hi") == "Hi Alice" | |
| # Test with variadic arguments | |
| def test_tool_with_variadic_args(self): | |
| def sum_numbers(*numbers: int) -> int: | |
| return sum(numbers) | |
| assert sum_numbers(1, 2, 3) == 6 | |
| assert sum_numbers(10, 20) == 30 | |
| # Test with keyword arguments | |
| def test_tool_with_kwargs(self): | |
| def build_query(**kwargs) -> str: | |
| return "&".join(f"{k}={v}" for k, v in kwargs.items()) | |
| assert build_query(a=1, b=2) == "a=1&b=2" | |
| assert build_query(foo="bar") == "foo=bar" | |
| # Test with mixed types of arguments | |
| def test_tool_with_mixed_args(self): | |
| def mixed_args(a: int, b: str, *args, **kwargs) -> str: | |
| return f"{a}{b}{len(args)}{'-'.join(kwargs.values())}" | |
| assert mixed_args(1, "b", "c", "d", x="y", z="w") == "1b2y-w" | |
| # Test error handling with incorrect types | |
| def test_tool_error_with_incorrect_types(self): | |
| def add_numbers(a: int, b: int) -> int: | |
| return a + b | |
| with pytest.raises(TypeError): | |
| add_numbers("1", "2") | |
| # Test with nested tools | |
| def test_nested_tools(self): | |
| def inner_tool(arg: str) -> str: | |
| return f"Inner {arg}" | |
| def outer_tool(arg: str) -> str: | |
| return f"Outer {inner_tool(arg)}" | |
| assert outer_tool("Test") == "Outer Inner Test" | |
| def test_tool_with_global_variable(self): | |
| def access_global(arg: str) -> str: | |
| return f"{global_var} {arg}" | |
| assert access_global("Var") == "global Var" | |
| # Test with environment variables | |
| def test_tool_with_env_variables(self, monkeypatch): | |
| monkeypatch.setenv("TEST_VAR", "Environment") | |
| def access_env_variable(arg: str) -> str: | |
| import os | |
| return f"{os.environ['TEST_VAR']} {arg}" | |
| assert access_env_variable("Var") == "Environment Var" | |
| # ... [Previous test cases] ... | |
| # Test with complex data structures | |
| def test_tool_with_complex_data_structures(self): | |
| def process_data(data: dict) -> list: | |
| return [data[key] for key in sorted(data.keys())] | |
| result = process_data({"b": 2, "a": 1}) | |
| assert result == [1, 2] | |
| # Test handling exceptions within the tool function | |
| def test_tool_handling_internal_exceptions(self): | |
| def function_that_raises(arg: str): | |
| if arg == "error": | |
| raise ValueError("Error occurred") | |
| return arg | |
| with pytest.raises(ValueError): | |
| function_that_raises("error") | |
| assert function_that_raises("ok") == "ok" | |
| # Test with functions returning None | |
| def test_tool_with_none_return(self): | |
| def return_none(arg: str): | |
| return None | |
| assert return_none("anything") is None | |
| # Test with lambda functions | |
| def test_tool_with_lambda(self): | |
| tool_lambda = tool(lambda x: x * 2) | |
| assert tool_lambda(3) == 6 | |
| # Test with class methods | |
| def test_tool_with_class_method(self): | |
| class MyClass: | |
| def method(self, arg: str) -> str: | |
| return f"Method {arg}" | |
| obj = MyClass() | |
| assert obj.method("test") == "Method test" | |
| # Test tool function with inheritance | |
| def test_tool_with_inheritance(self): | |
| class Parent: | |
| def parent_method(self, arg: str) -> str: | |
| return f"Parent {arg}" | |
| class Child(Parent): | |
| def child_method(self, arg: str) -> str: | |
| return f"Child {arg}" | |
| child_obj = Child() | |
| assert child_obj.parent_method("test") == "Parent test" | |
| assert child_obj.child_method("test") == "Child test" | |
| # Test with decorators stacking | |
| def test_tool_with_multiple_decorators(self): | |
| def another_decorator(func): | |
| def wrapper(*args, **kwargs): | |
| return f"Decorated {func(*args, **kwargs)}" | |
| return wrapper | |
| def decorated_function(arg: str): | |
| return f"Function {arg}" | |
| assert decorated_function("test") == "Decorated Function test" | |
| # Test tool function when used in a multi-threaded environment | |
| def test_tool_in_multithreaded_environment(self): | |
| import threading | |
| def threaded_function(arg: int) -> int: | |
| return arg * 2 | |
| results = [] | |
| def thread_target(): | |
| results.append(threaded_function(5)) | |
| threads = [ | |
| threading.Thread(target=thread_target) for _ in range(10) | |
| ] | |
| for t in threads: | |
| t.start() | |
| for t in threads: | |
| t.join() | |
| assert results == [10] * 10 | |
| # Test with recursive functions | |
| def test_tool_with_recursive_function(self): | |
| def recursive_function(n: int) -> int: | |
| if n == 0: | |
| return 0 | |
| else: | |
| return n + recursive_function(n - 1) | |
| assert recursive_function(5) == 15 | |
| # Additional tests can be added here to cover more scenarios | |