mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-09 15:19:10 +02:00
238 lines
7 KiB
Python
238 lines
7 KiB
Python
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!"
|