import pytest from fastmcp.resources import ResourceTemplate, FunctionResource class TestResourceTemplate: """Test ResourceTemplate functionality.""" def test_template_from_function(self): """Test creating a template from a function.""" def weather(city: str, units: str = "metric") -> str: return f"Weather in {city} ({units})" template = ResourceTemplate.from_function( fn=weather, uri_template="weather://{city}/current", name="weather", description="Get current weather", ) assert template.name == "weather" assert template.uri_template == "weather://{city}/current" assert template.mime_type == "text/plain" assert "city" in template.parameters["properties"] def test_template_from_lambda_error(self): """Test error when creating template from lambda without name.""" with pytest.raises( ValueError, match="You must provide a name for lambda functions" ): ResourceTemplate.from_function( fn=lambda x: x, uri_template="test://{x}", ) def test_template_matches(self): """Test URI matching against template.""" def dummy(x: str) -> str: return x template = ResourceTemplate.from_function( fn=dummy, uri_template="test://{x}/value", name="test", ) # Test matching URI params = template.matches("test://hello/value") assert params == {"x": "hello"} # Test non-matching URI params = template.matches("test://hello/wrong") assert params is None async def test_create_text_resource(self): """Test creating a text resource from template.""" def greet(name: str) -> str: return f"Hello, {name}!" template = ResourceTemplate.from_function( fn=greet, uri_template="greet://{name}", name="greeter", ) resource = await template.create_resource( "greet://world", {"name": "world"}, ) assert isinstance(resource, FunctionResource) content = await resource.read() assert content == "Hello, world!" async def test_create_binary_resource(self): """Test creating a binary resource from template.""" def get_bytes(value: str) -> bytes: return value.encode() template = ResourceTemplate.from_function( fn=get_bytes, uri_template="bytes://{value}", name="bytes", ) resource = await template.create_resource( "bytes://test", {"value": "test"}, ) assert isinstance(resource, FunctionResource) content = await resource.read() assert content == b"test" async def test_json_conversion(self): """Test automatic JSON conversion of non-string/bytes results.""" def get_data(key: str) -> dict: return {"key": key, "value": 123} template = ResourceTemplate.from_function( fn=get_data, uri_template="data://{key}", name="data", ) resource = await template.create_resource( "data://test", {"key": "test"}, ) assert isinstance(resource, FunctionResource) content = await resource.read() assert '"key": "test"' in content assert '"value": 123' in content async def test_template_error(self): """Test error handling in template resource creation.""" def failing_func(x: str) -> str: raise ValueError("Test error") template = ResourceTemplate.from_function( fn=failing_func, uri_template="fail://{x}", name="fail", ) with pytest.raises(ValueError, match="Error creating resource from template"): await template.create_resource("fail://test", {"x": "test"}) async def test_async_text_resource(self): """Test creating a text resource from async function.""" async def greet(name: str) -> str: return f"Hello, {name}!" template = ResourceTemplate.from_function( fn=greet, uri_template="greet://{name}", name="greeter", ) resource = await template.create_resource( "greet://world", {"name": "world"}, ) assert isinstance(resource, FunctionResource) content = await resource.read() assert content == "Hello, world!" async def test_async_binary_resource(self): """Test creating a binary resource from async function.""" async def get_bytes(value: str) -> bytes: return value.encode() template = ResourceTemplate.from_function( fn=get_bytes, uri_template="bytes://{value}", name="bytes", ) resource = await template.create_resource( "bytes://test", {"value": "test"}, ) assert isinstance(resource, FunctionResource) content = await resource.read() assert content == b"test" async def test_async_json_conversion(self): """Test automatic JSON conversion of async results.""" async def get_data(key: str) -> dict: return {"key": key, "value": 123} template = ResourceTemplate.from_function( fn=get_data, uri_template="data://{key}", name="data", ) resource = await template.create_resource( "data://test", {"key": "test"}, ) assert isinstance(resource, FunctionResource) content = await resource.read() assert '"key": "test"' in content assert '"value": 123' in content async def test_async_error(self): """Test error handling in async template.""" async def failing_func(x: str) -> str: raise ValueError("Test error") template = ResourceTemplate.from_function( fn=failing_func, uri_template="fail://{x}", name="fail", ) with pytest.raises( ValueError, match="Error creating resource from template: Test error" ): await template.create_resource("fail://test", {"x": "test"}) async def test_sync_returning_coroutine(self): """Test sync function that returns a coroutine.""" async def async_helper(name: str) -> str: return f"Hello, {name}!" def get_greeting(name: str) -> str: return async_helper(name) # Returns coroutine template = ResourceTemplate.from_function( fn=get_greeting, uri_template="greet://{name}", name="greeter", ) resource = await template.create_resource( "greet://world", {"name": "world"}, ) assert isinstance(resource, FunctionResource) content = await resource.read() assert content == "Hello, world!"